From 849d54496d045346bb5a26a0202b2cea6016929a Mon Sep 17 00:00:00 2001 From: mikereiche Date: Wed, 17 Feb 2021 09:46:39 -0800 Subject: [PATCH] Revert "DATACOUCH-550 - Refactored the usage of CouchbasePersistentEntityIndexCreator. (#298)" This reverts commit 883c931cf419f91f12ba93786e1f293f77bc7467. --- pom.xml | 4 +- .../AbstractCouchbaseConfiguration.java | 14 +--- .../couchbase/core/CouchbaseTemplate.java | 39 ++++++++++- ...CouchbasePersistentEntityIndexCreator.java | 56 +++++++-------- ...ouchbasePersistentEntityIndexResolver.java | 8 ++- .../core/index/QueryIndexResolver.java | 5 +- .../core/mapping/CouchbaseMappingContext.java | 69 ++++++++++++++++++- 7 files changed, 141 insertions(+), 54 deletions(-) diff --git a/pom.xml b/pom.xml index ebc559d50..3cdfa6b59 100644 --- a/pom.xml +++ b/pom.xml @@ -157,9 +157,9 @@ - com.couchbase.mock + com.github.Couchbase CouchbaseMock - 1.5.25 + 73e493d259 test 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 f33c80b55..fb81e8e2e 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.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; @@ -282,6 +281,7 @@ public CouchbaseMappingContext couchbaseMappingContext(CustomConversions customC mappingContext.setInitialEntitySet(getInitialEntitySet()); mappingContext.setSimpleTypeHolder(customConversions.getSimpleTypeHolder()); mappingContext.setFieldNamingStrategy(fieldNamingStrategy()); + mappingContext.setAutoIndexCreation(autoIndexCreation()); return mappingContext; } @@ -293,18 +293,6 @@ protected boolean autoIndexCreation() { 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 4ba4182f0..65f4535ec 100644 --- a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java +++ b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java @@ -19,10 +19,17 @@ import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.data.couchbase.CouchbaseClientFactory; import org.springframework.data.couchbase.core.convert.CouchbaseConverter; 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.CouchbasePersistentEntity; +import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.lang.Nullable; import com.couchbase.client.java.Collection; @@ -31,7 +38,6 @@ * * @author Michael Nitschinger * @author Michael Reiche - * @author Aaron Whiteside * @author Jorge Rodriguez Martin * @since 3.0 */ @@ -40,7 +46,9 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationContex private final CouchbaseClientFactory clientFactory; private final CouchbaseConverter converter; private final CouchbaseTemplateSupport templateSupport; + private final MappingContext, CouchbasePersistentProperty> mappingContext; private final ReactiveCouchbaseTemplate reactiveCouchbaseTemplate; + private @Nullable CouchbasePersistentEntityIndexCreator indexCreator; public CouchbaseTemplate(final CouchbaseClientFactory clientFactory, final CouchbaseConverter converter) { this(clientFactory, converter, new JacksonTranslationService()); @@ -52,6 +60,14 @@ public CouchbaseTemplate(final CouchbaseClientFactory clientFactory, final Couch 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); + } + } } @Override @@ -140,7 +156,28 @@ public ReactiveCouchbaseTemplate reactive() { @Override public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException { + prepareIndexCreator(applicationContext); templateSupport.setApplicationContext(applicationContext); reactiveCouchbaseTemplate.setApplicationContext(applicationContext); } + + private void prepareIndexCreator(final ApplicationContext context) { + String[] indexCreators = context.getBeanNamesForType(CouchbasePersistentEntityIndexCreator.class); + + for (String creator : indexCreators) { + CouchbasePersistentEntityIndexCreator creatorBean = context.getBean(creator, + CouchbasePersistentEntityIndexCreator.class); + if (creatorBean.isIndexCreatorFor(mappingContext)) { + return; + } + } + + if (context instanceof ConfigurableApplicationContext && indexCreator != null) { + ((ConfigurableApplicationContext) context).addApplicationListener(indexCreator); + if (mappingContext instanceof CouchbaseMappingContext) { + CouchbaseMappingContext cmc = (CouchbaseMappingContext) mappingContext; + cmc.setIndexCreator(indexCreator); + } + } + } } 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 b4c09c986..39ca60b7f 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 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 @@ private void checkForIndexes(final CouchbasePersistentEntity entity) { 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 @@ private void checkForAndCreateIndexes(final CouchbasePersistentEntity entity) } 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 @@ private void createIndex(final IndexDefinitionHolder indexToCreate) { 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 be3f83bc6..4ef36a4de 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.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 @@ 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 @@ protected List createCompositeQueryIndexDefinitions(final } 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 7cb2e8723..27b3f45c5 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 b30284c9d..6421dcd60 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 @@ * * @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 @@ protected CouchbasePersistentProperty createPersistentProperty(Property property */ @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; + } }