initial support of custom query (#9)
* initial support of custom query * change test db name
This commit is contained in:
Родитель
2c2daa0ade
Коммит
72ec322bf0
|
@ -34,6 +34,7 @@ Please refer to [sample project here](./samplecode).
|
|||
- Custom collection Name.
|
||||
By default, collection name will be class name of user domain class. To customize it, add annotation `@Document(collection="myCustomCollectionName")` to domain class, that's all.
|
||||
- Supports [Azure Cosmos DB partition](https://docs.microsoft.com/en-us/azure/cosmos-db/partition-data). To specify a field of domain class to be partition key field, just annotate it with `@PartitionKey`. When you do CRUD operation, pls specify your partition value. For more sample on partition CRUD, pls refer to [test here](./src/test/java/com/microsoft/azure/spring/data/cosmosdb/documentdb/repository/AddressRepositoryIT.java)
|
||||
- Supports [Spring Data custom query](https://docs.spring.io/spring-data/commons/docs/current/reference/html/#repositories.query-methods.details) find operation.
|
||||
- Supports [spring-boot-starter-data-rest](https://projects.spring.io/spring-data-rest/).
|
||||
|
||||
## Quick Start
|
||||
|
@ -135,10 +136,11 @@ import org.springframework.stereotype.Repository;
|
|||
|
||||
@Repository
|
||||
public interface UserRepository extends DocumentDbRepository<User, String> {
|
||||
List<User> findByFirstName(String firstName);
|
||||
}
|
||||
```
|
||||
|
||||
So far DocumentDbRepository provides basic save, delete, update and find operations. More operations will be supported later.
|
||||
`findByFirstName` method is custom query method, it will find documents per FirstName.
|
||||
|
||||
### Create an Application class
|
||||
Here create an application class with all the components
|
||||
|
|
|
@ -14,6 +14,8 @@ import org.springframework.boot.SpringApplication;
|
|||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SampleApplication implements CommandLineRunner {
|
||||
|
||||
|
@ -33,12 +35,32 @@ public class SampleApplication implements CommandLineRunner {
|
|||
repository.deleteAll();
|
||||
repository.save(testUser);
|
||||
|
||||
final User result = repository.findOne(testUser.getEmailAddress(), testUser.getLastName());
|
||||
final User result1 = repository.findOne(testUser.getEmailAddress(), testUser.getLastName());
|
||||
|
||||
Assert.state(result.getFirstName().equals(testUser.getFirstName()), "query result firstName doesn't match!");
|
||||
Assert.state(result.getLastName().equals(testUser.getLastName()), "query result lastName doesn't match!");
|
||||
Assert.state(result1.getFirstName().equals(testUser.getFirstName()), "query result firstName doesn't match!");
|
||||
Assert.state(result1.getLastName().equals(testUser.getLastName()), "query result lastName doesn't match!");
|
||||
|
||||
LOGGER.info("findOne in User collection get result: {}", result.toString());
|
||||
System.out.println("findOne in User collection get result:" + result.toString());
|
||||
LOGGER.info("findOne in User collection get result: {}", result1.toString());
|
||||
System.out.println("findOne in User collection get result:" + result1.toString());
|
||||
|
||||
final List<User> result2 = repository.findByFirstName(testUser.getFirstName());
|
||||
|
||||
Assert.state(result2.get(0).getFirstName().equals(testUser.getFirstName()),
|
||||
"query result firstName doesn't match!");
|
||||
Assert.state(result2.get(0).getLastName().equals(testUser.getLastName()),
|
||||
"query result lastName doesn't match!");
|
||||
|
||||
LOGGER.info("findByFirstName in User collection get result: {}", result2.get(0).toString());
|
||||
System.out.println("findByFirstName in User collection get result:" + result2.get(0).toString());
|
||||
|
||||
final List<User> result3 = repository.findByLastName(testUser.getLastName());
|
||||
|
||||
Assert.state(result3.get(0).getFirstName().equals(testUser.getFirstName()),
|
||||
"query result firstName doesn't match!");
|
||||
Assert.state(result3.get(0).getLastName().equals(testUser.getLastName()),
|
||||
"query result lastName doesn't match!");
|
||||
|
||||
LOGGER.info("findByFirstName in User collection get result: {}", result3.get(0).toString());
|
||||
System.out.println("findByFirstName in User collection get result:" + result3.get(0).toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,12 @@ package com.microsoft.azure.sample;
|
|||
import com.microsoft.azure.spring.data.documentdb.repository.DocumentDbRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface UserRepository extends DocumentDbRepository<User, String> {
|
||||
List<User> findByFirstName(String firstName);
|
||||
|
||||
List<User> findByLastName(String lastName);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ package com.microsoft.azure.spring.data.documentdb.core;
|
|||
|
||||
import com.microsoft.azure.documentdb.DocumentCollection;
|
||||
import com.microsoft.azure.spring.data.documentdb.core.convert.MappingDocumentDbConverter;
|
||||
import com.microsoft.azure.spring.data.documentdb.core.query.Query;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -37,6 +38,10 @@ public interface DocumentDbOperations {
|
|||
Class<T> entityClass,
|
||||
String partitionKeyFieldValue);
|
||||
|
||||
<T> List<T> find(Query query,
|
||||
Class<T> entityClass,
|
||||
String collectionName);
|
||||
|
||||
<T> T insert(T objectToSave, String partitionKeyFieldValue);
|
||||
|
||||
<T> T insert(String collectionName,
|
||||
|
|
|
@ -11,18 +11,17 @@ import com.microsoft.azure.documentdb.internal.HttpConstants;
|
|||
import com.microsoft.azure.spring.data.documentdb.DocumentDbFactory;
|
||||
import com.microsoft.azure.spring.data.documentdb.core.convert.DocumentDbConverter;
|
||||
import com.microsoft.azure.spring.data.documentdb.core.convert.MappingDocumentDbConverter;
|
||||
import com.microsoft.azure.spring.data.documentdb.core.mapping.DocumentDbPersistentEntity;
|
||||
import com.microsoft.azure.spring.data.documentdb.core.mapping.DocumentDbPersistentProperty;
|
||||
import com.microsoft.azure.spring.data.documentdb.core.query.Query;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class DocumentDbTemplate implements DocumentDbOperations, ApplicationContextAware {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DocumentDbTemplate.class);
|
||||
|
@ -30,7 +29,6 @@ public class DocumentDbTemplate implements DocumentDbOperations, ApplicationCont
|
|||
private final DocumentDbFactory documentDbFactory;
|
||||
private final MappingDocumentDbConverter mappingDocumentDbConverter;
|
||||
private final String databaseName;
|
||||
private final MappingContext<? extends DocumentDbPersistentEntity<?>, DocumentDbPersistentProperty> mappingContext;
|
||||
|
||||
private Database databaseCache;
|
||||
private List<String> collectionCache;
|
||||
|
@ -44,7 +42,6 @@ public class DocumentDbTemplate implements DocumentDbOperations, ApplicationCont
|
|||
this.databaseName = dbName;
|
||||
this.documentDbFactory = documentDbFactory;
|
||||
this.mappingDocumentDbConverter = mappingDocumentDbConverter;
|
||||
this.mappingContext = mappingDocumentDbConverter.getMappingContext();
|
||||
this.collectionCache = new ArrayList<>();
|
||||
}
|
||||
|
||||
|
@ -375,8 +372,50 @@ public class DocumentDbTemplate implements DocumentDbOperations, ApplicationCont
|
|||
return requestOptions;
|
||||
}
|
||||
|
||||
public <T> List<T> find(Query query, Class<T> domainClass, String collectionName) {
|
||||
final SqlQuerySpec sqlQuerySpec = createSqlQuerySpec(query);
|
||||
|
||||
final List<DocumentCollection> collections = documentDbFactory.getDocumentClient().
|
||||
queryCollections(
|
||||
getDatabaseLink(this.databaseName),
|
||||
new SqlQuerySpec("SELECT * FROM ROOT r WHERE r.id=@id",
|
||||
new SqlParameterCollection(new SqlParameter("@id", collectionName))), null)
|
||||
.getQueryIterable().toList();
|
||||
|
||||
if (collections.size() != 1) {
|
||||
throw new RuntimeException("expect only one collection: " + collectionName
|
||||
+ " in database: " + this.databaseName + ", but found " + collections.size());
|
||||
}
|
||||
|
||||
final FeedOptions feedOptions = new FeedOptions();
|
||||
feedOptions.setEnableCrossPartitionQuery(true);
|
||||
final List<Document> results = documentDbFactory.getDocumentClient()
|
||||
.queryDocuments(collections.get(0).getSelfLink(),
|
||||
sqlQuerySpec, feedOptions)
|
||||
.getQueryIterable().toList();
|
||||
|
||||
final List<T> entities = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < results.size(); i++) {
|
||||
final T entity = mappingDocumentDbConverter.read(domainClass, results.get(i));
|
||||
entities.add(entity);
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
|
||||
private static SqlQuerySpec createSqlQuerySpec(Query query) {
|
||||
String queryStr = "SELECT * FROM ROOT r WHERE r.";
|
||||
final SqlParameterCollection parameterCollection = new SqlParameterCollection();
|
||||
|
||||
for (final Map.Entry<String, Object> entry : query.getCriteria().entrySet()) {
|
||||
queryStr += entry.getKey() + "=@" + entry.getKey();
|
||||
parameterCollection.add(new SqlParameter("@" + entry.getKey(), entry.getValue()));
|
||||
}
|
||||
return new SqlQuerySpec(queryStr, parameterCollection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingDocumentDbConverter getConverter() {
|
||||
return mappingDocumentDbConverter;
|
||||
return this.mappingDocumentDbConverter;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.documentdb.core.query;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
|
||||
public class Criteria implements CriteriaDefinition {
|
||||
|
||||
private String key;
|
||||
private Object value;
|
||||
|
||||
public Criteria(String key, LinkedHashMap<String, Object> value) {
|
||||
this.key = key;
|
||||
this.value = value.get(key);
|
||||
}
|
||||
|
||||
public Object getCriteriaObject() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public static Criteria where(String key, Object value) {
|
||||
return new Criteria(key, (LinkedHashMap<String, Object>) value);
|
||||
}
|
||||
|
||||
public Criteria is(Object o) {
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.documentdb.core.query;
|
||||
|
||||
public interface CriteriaDefinition {
|
||||
Object getCriteriaObject();
|
||||
|
||||
String getKey();
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.documentdb.core.query;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class Query {
|
||||
|
||||
private final Map<String, Object> criteria = new LinkedHashMap<>();
|
||||
|
||||
public static Query query(CriteriaDefinition criteriaDefinition) {
|
||||
return new Query(criteriaDefinition);
|
||||
}
|
||||
|
||||
public Query() {
|
||||
}
|
||||
|
||||
public Query(CriteriaDefinition criteriaDefinition) {
|
||||
addCriteria(criteriaDefinition);
|
||||
}
|
||||
|
||||
public Query addCriteria(CriteriaDefinition criteriaDefinition) {
|
||||
final Object existing = this.criteria.get(criteriaDefinition.getKey());
|
||||
|
||||
if (existing == null) {
|
||||
this.criteria.put(criteriaDefinition.getKey(), criteriaDefinition.getCriteriaObject());
|
||||
} else {
|
||||
throw new RuntimeException("invalid criteriaDefinition!");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, Object> getCriteria() {
|
||||
return this.criteria;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.documentdb.repository.query;
|
||||
|
||||
import com.microsoft.azure.spring.data.documentdb.core.DocumentDbOperations;
|
||||
import com.microsoft.azure.spring.data.documentdb.core.query.Query;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
|
||||
public abstract class AbstractDocumentDbQuery implements RepositoryQuery {
|
||||
|
||||
private final DocumentDbQueryMethod method;
|
||||
private final DocumentDbOperations operations;
|
||||
|
||||
public AbstractDocumentDbQuery(DocumentDbQueryMethod method, DocumentDbOperations operations) {
|
||||
this.method = method;
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
public Object execute(Object[] parameters) {
|
||||
final DocumentDbParameterAccessor accessor = new DocumentDbParameterParameterAccessor(method, parameters);
|
||||
final Query query = createQuery(accessor);
|
||||
|
||||
final ResultProcessor processor = method.getResultProcessor().withDynamicProjection(accessor);
|
||||
final String collection = ((DocumentDbEntityMetadata) method.getEntityInformation()).getCollectionName();
|
||||
|
||||
final DocumentDbQueryExecution execution = getExecution(query, accessor);
|
||||
return execution.execute(query, processor.getReturnedType().getDomainType(), collection);
|
||||
}
|
||||
|
||||
|
||||
private DocumentDbQueryExecution getExecution(Query query, DocumentDbParameterAccessor accessor) {
|
||||
if (isDeleteQuery()) {
|
||||
return new DocumentDbQueryExecution.DeleteExecution();
|
||||
} else {
|
||||
return new DocumentDbQueryExecution.SingleEntityExecution(operations);
|
||||
}
|
||||
}
|
||||
|
||||
public DocumentDbQueryMethod getQueryMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
protected abstract Query createQuery(DocumentDbParameterAccessor accessor);
|
||||
|
||||
protected abstract boolean isDeleteQuery();
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.documentdb.repository.query;
|
||||
|
||||
import org.springframework.data.repository.core.EntityMetadata;
|
||||
|
||||
public interface DocumentDbEntityMetadata<T> extends EntityMetadata {
|
||||
String getCollectionName();
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.documentdb.repository.query;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.repository.query.Parameter;
|
||||
|
||||
public class DocumentDbParameter extends Parameter {
|
||||
|
||||
public DocumentDbParameter(MethodParameter parameter) {
|
||||
super(parameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSpecialParameter() {
|
||||
return super.isSpecialParameter();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.documentdb.repository.query;
|
||||
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
|
||||
public interface DocumentDbParameterAccessor extends ParameterAccessor {
|
||||
Object[] getValues();
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.documentdb.repository.query;
|
||||
|
||||
import org.springframework.data.repository.query.ParametersParameterAccessor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class DocumentDbParameterParameterAccessor extends ParametersParameterAccessor
|
||||
implements DocumentDbParameterAccessor {
|
||||
|
||||
private final List<Object> values;
|
||||
|
||||
public DocumentDbParameterParameterAccessor(DocumentDbQueryMethod method, Object[] values) {
|
||||
super(method.getParameters(), values);
|
||||
|
||||
this.values = Arrays.asList(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getValues() {
|
||||
return values.toArray();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.documentdb.repository.query;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
public class DocumentDbParameters extends Parameters<DocumentDbParameters, DocumentDbParameter> {
|
||||
|
||||
public DocumentDbParameters(Method method) {
|
||||
super(method);
|
||||
}
|
||||
|
||||
private DocumentDbParameters(List<DocumentDbParameter> parameters) {
|
||||
super(parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DocumentDbParameters createFrom(List<DocumentDbParameter> parameters) {
|
||||
return new DocumentDbParameters(parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DocumentDbParameter createParameter(MethodParameter parameter) {
|
||||
return new DocumentDbParameter(parameter);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.documentdb.repository.query;
|
||||
|
||||
import com.microsoft.azure.spring.data.documentdb.core.mapping.DocumentDbPersistentProperty;
|
||||
import com.microsoft.azure.spring.data.documentdb.core.query.Criteria;
|
||||
import com.microsoft.azure.spring.data.documentdb.core.query.Query;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.context.PersistentPropertyPath;
|
||||
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
|
||||
public class DocumentDbQueryCreator extends AbstractQueryCreator<Query, Criteria> {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DocumentDbQueryCreator.class);
|
||||
private final DocumentDbParameterAccessor accessor;
|
||||
private final MappingContext<?, DocumentDbPersistentProperty> mappingContext;
|
||||
|
||||
public DocumentDbQueryCreator(PartTree tree, DocumentDbParameterAccessor accessor,
|
||||
MappingContext<?, DocumentDbPersistentProperty> mappingContext) {
|
||||
super(tree, accessor);
|
||||
|
||||
this.accessor = accessor;
|
||||
this.mappingContext = mappingContext;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Criteria create(Part part, Iterator<Object> iterator) {
|
||||
final PersistentPropertyPath<DocumentDbPersistentProperty> propertyPath =
|
||||
mappingContext.getPersistentPropertyPath(part.getProperty());
|
||||
final DocumentDbPersistentProperty property = propertyPath.getLeafProperty();
|
||||
final LinkedHashMap<String, Object> params = new LinkedHashMap<String, Object>();
|
||||
final Collection<Object> clonedIterator = new ArrayList<Object>();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
final Object obj = iterator.next();
|
||||
params.put(propertyPath.toDotPath(), obj);
|
||||
clonedIterator.add(obj);
|
||||
}
|
||||
|
||||
final Criteria criteria = from(part, property, Criteria.where(propertyPath.toDotPath(), params),
|
||||
clonedIterator.iterator());
|
||||
return criteria;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Criteria and(Part part, Criteria base, Iterator<Object> iterator) {
|
||||
// not supported yet
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query complete(Criteria criteria, Sort sort) {
|
||||
final Query query = new Query(criteria);
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Criteria or(Criteria base, Criteria criteria) {
|
||||
// not supported yet
|
||||
return null;
|
||||
}
|
||||
|
||||
private Criteria from(Part part, DocumentDbPersistentProperty property,
|
||||
Criteria criteria, Iterator<Object> parameters) {
|
||||
|
||||
final Part.Type type = part.getType();
|
||||
|
||||
switch (type) {
|
||||
case SIMPLE_PROPERTY:
|
||||
|
||||
return isSimpleComparisionPossible(part) ? criteria.is(parameters.next())
|
||||
: createLikeRegexCriteriaOrThrow(part, property, criteria, parameters, false);
|
||||
default:
|
||||
throw new IllegalArgumentException("unsupported keyword: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSimpleComparisionPossible(Part part) {
|
||||
switch (part.shouldIgnoreCase()) {
|
||||
case NEVER:
|
||||
return true;
|
||||
case WHEN_POSSIBLE:
|
||||
return part.getProperty().getType() != String.class;
|
||||
case ALWAYS:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private Criteria createLikeRegexCriteriaOrThrow(Part part, DocumentDbPersistentProperty property,
|
||||
Criteria criteria, Iterator<Object> parameters, boolean shouldNegateExpression) {
|
||||
final PropertyPath path = part.getProperty().getLeafProperty();
|
||||
|
||||
switch (part.shouldIgnoreCase()) {
|
||||
case ALWAYS:
|
||||
if (path.getType() != String.class) {
|
||||
throw new IllegalArgumentException("part must be String, but: " + path.getType() + ", " + path);
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
case WHEN_POSSIBLE:
|
||||
|
||||
return criteria;
|
||||
|
||||
case NEVER:
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.documentdb.repository.query;
|
||||
|
||||
import com.microsoft.azure.spring.data.documentdb.core.DocumentDbOperations;
|
||||
import com.microsoft.azure.spring.data.documentdb.core.query.Query;
|
||||
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
|
||||
|
||||
|
||||
public interface DocumentDbQueryExecution {
|
||||
Object execute(Query query, Class<?> type, String collection);
|
||||
|
||||
final class CollectionExecution implements DocumentDbQueryExecution {
|
||||
|
||||
private final DocumentDbOperations operations;
|
||||
|
||||
public CollectionExecution(DocumentDbOperations operations) {
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type, String collection) {
|
||||
return operations.getCollectionName(type);
|
||||
}
|
||||
}
|
||||
|
||||
final class SingleEntityExecution implements DocumentDbQueryExecution {
|
||||
|
||||
private final DocumentDbOperations operations;
|
||||
|
||||
public SingleEntityExecution(DocumentDbOperations operations) {
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type, String collection) {
|
||||
return operations.find(query, type, collection);
|
||||
}
|
||||
}
|
||||
|
||||
final class DeleteExecution implements DocumentDbQueryExecution {
|
||||
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type, String collection) {
|
||||
// deletion not supported yet
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.documentdb.repository.query;
|
||||
|
||||
import com.microsoft.azure.spring.data.documentdb.repository.support.DocumentDbEntityInformation;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.core.EntityMetadata;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class DocumentDbQueryMethod extends QueryMethod {
|
||||
|
||||
private DocumentDbEntityMetadata<?> metadata;
|
||||
|
||||
public DocumentDbQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory) {
|
||||
super(method, metadata, factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMetadata<?> getEntityInformation() {
|
||||
final Class<?> domainClass = getDomainClass();
|
||||
final DocumentDbEntityInformation<Object, String> entityInformation =
|
||||
new DocumentDbEntityInformation(domainClass);
|
||||
|
||||
this.metadata = new SimpleDocumentDbEntityMetadata<Object>((Class<Object>) domainClass, entityInformation);
|
||||
return this.metadata;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.documentdb.repository.query;
|
||||
|
||||
import com.microsoft.azure.spring.data.documentdb.core.DocumentDbOperations;
|
||||
import com.microsoft.azure.spring.data.documentdb.core.mapping.DocumentDbPersistentProperty;
|
||||
import com.microsoft.azure.spring.data.documentdb.core.query.Query;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
|
||||
public class PartTreeDocumentDbQuery extends AbstractDocumentDbQuery {
|
||||
|
||||
private final PartTree tree;
|
||||
private final MappingContext<?, DocumentDbPersistentProperty> mappingContext;
|
||||
private final ResultProcessor processor;
|
||||
|
||||
public PartTreeDocumentDbQuery(DocumentDbQueryMethod method, DocumentDbOperations operations) {
|
||||
super(method, operations);
|
||||
|
||||
this.processor = method.getResultProcessor();
|
||||
this.tree = new PartTree(method.getName(), processor.getReturnedType().getDomainType());
|
||||
this.mappingContext = operations.getConverter().getMappingContext();
|
||||
}
|
||||
|
||||
public PartTree getTree() {
|
||||
return this.tree;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query createQuery(DocumentDbParameterAccessor accessor) {
|
||||
final DocumentDbQueryCreator creator = new DocumentDbQueryCreator(tree, accessor, mappingContext);
|
||||
|
||||
final Query query = creator.createQuery();
|
||||
|
||||
if (tree.isLimiting()) {
|
||||
System.out.println("not implement yet!");
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isDeleteQuery() {
|
||||
return tree.isDelete();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.documentdb.repository.query;
|
||||
|
||||
import com.microsoft.azure.spring.data.documentdb.repository.support.DocumentDbEntityInformation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class SimpleDocumentDbEntityMetadata<T> implements DocumentDbEntityMetadata<T> {
|
||||
|
||||
private final Class<T> type;
|
||||
private final DocumentDbEntityInformation<T, String> entityInformation;
|
||||
|
||||
public SimpleDocumentDbEntityMetadata(Class<T> type, DocumentDbEntityInformation<T, String> entityInformation) {
|
||||
Assert.notNull(type, "type must not be null!");
|
||||
Assert.notNull(entityInformation, "entityInformation must not be null!");
|
||||
|
||||
this.type = type;
|
||||
this.entityInformation = entityInformation;
|
||||
}
|
||||
|
||||
public Class<T> getJavaType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getCollectionName() {
|
||||
return entityInformation.getCollectionName();
|
||||
}
|
||||
}
|
|
@ -6,22 +6,33 @@
|
|||
|
||||
package com.microsoft.azure.spring.data.documentdb.repository.support;
|
||||
|
||||
import com.microsoft.azure.spring.data.documentdb.core.DocumentDbOperations;
|
||||
import com.microsoft.azure.spring.data.documentdb.repository.query.DocumentDbQueryMethod;
|
||||
import com.microsoft.azure.spring.data.documentdb.repository.query.PartTreeDocumentDbQuery;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.data.repository.query.EvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
|
||||
public class DocumentDbRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
private final DocumentDbOperations dbOperations;
|
||||
|
||||
public DocumentDbRepositoryFactory(ApplicationContext applicationContext) {
|
||||
public DocumentDbRepositoryFactory(DocumentDbOperations dbOperations, ApplicationContext applicationContext) {
|
||||
this.dbOperations = dbOperations;
|
||||
this.applicationContext = applicationContext;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -39,4 +50,29 @@ public class DocumentDbRepositoryFactory extends RepositoryFactorySupport {
|
|||
public <T, ID extends Serializable> EntityInformation<T, ID> getEntityInformation(Class<T> domainClass) {
|
||||
return new DocumentDbEntityInformation<T, ID>(domainClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected QueryLookupStrategy getQueryLookupStrategy(QueryLookupStrategy.Key key,
|
||||
EvaluationContextProvider evaluationContextProvider) {
|
||||
return new DocumentDbQueryLookupStrategy(dbOperations, evaluationContextProvider);
|
||||
}
|
||||
|
||||
private static class DocumentDbQueryLookupStrategy implements QueryLookupStrategy {
|
||||
private final DocumentDbOperations dbOperations;
|
||||
|
||||
public DocumentDbQueryLookupStrategy(DocumentDbOperations operations, EvaluationContextProvider provider) {
|
||||
this.dbOperations = operations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata,
|
||||
ProjectionFactory factory, NamedQueries namedQueries) {
|
||||
final DocumentDbQueryMethod queryMethod = new DocumentDbQueryMethod(method, metadata, factory);
|
||||
|
||||
Assert.notNull(queryMethod, "queryMethod must not be null!");
|
||||
Assert.notNull(dbOperations, "dbOperations must not be null!");
|
||||
return new PartTreeDocumentDbQuery(queryMethod, dbOperations);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,9 +28,11 @@ public class DocumentDbRepositoryFactoryBean<T extends Repository<S, ID>, S, ID
|
|||
private boolean mappingContextConfigured = false;
|
||||
|
||||
|
||||
public DocumentDbRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
|
||||
public DocumentDbRepositoryFactoryBean(Class<? extends T> repositoryInterface,
|
||||
DocumentDbOperations operations) {
|
||||
|
||||
super(repositoryInterface);
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
public void setDocumentDbOperations(DocumentDbOperations operations) {
|
||||
|
@ -43,7 +45,7 @@ public class DocumentDbRepositoryFactoryBean<T extends Repository<S, ID>, S, ID
|
|||
}
|
||||
|
||||
protected RepositoryFactorySupport getFactoryInstance(ApplicationContext applicationContext) {
|
||||
return new DocumentDbRepositoryFactory(applicationContext);
|
||||
return new DocumentDbRepositoryFactory(operations, applicationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,8 +17,6 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
|||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
public class AbstractDocumentDbConfiguratinUnitTest {
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.documentdb.core.query;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class CriteriaUnitTest {
|
||||
|
||||
@Test
|
||||
public void testSimpleCriteria() {
|
||||
final LinkedHashMap<String, Object> value = new LinkedHashMap<String, Object>();
|
||||
value.put("name", "test");
|
||||
final Criteria c = new Criteria("name", value);
|
||||
|
||||
assertThat(c.getKey()).isEqualTo("name");
|
||||
assertThat(c.getCriteriaObject()).isEqualTo("test");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE in the project root for
|
||||
* license information.
|
||||
*/
|
||||
package com.microsoft.azure.spring.data.documentdb.core.query;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class QueryUnitTest {
|
||||
|
||||
@Test
|
||||
public void testAddCriteria() {
|
||||
final LinkedHashMap<String, Object> values = new LinkedHashMap<>();
|
||||
values.putIfAbsent("name", "test");
|
||||
|
||||
final Query query = new Query().addCriteria(new Criteria("name", values));
|
||||
|
||||
assertThat(query.getCriteria().size()).isEqualTo(1);
|
||||
assertThat(query.getCriteria().get("name")).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhere() {
|
||||
final LinkedHashMap<String, Object> values = new LinkedHashMap<>();
|
||||
values.putIfAbsent("name", "test");
|
||||
|
||||
final Query query = new Query((Criteria.where("name", values)));
|
||||
|
||||
assertThat(query.getCriteria().size()).isEqualTo(1);
|
||||
assertThat(query.getCriteria().get("name")).isEqualTo("test");
|
||||
}
|
||||
}
|
|
@ -7,6 +7,9 @@ package com.microsoft.azure.spring.data.documentdb.repository;
|
|||
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface ContactRepository extends DocumentDbRepository<Contact, String> {
|
||||
List<Contact> findByTitle(String title);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,6 @@ public class ContactRepositoryConfig extends AbstractDocumentDbConfiguration {
|
|||
|
||||
@Override
|
||||
public String getDatabase() {
|
||||
return "itdb";
|
||||
return "testdb";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,6 +125,16 @@ public class ContactRepositoryIT {
|
|||
assertThat(result2.size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomQuery() {
|
||||
final List<Contact> result = repository.findByTitle(TEST_CONTACT.getTitle());
|
||||
|
||||
assertThat(result.size()).isEqualTo(1);
|
||||
assertThat(result.get(0).getLogicId()).isEqualTo(TEST_CONTACT.getLogicId());
|
||||
assertThat(result.get(0).getTitle()).isEqualTo(TEST_CONTACT.getTitle());
|
||||
|
||||
}
|
||||
|
||||
private <T> List<T> toList(Iterable<T> iterable) {
|
||||
if (iterable != null) {
|
||||
final List<T> list = new ArrayList<>();
|
||||
|
|
|
@ -5,16 +5,25 @@
|
|||
*/
|
||||
package com.microsoft.azure.spring.data.documentdb.repository.support;
|
||||
|
||||
import com.microsoft.azure.spring.data.documentdb.core.DocumentDbTemplate;
|
||||
import com.microsoft.azure.spring.data.documentdb.domain.PersonRepository;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DocumentDbRepositoryFactoryBeanUnitTest {
|
||||
@Mock
|
||||
DocumentDbTemplate dbTemplate;
|
||||
|
||||
@Test
|
||||
public void testCreateRepositoryFactory() {
|
||||
final DocumentDbRepositoryFactoryBean factoryBean = new DocumentDbRepositoryFactoryBean(PersonRepository.class);
|
||||
final DocumentDbRepositoryFactoryBean factoryBean =
|
||||
new DocumentDbRepositoryFactoryBean(PersonRepository.class, dbTemplate);
|
||||
final RepositoryFactorySupport factory = factoryBean.createRepositoryFactory();
|
||||
assertThat(factory).isNotNull();
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
package com.microsoft.azure.spring.data.documentdb.repository.support;
|
||||
|
||||
import com.microsoft.azure.spring.data.documentdb.core.mapping.DocumentDbPersistentEntity;
|
||||
import com.microsoft.azure.spring.data.documentdb.core.DocumentDbTemplate;
|
||||
import com.microsoft.azure.spring.data.documentdb.domain.Person;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
@ -13,7 +13,6 @@ import org.mockito.Mock;
|
|||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
@ -22,17 +21,14 @@ import static org.junit.Assert.assertTrue;
|
|||
public class DocumentDbRepositoryFactoryUnitTest {
|
||||
|
||||
@Mock
|
||||
MappingContext mappingContext;
|
||||
|
||||
@Mock
|
||||
DocumentDbPersistentEntity entity;
|
||||
DocumentDbTemplate dbTemplate;
|
||||
|
||||
@Autowired
|
||||
ApplicationContext applicationContext;
|
||||
|
||||
@Test
|
||||
public void useMappingDocumentDBEntityInfoIfMappingContextSet() {
|
||||
final DocumentDbRepositoryFactory factory = new DocumentDbRepositoryFactory(applicationContext);
|
||||
final DocumentDbRepositoryFactory factory = new DocumentDbRepositoryFactory(dbTemplate, applicationContext);
|
||||
final EntityInformation<Person, String> entityInfo = factory.getEntityInformation(Person.class);
|
||||
assertTrue(entityInfo instanceof DocumentDbEntityInformation);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче