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 extends CouchbasePersistentEntity>, 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 extends CouchbasePersistentEntity>, CouchbasePersistentProperty> mappingContext;
- private final String typeKey;
+ private final CouchbaseOperations operations;
public CouchbasePersistentEntityIndexResolver(
final MappingContext extends CouchbasePersistentEntity>, 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, Object> 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 extends CouchbasePersistentEntity>, 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;
+ }
}