diff --git a/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java b/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java index f33c80b5..32cf783c 100644 --- a/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java +++ b/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java @@ -37,7 +37,6 @@ import org.springframework.data.couchbase.core.convert.CouchbaseCustomConversion import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter; import org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService; import org.springframework.data.couchbase.core.convert.translation.TranslationService; -import org.springframework.data.couchbase.core.index.CouchbasePersistentEntityIndexCreator; import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext; import org.springframework.data.couchbase.core.mapping.Document; import org.springframework.data.couchbase.repository.config.ReactiveRepositoryOperationsMapping; @@ -62,7 +61,6 @@ import com.couchbase.client.java.json.JacksonTransformers; * @author Simon Baslé * @author Stephane Nicoll * @author Subhashni Balakrishnan - * @author Jorge Rodriguez Martin */ @Configuration public abstract class AbstractCouchbaseConfiguration { @@ -146,27 +144,15 @@ public abstract class AbstractCouchbaseConfiguration { } @Bean(name = BeanNames.COUCHBASE_TEMPLATE) - public CouchbaseTemplate couchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory, - MappingCouchbaseConverter mappingCouchbaseConverter, TranslationService couchbaseTranslationService) { - return new CouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter, couchbaseTranslationService); - } - public CouchbaseTemplate couchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory, MappingCouchbaseConverter mappingCouchbaseConverter) { - return couchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter, new JacksonTranslationService()); + return new CouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter); } @Bean(name = BeanNames.REACTIVE_COUCHBASE_TEMPLATE) - public ReactiveCouchbaseTemplate reactiveCouchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory, - MappingCouchbaseConverter mappingCouchbaseConverter, TranslationService couchbaseTranslationService) { - return new ReactiveCouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter, - couchbaseTranslationService); - } - public ReactiveCouchbaseTemplate reactiveCouchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory, MappingCouchbaseConverter mappingCouchbaseConverter) { - return reactiveCouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter, - new JacksonTranslationService()); + return new ReactiveCouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter); } @Bean(name = BeanNames.COUCHBASE_OPERATIONS_MAPPING) @@ -282,6 +268,7 @@ public abstract class AbstractCouchbaseConfiguration { mappingContext.setInitialEntitySet(getInitialEntitySet()); mappingContext.setSimpleTypeHolder(customConversions.getSimpleTypeHolder()); mappingContext.setFieldNamingStrategy(fieldNamingStrategy()); + mappingContext.setAutoIndexCreation(autoIndexCreation()); return mappingContext; } @@ -293,18 +280,6 @@ public abstract class AbstractCouchbaseConfiguration { return false; } - /** - * Creates a {@link CouchbasePersistentEntityIndexCreator} bean that takes on the responsibility of automatically - * creating indices. - * - * Does nothing if {@link #autoIndexCreation()} returns false. - */ - @Bean - public CouchbasePersistentEntityIndexCreator couchbasePersistentEntityIndexCreator(CouchbaseMappingContext couchbaseMappingContext, - CouchbaseClientFactory clientFactory) { - return new CouchbasePersistentEntityIndexCreator(couchbaseMappingContext, clientFactory, typeKey(), autoIndexCreation()); - } - /** * Register custom Converters in a {@link CustomConversions} object if required. These {@link CustomConversions} will * be registered with the {@link #mappingCouchbaseConverter(CouchbaseMappingContext, CouchbaseCustomConversions)} )} diff --git a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java index 8b10ac22..9fc59eb3 100644 --- a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java +++ b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java @@ -38,7 +38,6 @@ import com.couchbase.client.java.Collection; * * @author Michael Nitschinger * @author Michael Reiche - * @author Aaron Whiteside * @author Jorge Rodriguez Martin * @since 3.0 */ @@ -56,19 +55,19 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationContex } public CouchbaseTemplate(final CouchbaseClientFactory clientFactory, final CouchbaseConverter converter, - final TranslationService translationService) { + final TranslationService translationService) { this.clientFactory = clientFactory; this.converter = converter; this.templateSupport = new CouchbaseTemplateSupport(converter, translationService); this.reactiveCouchbaseTemplate = new ReactiveCouchbaseTemplate(clientFactory, converter, translationService); - + this.mappingContext = this.converter.getMappingContext(); - if (mappingContext instanceof CouchbaseMappingContext) { - CouchbaseMappingContext cmc = (CouchbaseMappingContext) mappingContext; - if (cmc.isAutoIndexCreation()) { - indexCreator = new CouchbasePersistentEntityIndexCreator(cmc, this); - } - } + if (mappingContext instanceof CouchbaseMappingContext) { + CouchbaseMappingContext cmc = (CouchbaseMappingContext) mappingContext; + if (cmc.isAutoIndexCreation()) { + indexCreator = new CouchbasePersistentEntityIndexCreator(cmc, this); + } + } } @Override diff --git a/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexCreator.java b/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexCreator.java index b4c09c98..39ca60b7 100644 --- a/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexCreator.java +++ b/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexCreator.java @@ -20,56 +20,39 @@ import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationListener; import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.data.couchbase.CouchbaseClientFactory; +import org.springframework.data.couchbase.core.CouchbaseOperations; import org.springframework.data.couchbase.core.index.CouchbasePersistentEntityIndexResolver.IndexDefinitionHolder; import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity; import org.springframework.data.couchbase.core.mapping.Document; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.mapping.context.MappingContextEvent; import com.couchbase.client.core.error.IndexExistsException; import com.couchbase.client.java.Cluster; -import org.springframework.data.mapping.PersistentEntity; -import org.springframework.data.mapping.context.MappingContextEvent; -/** - * Encapsulates the logic of creating indices. - * - * @author Michael Nitschinger - * @author Aaron Whiteside - */ -public class CouchbasePersistentEntityIndexCreator implements InitializingBean, ApplicationListener> { +public class CouchbasePersistentEntityIndexCreator implements ApplicationListener> { private static final Logger LOGGER = LoggerFactory.getLogger(CouchbasePersistentEntityIndexCreator.class); private final Map, Boolean> classesSeen = new ConcurrentHashMap<>(); private final CouchbaseMappingContext mappingContext; private final QueryIndexResolver indexResolver; - private final CouchbaseClientFactory clientFactory; - private final boolean enabled; + private final CouchbaseOperations couchbaseOperations; public CouchbasePersistentEntityIndexCreator(final CouchbaseMappingContext mappingContext, - final CouchbaseClientFactory clientFactory, final String typeKey, final boolean enabled) { + final CouchbaseOperations operations) { this.mappingContext = mappingContext; - this.clientFactory = clientFactory; - this.enabled = enabled; - this.indexResolver = QueryIndexResolver.create(mappingContext, typeKey); - } - - @Override - public void afterPropertiesSet() throws Exception { - if (enabled) { - mappingContext.getPersistentEntities().forEach(this::checkForIndexes); - } else { - LOGGER.debug("Automatic index creation not enabled."); - } + this.couchbaseOperations = operations; + this.indexResolver = QueryIndexResolver.create(mappingContext, operations); } @Override public void onApplicationEvent(final MappingContextEvent event) { - if (!enabled || !event.wasEmittedBy(mappingContext)) { + if (!event.wasEmittedBy(mappingContext)) { return; } @@ -88,7 +71,7 @@ public class CouchbasePersistentEntityIndexCreator implements InitializingBean, this.classesSeen.put(type, Boolean.TRUE); if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Analyzing class {} for index information.", type); + LOGGER.debug("Analyzing class " + type + " for index information."); } checkForAndCreateIndexes(entity); @@ -110,10 +93,10 @@ public class CouchbasePersistentEntityIndexCreator implements InitializingBean, } private void createIndex(final IndexDefinitionHolder indexToCreate) { - Cluster cluster = clientFactory.getCluster(); + Cluster cluster = couchbaseOperations.getCouchbaseClientFactory().getCluster(); StringBuilder statement = new StringBuilder("CREATE INDEX ").append(indexToCreate.getIndexName()).append(" ON `") - .append(clientFactory.getBucket().name()).append("` (") + .append(couchbaseOperations.getBucketName()).append("` (") .append(String.join(",", indexToCreate.getIndexFields())).append(")"); if (indexToCreate.getIndexPredicate() != null && !indexToCreate.getIndexPredicate().isEmpty()) { @@ -124,10 +107,21 @@ public class CouchbasePersistentEntityIndexCreator implements InitializingBean, cluster.query(statement.toString()); } catch (IndexExistsException ex) { // ignored on purpose, rest is propagated - LOGGER.debug("Index \"{}\" already exists, ignoring.", indexToCreate.getIndexName()); + LOGGER.debug("Index \"" + indexToCreate.getIndexName() + "\" already exists, ignoring."); } catch (Exception ex) { throw new DataIntegrityViolationException("Could not auto-create index with statement: " + statement.toString(), ex); } } + + /** + * Returns whether the current index creator was registered for the given {@link MappingContext}. + */ + public boolean isIndexCreatorFor(final MappingContext context) { + return this.mappingContext.equals(context); + } + + public boolean hasSeen(CouchbasePersistentEntity entity) { + return classesSeen.containsKey(entity.getType()); + } } diff --git a/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexResolver.java b/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexResolver.java index be3f83bc..4ef36a4d 100644 --- a/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexResolver.java +++ b/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexResolver.java @@ -20,6 +20,7 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; +import org.springframework.data.couchbase.core.CouchbaseOperations; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty; import org.springframework.data.couchbase.core.mapping.Document; @@ -34,13 +35,13 @@ import org.springframework.util.StringUtils; public class CouchbasePersistentEntityIndexResolver implements QueryIndexResolver { private final MappingContext, CouchbasePersistentProperty> mappingContext; - private final String typeKey; + private final CouchbaseOperations operations; public CouchbasePersistentEntityIndexResolver( final MappingContext, CouchbasePersistentProperty> mappingContext, - final String typeKey) { + CouchbaseOperations operations) { this.mappingContext = mappingContext; - this.typeKey = typeKey; + this.operations = operations; } @Override @@ -134,6 +135,7 @@ public class CouchbasePersistentEntityIndexResolver implements QueryIndexResolve } private String getPredicate(final MappingCouchbaseEntityInformation entityInfo) { + String typeKey = operations.getConverter().getTypeKey(); String typeValue = entityInfo.getJavaType().getName(); return "`" + typeKey + "` = \"" + typeValue + "\""; } diff --git a/src/main/java/org/springframework/data/couchbase/core/index/QueryIndexResolver.java b/src/main/java/org/springframework/data/couchbase/core/index/QueryIndexResolver.java index 7cb2e872..27b3f45c 100644 --- a/src/main/java/org/springframework/data/couchbase/core/index/QueryIndexResolver.java +++ b/src/main/java/org/springframework/data/couchbase/core/index/QueryIndexResolver.java @@ -15,6 +15,7 @@ */ package org.springframework.data.couchbase.core.index; +import org.springframework.data.couchbase.core.CouchbaseOperations; import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty; @@ -42,9 +43,9 @@ public interface QueryIndexResolver { */ static QueryIndexResolver create( MappingContext, CouchbasePersistentProperty> mappingContext, - String typeKey) { + CouchbaseOperations operations) { Assert.notNull(mappingContext, "CouchbaseMappingContext must not be null!"); - return new CouchbasePersistentEntityIndexResolver(mappingContext, typeKey); + return new CouchbasePersistentEntityIndexResolver(mappingContext, operations); } /** diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java index b30284c9..6421dcd6 100644 --- a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java @@ -16,10 +16,15 @@ package org.springframework.data.couchbase.core.mapping; +import java.util.Optional; + import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.data.couchbase.core.index.CouchbasePersistentEntityIndexCreator; import org.springframework.data.mapping.context.AbstractMappingContext; +import org.springframework.data.mapping.context.MappingContextEvent; import org.springframework.data.mapping.model.FieldNamingStrategy; import org.springframework.data.mapping.model.Property; import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy; @@ -32,7 +37,6 @@ import org.springframework.data.util.TypeInformation; * * @author Michael Nitschinger * @author Michael Reiche - * @author Aaron Whiteside */ public class CouchbaseMappingContext extends AbstractMappingContext, CouchbasePersistentProperty> @@ -51,6 +55,10 @@ public class CouchbaseMappingContext */ private FieldNamingStrategy fieldNamingStrategy = DEFAULT_NAMING_STRATEGY; + private boolean autoIndexCreation = true; + private ApplicationEventPublisher eventPublisher; + private CouchbasePersistentEntityIndexCreator indexCreator = null; + /** * Configures the {@link FieldNamingStrategy} to be used to determine the field name if no manual mapping is applied. * Defaults to a strategy using the plain property name. @@ -100,8 +108,65 @@ public class CouchbaseMappingContext */ @Override public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException { - super.setApplicationContext(applicationContext); context = applicationContext; + super.setApplicationContext(applicationContext); } + @Override + public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { + eventPublisher = applicationEventPublisher; + if (this.eventPublisher == null) { + this.eventPublisher = context; + } + } + + public boolean isAutoIndexCreation() { + return autoIndexCreation; + } + + public void setAutoIndexCreation(boolean autoCreateIndexes) { + this.autoIndexCreation = autoCreateIndexes; + } + + /** + * override method from AbstractMappingContext as that method will not publishEvent() if it finds the entity has + * already been cached + * + * @param typeInformation - entity type + */ + @Override + protected Optional> addPersistentEntity(TypeInformation typeInformation) { + Optional> entity = super.addPersistentEntity(typeInformation); + + if (this.eventPublisher != null && entity.isPresent()) { + if (this.indexCreator != null) { + if (!indexCreator.hasSeen(entity.get())) { + this.eventPublisher.publishEvent(new MappingContextEvent(this, entity.get())); + } + } + } + return entity; + } + + /** + * override method from AbstractMappingContext as that method will not publishEvent() if it finds the entity has + * already been cached. Instead, user our own addPersistEntity that will. + * + * @param typeInformation - entity type + */ + @Override + public BasicCouchbasePersistentEntity getPersistentEntity(TypeInformation typeInformation) { + Optional> entity = addPersistentEntity(typeInformation); + return entity.isPresent() ? entity.get() : null; + } + + /** + * capture the indexCreator when it has been added as a listener. only publishEvent() if the indexCreator hasn't + * already seen the class. + * + * @param indexCreator + */ + public void setIndexCreator(CouchbasePersistentEntityIndexCreator indexCreator) { + this.indexCreator = indexCreator; + } }