DATACOUCH-161 - Allow repositories to use different templates
Added a configuration bean that allows to express what CouchbaseOperations each repository should use, individually or by domain type. Added documentation for the feature in repository section.
This commit is contained in:
@@ -36,6 +36,7 @@ import org.springframework.context.annotation.ClassPathScanningCandidateComponen
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.data.annotation.Persistent;
|
||||
import org.springframework.data.couchbase.core.CouchbaseOperations;
|
||||
import org.springframework.data.couchbase.core.CouchbaseTemplate;
|
||||
import org.springframework.data.couchbase.core.convert.CustomConversions;
|
||||
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
|
||||
@@ -44,6 +45,8 @@ import org.springframework.data.couchbase.core.convert.translation.TranslationSe
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
|
||||
import org.springframework.data.couchbase.core.mapping.Document;
|
||||
import org.springframework.data.couchbase.core.view.Consistency;
|
||||
import org.springframework.data.couchbase.repository.CouchbaseRepository;
|
||||
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
|
||||
import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy;
|
||||
import org.springframework.data.mapping.model.FieldNamingStrategy;
|
||||
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
|
||||
@@ -151,6 +154,17 @@ public abstract class AbstractCouchbaseConfiguration {
|
||||
return template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link RepositoryOperationsMapping} bean which will be used by the framework to choose which
|
||||
* {@link CouchbaseOperations} should back which {@link CouchbaseRepository}.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Bean(name = BeanNames.REPO_OPERATIONS_MAPPING)
|
||||
public RepositoryOperationsMapping repositoryOperationsMapping() throws Exception {
|
||||
return new RepositoryOperationsMapping(couchbaseTemplate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the name of the field that will store the type information for complex types when
|
||||
* using the {@link #mappingCouchbaseConverter()}.
|
||||
|
||||
@@ -22,7 +22,7 @@ package org.springframework.data.couchbase.config;
|
||||
* @author Michael Nitschinger
|
||||
* @author Simon Baslé
|
||||
*/
|
||||
class BeanNames {
|
||||
public class BeanNames {
|
||||
|
||||
/**
|
||||
* Refers to the "<couchbase:env />" bean.
|
||||
@@ -52,4 +52,9 @@ class BeanNames {
|
||||
* Refers to the "<couchbase:clusterInfo>" bean
|
||||
*/
|
||||
static final String COUCHBASE_CLUSTER_INFO = "couchbaseClusterInfo";
|
||||
|
||||
/**
|
||||
* The bean that stores custom mapping between repositories and their backing couchbaseOperations.
|
||||
*/
|
||||
public static final String REPO_OPERATIONS_MAPPING = "repositoryOperationsMapping";
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.lang.annotation.Annotation;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.couchbase.core.CouchbaseOperations;
|
||||
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
|
||||
import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory;
|
||||
import org.springframework.data.repository.cdi.CdiRepositoryBean;
|
||||
import org.springframework.data.repository.config.CustomRepositoryImplementationDetector;
|
||||
@@ -61,7 +62,8 @@ public class CouchbaseRepositoryBean<T> extends CdiRepositoryBean<T> {
|
||||
@Override
|
||||
protected T create(CreationalContext<T> creationalContext, Class<T> repositoryType, Object customImplementation) {
|
||||
CouchbaseOperations couchbaseOperations = getDependencyInstance(couchbaseOperationsBean, CouchbaseOperations.class);
|
||||
return new CouchbaseRepositoryFactory(couchbaseOperations).getRepository(repositoryType, customImplementation);
|
||||
RepositoryOperationsMapping couchbaseOperationsMapping = new RepositoryOperationsMapping(couchbaseOperations);
|
||||
return new CouchbaseRepositoryFactory(couchbaseOperationsMapping).getRepository(repositoryType, customImplementation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,14 +16,15 @@
|
||||
|
||||
package org.springframework.data.couchbase.repository.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.data.config.ParsingUtils;
|
||||
import org.springframework.data.couchbase.config.BeanNames;
|
||||
import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactoryBean;
|
||||
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
|
||||
import org.springframework.data.repository.config.XmlRepositoryConfigurationSource;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* @author Michael Nitschinger
|
||||
@@ -49,7 +50,7 @@ public class CouchbaseRepositoryConfigurationExtension extends RepositoryConfigu
|
||||
|
||||
@Override
|
||||
public void postProcess(final BeanDefinitionBuilder builder, final AnnotationRepositoryConfigurationSource config) {
|
||||
AnnotationAttributes attributes = config.getAttributes();
|
||||
builder.addPropertyReference("couchbaseOperations", attributes.getString("couchbaseTemplateRef"));
|
||||
builder.addDependsOn(BeanNames.REPO_OPERATIONS_MAPPING);
|
||||
builder.addPropertyReference("couchbaseOperationsMapping", BeanNames.REPO_OPERATIONS_MAPPING);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
|
||||
package org.springframework.data.couchbase.repository.config;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.couchbase.core.CouchbaseTemplate;
|
||||
import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactoryBean;
|
||||
import org.springframework.data.repository.config.DefaultRepositoryBaseClass;
|
||||
|
||||
@@ -100,7 +102,13 @@ public @interface EnableCouchbaseRepositories {
|
||||
Class<?> repositoryFactoryBeanClass() default CouchbaseRepositoryFactoryBean.class;
|
||||
|
||||
/**
|
||||
* Configures the name of the {@link CouchbaseTemplate} bean to be used with the repositories detected.
|
||||
* Configures whether nested repository-interfaces (e.g. defined as inner classes) should be discovered by the
|
||||
* repositories infrastructure.
|
||||
*/
|
||||
boolean considerNestedRepositories() default false;
|
||||
|
||||
/**
|
||||
* Configures the name of the {@link CouchbaseTemplate} bean to be used by default with the repositories detected.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2012-2015 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.couchbase.repository.config;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.couchbase.core.CouchbaseOperations;
|
||||
|
||||
/**
|
||||
* A utility class for configuration allowing to tell which {@link CouchbaseOperations} should be backing
|
||||
* repositories. Its fluent API allows to set that (in order of precedence) for specific repository interfaces,
|
||||
* by repository domain type or as a default fallback.
|
||||
*
|
||||
* @author Simon Baslé
|
||||
*/
|
||||
public class RepositoryOperationsMapping {
|
||||
private CouchbaseOperations defaultOperations;
|
||||
private Map<String, CouchbaseOperations> byRepository = new HashMap<String, CouchbaseOperations>();
|
||||
private Map<String, CouchbaseOperations> byEntity = new HashMap<String, CouchbaseOperations>();
|
||||
|
||||
/**
|
||||
* Creates a new mapping, setting the default fallback to use by otherwise non mapped repositories.
|
||||
*
|
||||
* @param defaultOperations the default fallback couchbase operations.
|
||||
*/
|
||||
public RepositoryOperationsMapping(CouchbaseOperations defaultOperations) {
|
||||
this.defaultOperations = defaultOperations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the default couchbase operations in an existing mapping.
|
||||
*
|
||||
* @param aDefault the new default couchbase operations.
|
||||
* @return the mapping, for chaining.
|
||||
*/
|
||||
public RepositoryOperationsMapping setDefault(CouchbaseOperations aDefault) {
|
||||
this.defaultOperations = aDefault;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a highest priority mapping that will associate a specific repository interface with a given {@link CouchbaseOperations}.
|
||||
*
|
||||
* @param repositoryInterface the repository interface {@link Class}.
|
||||
* @param couchbaseOperations the CouchbaseOperations to use.
|
||||
* @return the mapping, for chaining.
|
||||
*/
|
||||
public RepositoryOperationsMapping map(Class<?> repositoryInterface, CouchbaseOperations couchbaseOperations) {
|
||||
byRepository.put(repositoryInterface.getName(), couchbaseOperations);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a middle priority mapping that will associate any un-mapped repository that deals with the given domain type
|
||||
* Class with a given {@link CouchbaseOperations}.
|
||||
*
|
||||
* @param entityClass the domain type's {@link Class}.
|
||||
* @param couchbaseOperations the CouchbaseOperations to use.
|
||||
* @return the mapping, for chaining.
|
||||
*/
|
||||
public RepositoryOperationsMapping mapEntity(Class<?> entityClass, CouchbaseOperations couchbaseOperations) {
|
||||
byEntity.put(entityClass.getName(), couchbaseOperations);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the configured default {@link CouchbaseOperations}.
|
||||
*/
|
||||
public CouchbaseOperations getDefault() {
|
||||
return defaultOperations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a repository interface and its domain type, resolves which {@link CouchbaseOperations} it should be backed with.
|
||||
*
|
||||
* Starts by looking for a direct mapping to the interface, then a common mapping for the domain type, then falls back
|
||||
* to the default CouchbaseOperations.
|
||||
*
|
||||
* @param repositoryInterface the repository's interface.
|
||||
* @param domainType the repository's domain type / entity.
|
||||
* @return the CouchbaseOperations to back the repository.
|
||||
*/
|
||||
public CouchbaseOperations resolve(Class<?> repositoryInterface, Class<?> domainType) {
|
||||
CouchbaseOperations result = byRepository.get(repositoryInterface.getName());
|
||||
if (result != null) {
|
||||
return result;
|
||||
} else {
|
||||
result = byEntity.get(domainType.getName());
|
||||
if (result != null) {
|
||||
return result;
|
||||
} else {
|
||||
return defaultOperations;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
|
||||
import org.springframework.data.couchbase.core.view.Query;
|
||||
import org.springframework.data.couchbase.core.view.View;
|
||||
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
|
||||
import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation;
|
||||
import org.springframework.data.couchbase.repository.query.CouchbaseQueryMethod;
|
||||
import org.springframework.data.couchbase.repository.query.PartTreeN1qlBasedQuery;
|
||||
@@ -54,7 +55,7 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport {
|
||||
/**
|
||||
* Holds the reference to the template.
|
||||
*/
|
||||
private final CouchbaseOperations couchbaseOperations;
|
||||
private final RepositoryOperationsMapping couchbaseOperationsMapping;
|
||||
|
||||
/**
|
||||
* Holds the mapping context.
|
||||
@@ -66,26 +67,19 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport {
|
||||
*/
|
||||
private final ViewPostProcessor viewPostProcessor;
|
||||
|
||||
/**
|
||||
* Flag indicating if N1QL is available on the underlying cluster (at the time of the factory's creation).
|
||||
*/
|
||||
private final boolean isN1qlAvailable;
|
||||
|
||||
/**
|
||||
* Create a new factory.
|
||||
*
|
||||
* @param couchbaseOperations the template for the underlying actions.
|
||||
* @param couchbaseOperationsMapping the template for the underlying actions.
|
||||
*/
|
||||
public CouchbaseRepositoryFactory(final CouchbaseOperations couchbaseOperations) {
|
||||
Assert.notNull(couchbaseOperations);
|
||||
public CouchbaseRepositoryFactory(final RepositoryOperationsMapping couchbaseOperationsMapping) {
|
||||
Assert.notNull(couchbaseOperationsMapping);
|
||||
|
||||
this.couchbaseOperations = couchbaseOperations;
|
||||
mappingContext = couchbaseOperations.getConverter().getMappingContext();
|
||||
this.couchbaseOperationsMapping = couchbaseOperationsMapping;
|
||||
mappingContext = this.couchbaseOperationsMapping.getDefault().getConverter().getMappingContext();
|
||||
viewPostProcessor = ViewPostProcessor.INSTANCE;
|
||||
|
||||
addRepositoryProxyPostProcessor(viewPostProcessor);
|
||||
|
||||
this.isN1qlAvailable = couchbaseOperations.getCouchbaseClusterInfo().checkAvailable(CouchbaseFeature.N1QL);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,10 +111,12 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport {
|
||||
*/
|
||||
@Override
|
||||
protected Object getTargetRepository(final RepositoryInformation metadata) {
|
||||
checkFeatures(metadata);
|
||||
CouchbaseOperations couchbaseOperations = couchbaseOperationsMapping.resolve(metadata.getRepositoryInterface(), metadata.getDomainType());
|
||||
boolean isN1qlAvailable = couchbaseOperations.getCouchbaseClusterInfo().checkAvailable(CouchbaseFeature.N1QL);
|
||||
checkFeatures(metadata, isN1qlAvailable);
|
||||
|
||||
CouchbaseEntityInformation<?, Serializable> entityInformation = getEntityInformation(metadata.getDomainType());
|
||||
if (this.isN1qlAvailable) {
|
||||
if (isN1qlAvailable) {
|
||||
//this implementation also conforms to PagingAndSortingRepository
|
||||
N1qlCouchbaseRepository n1qlRepository = new N1qlCouchbaseRepository(entityInformation, couchbaseOperations);
|
||||
n1qlRepository.setViewMetadataProvider(viewPostProcessor.getViewMetadataProvider());
|
||||
@@ -132,7 +128,7 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkFeatures(RepositoryInformation metadata) {
|
||||
private void checkFeatures(RepositoryInformation metadata, boolean isN1qlAvailable) {
|
||||
boolean needsN1ql = metadata.isPagingRepository();
|
||||
//paging repo will need N1QL, other repos might also if they don't have only @View methods
|
||||
if (!needsN1ql) {
|
||||
@@ -148,7 +144,7 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport {
|
||||
}
|
||||
}
|
||||
|
||||
if (needsN1ql && !this.isN1qlAvailable) {
|
||||
if (needsN1ql && !isN1qlAvailable) {
|
||||
throw new UnsupportedCouchbaseFeatureException("Repository uses N1QL", CouchbaseFeature.N1QL);
|
||||
}
|
||||
}
|
||||
@@ -162,6 +158,9 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport {
|
||||
*/
|
||||
@Override
|
||||
protected Class<?> getRepositoryBaseClass(final RepositoryMetadata repositoryMetadata) {
|
||||
CouchbaseOperations couchbaseOperations = couchbaseOperationsMapping.resolve(repositoryMetadata.getRepositoryInterface(),
|
||||
repositoryMetadata.getDomainType());
|
||||
boolean isN1qlAvailable = couchbaseOperations.getCouchbaseClusterInfo().checkAvailable(CouchbaseFeature.N1QL);
|
||||
if (isN1qlAvailable) {
|
||||
return N1qlCouchbaseRepository.class;
|
||||
}
|
||||
@@ -179,6 +178,9 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport {
|
||||
private class CouchbaseQueryLookupStrategy implements QueryLookupStrategy {
|
||||
@Override
|
||||
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, NamedQueries namedQueries) {
|
||||
CouchbaseOperations couchbaseOperations = couchbaseOperationsMapping.resolve(metadata.getRepositoryInterface(),
|
||||
metadata.getDomainType());
|
||||
|
||||
CouchbaseQueryMethod queryMethod = new CouchbaseQueryMethod(method, metadata, mappingContext);
|
||||
String namedQueryName = queryMethod.getNamedQueryName();
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.data.couchbase.repository.support;
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.data.couchbase.core.CouchbaseOperations;
|
||||
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
@@ -35,7 +36,7 @@ public class CouchbaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID e
|
||||
/**
|
||||
* Contains the reference to the template.
|
||||
*/
|
||||
private CouchbaseOperations operations;
|
||||
private RepositoryOperationsMapping operationsMapping;
|
||||
|
||||
/**
|
||||
* Set the template reference.
|
||||
@@ -43,8 +44,12 @@ public class CouchbaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID e
|
||||
* @param operations the reference to the operations template.
|
||||
*/
|
||||
public void setCouchbaseOperations(final CouchbaseOperations operations) {
|
||||
this.operations = operations;
|
||||
setMappingContext(operations.getConverter().getMappingContext());
|
||||
setCouchbaseOperationsMapping(new RepositoryOperationsMapping(operations));
|
||||
}
|
||||
|
||||
public void setCouchbaseOperationsMapping(final RepositoryOperationsMapping mapping) {
|
||||
this.operationsMapping = mapping;
|
||||
setMappingContext(operationsMapping.getDefault().getConverter().getMappingContext());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,17 +59,17 @@ public class CouchbaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID e
|
||||
*/
|
||||
@Override
|
||||
protected RepositoryFactorySupport createRepositoryFactory() {
|
||||
return getFactoryInstance(operations);
|
||||
return getFactoryInstance(operationsMapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the factory instance for the operations.
|
||||
*
|
||||
* @param operations the reference to the template.
|
||||
* @param operationsMapping the reference to the template.
|
||||
* @return the factory instance.
|
||||
*/
|
||||
private RepositoryFactorySupport getFactoryInstance(final CouchbaseOperations operations) {
|
||||
return new CouchbaseRepositoryFactory(operations);
|
||||
private RepositoryFactorySupport getFactoryInstance(final RepositoryOperationsMapping operationsMapping) {
|
||||
return new CouchbaseRepositoryFactory(operationsMapping);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,6 +78,6 @@ public class CouchbaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID e
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
Assert.notNull(operations, "CouchbaseTemplate must not be null!");
|
||||
Assert.notNull(operationsMapping, "operationsMapping must not be null!");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user