initial support of custom query (#9)

* initial support of custom query

* change test db name
This commit is contained in:
yungez 2018-01-02 15:37:20 +08:00 коммит произвёл GitHub
Родитель 2c2daa0ade
Коммит 72ec322bf0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
29 изменённых файлов: 758 добавлений и 27 удалений

Просмотреть файл

@ -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);
}