Skip to content

Commit 80bae4b

Browse files
DATACOUCH-550 - Refactored the usage of CouchbasePersistentEntityIndexCreator. (#298)
pom.xml - updated CouchbaseMock dependency to the official release on maven central, which now includes the required commit. CouchbaseTemplate.java - remove creation of CouchbasePersistentEntityIndexCreator - there can only be a single CouchbaseMappingContext and CouchbasePersistentEntityIndexCreator, not sure why there was code dealing with multiples of each, and there was no corresponding tests for these assumptions. - no longer does the job of a @Configuation class by gluing beans together with their dependencies. CouchbaseMappingContext.java - removed references to auto index creation, that functionality should be a completely separate concern. CouchbasePersistentEntityIndexCreator.java - as well as implementing ApplicationListener, it now also implements InitializingBean - when afterPropertiesSet() is called it uses CouchbaseMappingContext#getPersistentEntities to retrieve the list of entity candidates for index creation. This will handle the creation of indices for entities known at @configuration time. - for entities not in the initialEntitySet of the MappingContext, the ApplicationListener will handle those entities at runtime. - this is needed because the AbstractMappingContext will emit events when it is initialized via InitializingBean, and any beans that depend on MappingContext and also implement ApplicationListener expecting to receive those events won't. - in our case because Spring will have fully initialised the CouchbaseMappingContext instance before passing it to the CouchbasePersistentEntityIndexCreator instance we won't get those events. - added a boolean flag to indicate if automatic index creation is to be enabled or now, passed in by the factory method that creates this bean. - minor logging cleanup, use property placeholders where appropriate AbstractCouchbaseConfiguration.java - added a new factory method for the CouchbasePersistentEntityIndexCreator bean. - CouchbaseMappingContext no longer has anything to do with index creation, other than supplying a candidate list of entities that may need indices. Prompted by: spring-projects/spring-boot#24525 and the discussion on #295 Co-authored-by: Aaron Whiteside <awhiteside@yapstone.com>
1 parent 59bf9d7 commit 80bae4b

File tree

7 files changed

+54
-141
lines changed

7 files changed

+54
-141
lines changed

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,9 @@
157157
</dependency>
158158

159159
<dependency>
160-
<groupId>com.github.Couchbase</groupId>
160+
<groupId>com.couchbase.mock</groupId>
161161
<artifactId>CouchbaseMock</artifactId>
162-
<version>73e493d259</version>
162+
<version>1.5.25</version>
163163
<scope>test</scope>
164164
</dependency>
165165

src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
3838
import org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService;
3939
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
40+
import org.springframework.data.couchbase.core.index.CouchbasePersistentEntityIndexCreator;
4041
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
4142
import org.springframework.data.couchbase.core.mapping.Document;
4243
import org.springframework.data.couchbase.repository.config.ReactiveRepositoryOperationsMapping;
@@ -268,7 +269,6 @@ public CouchbaseMappingContext couchbaseMappingContext(CustomConversions customC
268269
mappingContext.setInitialEntitySet(getInitialEntitySet());
269270
mappingContext.setSimpleTypeHolder(customConversions.getSimpleTypeHolder());
270271
mappingContext.setFieldNamingStrategy(fieldNamingStrategy());
271-
mappingContext.setAutoIndexCreation(autoIndexCreation());
272272

273273
return mappingContext;
274274
}
@@ -280,6 +280,18 @@ protected boolean autoIndexCreation() {
280280
return false;
281281
}
282282

283+
/**
284+
* Creates a {@link CouchbasePersistentEntityIndexCreator} bean that takes on the responsibility of automatically
285+
* creating indices.
286+
*
287+
* Does nothing if {@link #autoIndexCreation()} returns false.
288+
*/
289+
@Bean
290+
public CouchbasePersistentEntityIndexCreator couchbasePersistentEntityIndexCreator(CouchbaseMappingContext couchbaseMappingContext,
291+
CouchbaseClientFactory clientFactory) {
292+
return new CouchbasePersistentEntityIndexCreator(couchbaseMappingContext, clientFactory, typeKey(), autoIndexCreation());
293+
}
294+
283295
/**
284296
* Register custom Converters in a {@link CustomConversions} object if required. These {@link CustomConversions} will
285297
* be registered with the {@link #mappingCouchbaseConverter(CouchbaseMappingContext, CouchbaseCustomConversions)} )}

src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,8 @@
1919
import org.springframework.beans.BeansException;
2020
import org.springframework.context.ApplicationContext;
2121
import org.springframework.context.ApplicationContextAware;
22-
import org.springframework.context.ConfigurableApplicationContext;
2322
import org.springframework.data.couchbase.CouchbaseClientFactory;
2423
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
25-
import org.springframework.data.couchbase.core.index.CouchbasePersistentEntityIndexCreator;
26-
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
27-
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
28-
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
29-
import org.springframework.data.mapping.context.MappingContext;
30-
import org.springframework.lang.Nullable;
3124

3225
import com.couchbase.client.java.Collection;
3326

@@ -36,30 +29,21 @@
3629
*
3730
* @author Michael Nitschinger
3831
* @author Michael Reiche
32+
* @author Aaron Whiteside
3933
* @since 3.0
4034
*/
4135
public class CouchbaseTemplate implements CouchbaseOperations, ApplicationContextAware {
4236

4337
private final CouchbaseClientFactory clientFactory;
4438
private final CouchbaseConverter converter;
4539
private final CouchbaseTemplateSupport templateSupport;
46-
private final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
4740
private final ReactiveCouchbaseTemplate reactiveCouchbaseTemplate;
48-
private @Nullable CouchbasePersistentEntityIndexCreator indexCreator;
4941

5042
public CouchbaseTemplate(final CouchbaseClientFactory clientFactory, final CouchbaseConverter converter) {
5143
this.clientFactory = clientFactory;
5244
this.converter = converter;
5345
this.templateSupport = new CouchbaseTemplateSupport(converter);
5446
this.reactiveCouchbaseTemplate = new ReactiveCouchbaseTemplate(clientFactory, converter);
55-
56-
this.mappingContext = this.converter.getMappingContext();
57-
if (mappingContext instanceof CouchbaseMappingContext) {
58-
CouchbaseMappingContext cmc = (CouchbaseMappingContext) mappingContext;
59-
if (cmc.isAutoIndexCreation()) {
60-
indexCreator = new CouchbasePersistentEntityIndexCreator(cmc, this);
61-
}
62-
}
6347
}
6448

6549
@Override
@@ -148,28 +132,7 @@ public ReactiveCouchbaseTemplate reactive() {
148132

149133
@Override
150134
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
151-
prepareIndexCreator(applicationContext);
152135
templateSupport.setApplicationContext(applicationContext);
153136
reactiveCouchbaseTemplate.setApplicationContext(applicationContext);
154137
}
155-
156-
private void prepareIndexCreator(final ApplicationContext context) {
157-
String[] indexCreators = context.getBeanNamesForType(CouchbasePersistentEntityIndexCreator.class);
158-
159-
for (String creator : indexCreators) {
160-
CouchbasePersistentEntityIndexCreator creatorBean = context.getBean(creator,
161-
CouchbasePersistentEntityIndexCreator.class);
162-
if (creatorBean.isIndexCreatorFor(mappingContext)) {
163-
return;
164-
}
165-
}
166-
167-
if (context instanceof ConfigurableApplicationContext && indexCreator != null) {
168-
((ConfigurableApplicationContext) context).addApplicationListener(indexCreator);
169-
if (mappingContext instanceof CouchbaseMappingContext) {
170-
CouchbaseMappingContext cmc = (CouchbaseMappingContext) mappingContext;
171-
cmc.setIndexCreator(indexCreator);
172-
}
173-
}
174-
}
175138
}

src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexCreator.java

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,39 +20,56 @@
2020

2121
import org.slf4j.Logger;
2222
import org.slf4j.LoggerFactory;
23+
import org.springframework.beans.factory.InitializingBean;
2324
import org.springframework.context.ApplicationListener;
2425
import org.springframework.dao.DataIntegrityViolationException;
25-
import org.springframework.data.couchbase.core.CouchbaseOperations;
26+
import org.springframework.data.couchbase.CouchbaseClientFactory;
2627
import org.springframework.data.couchbase.core.index.CouchbasePersistentEntityIndexResolver.IndexDefinitionHolder;
2728
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
2829
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
2930
import org.springframework.data.couchbase.core.mapping.Document;
30-
import org.springframework.data.mapping.PersistentEntity;
31-
import org.springframework.data.mapping.context.MappingContext;
32-
import org.springframework.data.mapping.context.MappingContextEvent;
3331

3432
import com.couchbase.client.core.error.IndexExistsException;
3533
import com.couchbase.client.java.Cluster;
34+
import org.springframework.data.mapping.PersistentEntity;
35+
import org.springframework.data.mapping.context.MappingContextEvent;
3636

37-
public class CouchbasePersistentEntityIndexCreator implements ApplicationListener<MappingContextEvent<?, ?>> {
37+
/**
38+
* Encapsulates the logic of creating indices.
39+
*
40+
* @author Michael Nitschinger
41+
* @author Aaron Whiteside
42+
*/
43+
public class CouchbasePersistentEntityIndexCreator implements InitializingBean, ApplicationListener<MappingContextEvent<?, ?>> {
3844

3945
private static final Logger LOGGER = LoggerFactory.getLogger(CouchbasePersistentEntityIndexCreator.class);
4046

4147
private final Map<Class<?>, Boolean> classesSeen = new ConcurrentHashMap<>();
4248
private final CouchbaseMappingContext mappingContext;
4349
private final QueryIndexResolver indexResolver;
44-
private final CouchbaseOperations couchbaseOperations;
50+
private final CouchbaseClientFactory clientFactory;
51+
private final boolean enabled;
4552

4653
public CouchbasePersistentEntityIndexCreator(final CouchbaseMappingContext mappingContext,
47-
final CouchbaseOperations operations) {
54+
final CouchbaseClientFactory clientFactory, final String typeKey, final boolean enabled) {
4855
this.mappingContext = mappingContext;
49-
this.couchbaseOperations = operations;
50-
this.indexResolver = QueryIndexResolver.create(mappingContext, operations);
56+
this.clientFactory = clientFactory;
57+
this.enabled = enabled;
58+
this.indexResolver = QueryIndexResolver.create(mappingContext, typeKey);
59+
}
60+
61+
@Override
62+
public void afterPropertiesSet() throws Exception {
63+
if (enabled) {
64+
mappingContext.getPersistentEntities().forEach(this::checkForIndexes);
65+
} else {
66+
LOGGER.debug("Automatic index creation not enabled.");
67+
}
5168
}
5269

5370
@Override
5471
public void onApplicationEvent(final MappingContextEvent<?, ?> event) {
55-
if (!event.wasEmittedBy(mappingContext)) {
72+
if (!enabled || !event.wasEmittedBy(mappingContext)) {
5673
return;
5774
}
5875

@@ -71,7 +88,7 @@ private void checkForIndexes(final CouchbasePersistentEntity<?> entity) {
7188
this.classesSeen.put(type, Boolean.TRUE);
7289

7390
if (LOGGER.isDebugEnabled()) {
74-
LOGGER.debug("Analyzing class " + type + " for index information.");
91+
LOGGER.debug("Analyzing class {} for index information.", type);
7592
}
7693

7794
checkForAndCreateIndexes(entity);
@@ -93,10 +110,10 @@ private void checkForAndCreateIndexes(final CouchbasePersistentEntity<?> entity)
93110
}
94111

95112
private void createIndex(final IndexDefinitionHolder indexToCreate) {
96-
Cluster cluster = couchbaseOperations.getCouchbaseClientFactory().getCluster();
113+
Cluster cluster = clientFactory.getCluster();
97114

98115
StringBuilder statement = new StringBuilder("CREATE INDEX ").append(indexToCreate.getIndexName()).append(" ON `")
99-
.append(couchbaseOperations.getBucketName()).append("` (")
116+
.append(clientFactory.getBucket().name()).append("` (")
100117
.append(String.join(",", indexToCreate.getIndexFields())).append(")");
101118

102119
if (indexToCreate.getIndexPredicate() != null && !indexToCreate.getIndexPredicate().isEmpty()) {
@@ -107,21 +124,10 @@ private void createIndex(final IndexDefinitionHolder indexToCreate) {
107124
cluster.query(statement.toString());
108125
} catch (IndexExistsException ex) {
109126
// ignored on purpose, rest is propagated
110-
LOGGER.debug("Index \"" + indexToCreate.getIndexName() + "\" already exists, ignoring.");
127+
LOGGER.debug("Index \"{}\" already exists, ignoring.", indexToCreate.getIndexName());
111128
} catch (Exception ex) {
112129
throw new DataIntegrityViolationException("Could not auto-create index with statement: " + statement.toString(),
113130
ex);
114131
}
115132
}
116-
117-
/**
118-
* Returns whether the current index creator was registered for the given {@link MappingContext}.
119-
*/
120-
public boolean isIndexCreatorFor(final MappingContext<?, ?> context) {
121-
return this.mappingContext.equals(context);
122-
}
123-
124-
public boolean hasSeen(CouchbasePersistentEntity<?> entity) {
125-
return classesSeen.containsKey(entity.getType());
126-
}
127133
}

src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexResolver.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.util.List;
2121
import java.util.stream.Collectors;
2222

23-
import org.springframework.data.couchbase.core.CouchbaseOperations;
2423
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
2524
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
2625
import org.springframework.data.couchbase.core.mapping.Document;
@@ -35,13 +34,13 @@
3534
public class CouchbasePersistentEntityIndexResolver implements QueryIndexResolver {
3635

3736
private final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
38-
private final CouchbaseOperations operations;
37+
private final String typeKey;
3938

4039
public CouchbasePersistentEntityIndexResolver(
4140
final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext,
42-
CouchbaseOperations operations) {
41+
final String typeKey) {
4342
this.mappingContext = mappingContext;
44-
this.operations = operations;
43+
this.typeKey = typeKey;
4544
}
4645

4746
@Override
@@ -135,7 +134,6 @@ protected List<IndexDefinitionHolder> createCompositeQueryIndexDefinitions(final
135134
}
136135

137136
private String getPredicate(final MappingCouchbaseEntityInformation<?, Object> entityInfo) {
138-
String typeKey = operations.getConverter().getTypeKey();
139137
String typeValue = entityInfo.getJavaType().getName();
140138
return "`" + typeKey + "` = \"" + typeValue + "\"";
141139
}

src/main/java/org/springframework/data/couchbase/core/index/QueryIndexResolver.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
*/
1616
package org.springframework.data.couchbase.core.index;
1717

18-
import org.springframework.data.couchbase.core.CouchbaseOperations;
1918
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
2019
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
2120
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
@@ -43,9 +42,9 @@ public interface QueryIndexResolver {
4342
*/
4443
static QueryIndexResolver create(
4544
MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext,
46-
CouchbaseOperations operations) {
45+
String typeKey) {
4746
Assert.notNull(mappingContext, "CouchbaseMappingContext must not be null!");
48-
return new CouchbasePersistentEntityIndexResolver(mappingContext, operations);
47+
return new CouchbasePersistentEntityIndexResolver(mappingContext, typeKey);
4948
}
5049

5150
/**

src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java

Lines changed: 2 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,10 @@
1616

1717
package org.springframework.data.couchbase.core.mapping;
1818

19-
import java.util.Optional;
20-
2119
import org.springframework.beans.BeansException;
2220
import org.springframework.context.ApplicationContext;
2321
import org.springframework.context.ApplicationContextAware;
24-
import org.springframework.context.ApplicationEventPublisher;
25-
import org.springframework.data.couchbase.core.index.CouchbasePersistentEntityIndexCreator;
2622
import org.springframework.data.mapping.context.AbstractMappingContext;
27-
import org.springframework.data.mapping.context.MappingContextEvent;
2823
import org.springframework.data.mapping.model.FieldNamingStrategy;
2924
import org.springframework.data.mapping.model.Property;
3025
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
@@ -37,6 +32,7 @@
3732
*
3833
* @author Michael Nitschinger
3934
* @author Michael Reiche
35+
* @author Aaron Whiteside
4036
*/
4137
public class CouchbaseMappingContext
4238
extends AbstractMappingContext<BasicCouchbasePersistentEntity<?>, CouchbasePersistentProperty>
@@ -55,10 +51,6 @@ public class CouchbaseMappingContext
5551
*/
5652
private FieldNamingStrategy fieldNamingStrategy = DEFAULT_NAMING_STRATEGY;
5753

58-
private boolean autoIndexCreation = true;
59-
private ApplicationEventPublisher eventPublisher;
60-
private CouchbasePersistentEntityIndexCreator indexCreator = null;
61-
6254
/**
6355
* Configures the {@link FieldNamingStrategy} to be used to determine the field name if no manual mapping is applied.
6456
* Defaults to a strategy using the plain property name.
@@ -108,65 +100,8 @@ protected CouchbasePersistentProperty createPersistentProperty(Property property
108100
*/
109101
@Override
110102
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
111-
context = applicationContext;
112103
super.setApplicationContext(applicationContext);
104+
context = applicationContext;
113105
}
114106

115-
@Override
116-
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
117-
eventPublisher = applicationEventPublisher;
118-
if (this.eventPublisher == null) {
119-
this.eventPublisher = context;
120-
}
121-
}
122-
123-
public boolean isAutoIndexCreation() {
124-
return autoIndexCreation;
125-
}
126-
127-
public void setAutoIndexCreation(boolean autoCreateIndexes) {
128-
this.autoIndexCreation = autoCreateIndexes;
129-
}
130-
131-
/**
132-
* override method from AbstractMappingContext as that method will not publishEvent() if it finds the entity has
133-
* already been cached
134-
*
135-
* @param typeInformation - entity type
136-
*/
137-
@Override
138-
protected Optional<BasicCouchbasePersistentEntity<?>> addPersistentEntity(TypeInformation<?> typeInformation) {
139-
Optional<BasicCouchbasePersistentEntity<?>> entity = super.addPersistentEntity(typeInformation);
140-
141-
if (this.eventPublisher != null && entity.isPresent()) {
142-
if (this.indexCreator != null) {
143-
if (!indexCreator.hasSeen(entity.get())) {
144-
this.eventPublisher.publishEvent(new MappingContextEvent(this, entity.get()));
145-
}
146-
}
147-
}
148-
return entity;
149-
}
150-
151-
/**
152-
* override method from AbstractMappingContext as that method will not publishEvent() if it finds the entity has
153-
* already been cached. Instead, user our own addPersistEntity that will.
154-
*
155-
* @param typeInformation - entity type
156-
*/
157-
@Override
158-
public BasicCouchbasePersistentEntity<?> getPersistentEntity(TypeInformation<?> typeInformation) {
159-
Optional<BasicCouchbasePersistentEntity<?>> entity = addPersistentEntity(typeInformation);
160-
return entity.isPresent() ? entity.get() : null;
161-
}
162-
163-
/**
164-
* capture the indexCreator when it has been added as a listener. only publishEvent() if the indexCreator hasn't
165-
* already seen the class.
166-
*
167-
* @param indexCreator
168-
*/
169-
public void setIndexCreator(CouchbasePersistentEntityIndexCreator indexCreator) {
170-
this.indexCreator = indexCreator;
171-
}
172107
}

0 commit comments

Comments
 (0)