Skip to content

Commit 9856a04

Browse files
committed
Merge pull request spring-projects#29 from henszey/multipleprimitiverepo
Added support for varying ID types (int/long) for repositories.
2 parents cf5804a + e9a5b7e commit 9856a04

File tree

11 files changed

+1464
-294
lines changed

11 files changed

+1464
-294
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
/*
2+
* Copyright 2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.elasticsearch.repository.support;
17+
18+
import org.elasticsearch.index.query.QueryBuilder;
19+
import org.springframework.dao.InvalidDataAccessApiUsageException;
20+
import org.springframework.data.domain.*;
21+
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
22+
import org.springframework.data.elasticsearch.core.query.*;
23+
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
24+
import org.springframework.util.Assert;
25+
26+
import java.io.Serializable;
27+
import java.lang.reflect.ParameterizedType;
28+
import java.lang.reflect.Type;
29+
import java.util.ArrayList;
30+
import java.util.Collection;
31+
import java.util.Collections;
32+
import java.util.List;
33+
34+
import static org.elasticsearch.index.query.QueryBuilders.inQuery;
35+
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
36+
import static org.springframework.data.elasticsearch.core.query.Query.DEFAULT_PAGE;
37+
38+
/**
39+
* Elasticsearch specific repository implementation. Likely to be used as target within {@link ElasticsearchRepositoryFactory}
40+
*
41+
*
42+
* @author Rizwan Idrees
43+
* @author Mohsin Husen
44+
* @author Ryan Henszey
45+
*/
46+
public abstract class AbstractElasticsearchRepository<T,ID extends Serializable> implements ElasticsearchRepository<T, ID> {
47+
48+
49+
protected ElasticsearchOperations elasticsearchOperations;
50+
protected Class<T> entityClass;
51+
protected ElasticsearchEntityInformation<T, ID> entityInformation;
52+
53+
public AbstractElasticsearchRepository() {
54+
}
55+
56+
public AbstractElasticsearchRepository(ElasticsearchOperations elasticsearchOperations) {
57+
Assert.notNull(elasticsearchOperations);
58+
this.setElasticsearchOperations(elasticsearchOperations);
59+
}
60+
61+
public AbstractElasticsearchRepository(ElasticsearchEntityInformation<T, ID> metadata, ElasticsearchOperations elasticsearchOperations) {
62+
this(elasticsearchOperations);
63+
Assert.notNull(metadata);
64+
this.entityInformation = metadata;
65+
setEntityClass(this.entityInformation.getJavaType());
66+
createIndex();
67+
putMapping();
68+
}
69+
70+
private void createIndex(){
71+
elasticsearchOperations.createIndex(getEntityClass());
72+
}
73+
74+
private void putMapping(){
75+
elasticsearchOperations.putMapping(getEntityClass());
76+
}
77+
78+
@Override
79+
public T findOne(ID id) {
80+
GetQuery query = new GetQuery();
81+
query.setId(stringIdRepresentation(id));
82+
return elasticsearchOperations.queryForObject(query, getEntityClass());
83+
}
84+
85+
@Override
86+
public Iterable<T> findAll() {
87+
int itemCount = (int) this.count();
88+
if (itemCount == 0) {
89+
return new PageImpl<T>(Collections.<T> emptyList());
90+
}
91+
return this.findAll(new PageRequest(0, Math.max(1, itemCount)));
92+
}
93+
94+
@Override
95+
public Page<T> findAll(Pageable pageable) {
96+
SearchQuery query = new NativeSearchQueryBuilder()
97+
.withQuery(matchAllQuery())
98+
.withPageable(pageable)
99+
.build();
100+
return elasticsearchOperations.queryForPage(query, getEntityClass());
101+
}
102+
103+
@Override
104+
public Iterable<T> findAll(Sort sort) {
105+
int itemCount = (int) this.count();
106+
if (itemCount == 0) {
107+
return new PageImpl<T>(Collections.<T> emptyList());
108+
}
109+
SearchQuery query = new NativeSearchQueryBuilder()
110+
.withQuery(matchAllQuery())
111+
.withPageable(new PageRequest(0,itemCount, sort))
112+
.build();
113+
return elasticsearchOperations.queryForPage(query, getEntityClass());
114+
}
115+
116+
@Override
117+
public Iterable<T> findAll(Iterable<ID> ids) {
118+
SearchQuery query = new NativeSearchQueryBuilder()
119+
.withQuery(inQuery(entityInformation.getIdAttribute(), ids))
120+
.build();
121+
return elasticsearchOperations.queryForPage(query, getEntityClass());
122+
}
123+
124+
@Override
125+
public long count() {
126+
SearchQuery query = new NativeSearchQueryBuilder()
127+
.withQuery(matchAllQuery()).build();
128+
return elasticsearchOperations.count(query,getEntityClass());
129+
}
130+
131+
@Override
132+
public <S extends T> S save(S entity) {
133+
Assert.notNull(entity, "Cannot save 'null' entity.");
134+
elasticsearchOperations.index(createIndexQuery(entity));
135+
elasticsearchOperations.refresh(entityInformation.getIndexName(), true);
136+
return entity;
137+
}
138+
139+
public <S extends T> List<S> save(List<S> entities) {
140+
Assert.notNull(entities, "Cannot insert 'null' as a List.");
141+
Assert.notEmpty(entities,"Cannot insert empty List.");
142+
List<IndexQuery> queries = new ArrayList<IndexQuery>();
143+
for(S s:entities){
144+
queries.add(createIndexQuery(s));
145+
}
146+
elasticsearchOperations.bulkIndex(queries);
147+
elasticsearchOperations.refresh(entityInformation.getIndexName(), true);
148+
return entities;
149+
}
150+
151+
@Override
152+
public <S extends T> S index(S entity) {
153+
return save(entity);
154+
}
155+
156+
@Override
157+
public <S extends T> Iterable<S> save(Iterable<S> entities) {
158+
Assert.notNull(entities, "Cannot insert 'null' as a List.");
159+
if (!(entities instanceof Collection<?>)) {
160+
throw new InvalidDataAccessApiUsageException("Entities have to be inside a collection");
161+
}
162+
List<IndexQuery> queries = new ArrayList<IndexQuery>();
163+
for(S s: entities){
164+
queries.add(createIndexQuery(s));
165+
}
166+
elasticsearchOperations.bulkIndex(queries);
167+
elasticsearchOperations.refresh(entityInformation.getIndexName(), true);
168+
return entities;
169+
}
170+
171+
@Override
172+
public boolean exists(ID id) {
173+
return findOne(id) != null;
174+
}
175+
176+
@Override
177+
public Iterable<T> search(QueryBuilder query) {
178+
SearchQuery searchQuery = new NativeSearchQueryBuilder()
179+
.withQuery(query).build();
180+
int count = (int) elasticsearchOperations.count(searchQuery, getEntityClass());
181+
if(count == 0){
182+
return new PageImpl<T>(Collections.<T>emptyList());
183+
}
184+
searchQuery.setPageable(new PageRequest(0, count));
185+
return elasticsearchOperations.queryForPage(searchQuery, getEntityClass());
186+
}
187+
188+
@Override
189+
public Page<T> search(QueryBuilder query, Pageable pageable) {
190+
SearchQuery searchQuery = new NativeSearchQueryBuilder()
191+
.withQuery(query)
192+
.withPageable(pageable)
193+
.build();
194+
return elasticsearchOperations.queryForPage(searchQuery, getEntityClass());
195+
}
196+
197+
@Override
198+
public Page<T> search(SearchQuery query){
199+
return elasticsearchOperations.queryForPage(query, getEntityClass());
200+
}
201+
202+
@Override
203+
public Page<T> searchSimilar(T entity) {
204+
return searchSimilar(entity, DEFAULT_PAGE);
205+
}
206+
207+
@Override
208+
public Page<T> searchSimilar(T entity, Pageable pageable) {
209+
Assert.notNull(entity, "Cannot search similar records for 'null'.");
210+
Assert.notNull(entity, "Pageable cannot be 'null'");
211+
MoreLikeThisQuery query = new MoreLikeThisQuery();
212+
query.setId(stringIdRepresentation(extractIdFromBean(entity)));
213+
query.setPageable(pageable);
214+
return elasticsearchOperations.moreLikeThis(query, getEntityClass());
215+
}
216+
217+
218+
@Override
219+
public void delete(ID id) {
220+
Assert.notNull(id, "Cannot delete entity with id 'null'.");
221+
elasticsearchOperations.delete(entityInformation.getIndexName(), entityInformation.getType(),stringIdRepresentation(id));
222+
elasticsearchOperations.refresh(entityInformation.getIndexName(),true);
223+
}
224+
225+
@Override
226+
public void delete(T entity) {
227+
Assert.notNull(entity, "Cannot delete 'null' entity.");
228+
delete(extractIdFromBean(entity));
229+
elasticsearchOperations.refresh(entityInformation.getIndexName(), true);
230+
}
231+
232+
@Override
233+
public void delete(Iterable<? extends T> entities) {
234+
Assert.notNull(entities, "Cannot delete 'null' list.");
235+
for (T entity : entities) {
236+
delete(entity);
237+
}
238+
}
239+
240+
@Override
241+
public void deleteAll() {
242+
DeleteQuery deleteQuery = new DeleteQuery();
243+
deleteQuery.setQuery(matchAllQuery());
244+
elasticsearchOperations.delete(deleteQuery, getEntityClass());
245+
elasticsearchOperations.refresh(entityInformation.getIndexName(),true);
246+
}
247+
248+
private IndexQuery createIndexQuery(T entity){
249+
IndexQuery query = new IndexQuery();
250+
query.setObject(entity);
251+
query.setId(stringIdRepresentation(extractIdFromBean(entity)));
252+
query.setVersion(extractVersionFromBean(entity));
253+
return query;
254+
}
255+
256+
@SuppressWarnings("unchecked")
257+
private Class<T> resolveReturnedClassFromGenericType() {
258+
ParameterizedType parameterizedType = resolveReturnedClassFromGenericType(getClass());
259+
return (Class<T>) parameterizedType.getActualTypeArguments()[0];
260+
}
261+
262+
private ParameterizedType resolveReturnedClassFromGenericType(Class<?> clazz) {
263+
Object genericSuperclass = clazz.getGenericSuperclass();
264+
if (genericSuperclass instanceof ParameterizedType) {
265+
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
266+
Type rawtype = parameterizedType.getRawType();
267+
if (SimpleElasticsearchRepository.class.equals(rawtype)) {
268+
return parameterizedType;
269+
}
270+
}
271+
return resolveReturnedClassFromGenericType(clazz.getSuperclass());
272+
}
273+
274+
public Class<T> getEntityClass() {
275+
if (!isEntityClassSet()) {
276+
try {
277+
this.entityClass = resolveReturnedClassFromGenericType();
278+
} catch (Exception e) {
279+
throw new InvalidDataAccessApiUsageException("Unable to resolve EntityClass. Please use according setter!", e);
280+
}
281+
}
282+
return entityClass;
283+
}
284+
285+
private boolean isEntityClassSet() {
286+
return entityClass != null;
287+
}
288+
289+
public final void setEntityClass(Class<T> entityClass) {
290+
Assert.notNull(entityClass, "EntityClass must not be null.");
291+
this.entityClass = entityClass;
292+
}
293+
294+
public final void setElasticsearchOperations(ElasticsearchOperations elasticsearchOperations) {
295+
Assert.notNull(elasticsearchOperations, "ElasticsearchOperations must not be null.");
296+
this.elasticsearchOperations = elasticsearchOperations;
297+
}
298+
299+
300+
protected ID extractIdFromBean(T entity) {
301+
if (entityInformation != null) {
302+
return entityInformation.getId(entity);
303+
}
304+
return null;
305+
}
306+
307+
protected abstract String stringIdRepresentation(ID id);
308+
309+
private Long extractVersionFromBean(T entity){
310+
if (entityInformation != null) {
311+
return entityInformation.getVersion(entity);
312+
}
313+
return null;
314+
}
315+
316+
}

src/main/java/org/springframework/data/elasticsearch/repository/support/ElasticsearchRepositoryFactory.java

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
*/
1616
package org.springframework.data.elasticsearch.repository.support;
1717

18+
import static org.springframework.data.querydsl.QueryDslUtils.QUERY_DSL_PRESENT;
19+
20+
import java.io.Serializable;
21+
import java.lang.reflect.Method;
22+
1823
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
1924
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
2025
import org.springframework.data.elasticsearch.repository.query.ElasticsearchPartQuery;
@@ -28,18 +33,15 @@
2833
import org.springframework.data.repository.query.RepositoryQuery;
2934
import org.springframework.util.Assert;
3035

31-
import java.io.Serializable;
32-
import java.lang.reflect.Method;
33-
34-
import static org.springframework.data.querydsl.QueryDslUtils.QUERY_DSL_PRESENT;
35-
3636
/**
3737
* Factory to create {@link ElasticsearchRepository}
3838
*
3939
* @author Rizwan Idrees
4040
* @author Mohsin Husen
41+
* @author Ryan Henszey
4142
*/
4243
public class ElasticsearchRepositoryFactory extends RepositoryFactorySupport {
44+
4345

4446
private final ElasticsearchOperations elasticsearchOperations;
4547
private final ElasticsearchEntityInformationCreator entityInformationCreator;
@@ -59,8 +61,27 @@ public <T, ID extends Serializable> ElasticsearchEntityInformation<T, ID> getEnt
5961
@Override
6062
@SuppressWarnings({ "rawtypes", "unchecked" })
6163
protected Object getTargetRepository(RepositoryMetadata metadata) {
62-
SimpleElasticsearchRepository repository = new SimpleElasticsearchRepository(getEntityInformation(metadata.getDomainType()), elasticsearchOperations);
64+
65+
ElasticsearchEntityInformation<?, ?> entityInformation = getEntityInformation(metadata.getDomainType());
66+
67+
AbstractElasticsearchRepository repository;
68+
69+
//Probably a better way to store and look these up.
70+
if(Integer.class.isAssignableFrom(entityInformation.getIdType()) ||
71+
Long.class.isAssignableFrom(entityInformation.getIdType()) ||
72+
Double.class.isAssignableFrom(entityInformation.getIdType())){
73+
//logger.debug("Using NumberKeyedRepository for " + metadata.getRepositoryInterface());
74+
repository = new NumberKeyedRepository(getEntityInformation(metadata.getDomainType()), elasticsearchOperations);
75+
}
76+
else if (entityInformation.getIdType() == String.class){
77+
//logger.debug("Using SimpleElasticsearchRepository for " + metadata.getRepositoryInterface());
78+
repository = new SimpleElasticsearchRepository(getEntityInformation(metadata.getDomainType()), elasticsearchOperations);
79+
}
80+
else {
81+
throw new IllegalArgumentException("Unsuppored ID type " + entityInformation.getIdType());
82+
}
6383
repository.setEntityClass(metadata.getDomainType());
84+
6485
return repository;
6586
}
6687

0 commit comments

Comments
 (0)