Skip to content

Commit be75e74

Browse files
committed
Reinstate the getDefaultConsistency() method in the Configuration.
Closes #1243.
1 parent 37648c8 commit be75e74

File tree

9 files changed

+186
-13
lines changed

9 files changed

+186
-13
lines changed

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.HashSet;
2323
import java.util.Set;
2424

25+
import com.couchbase.client.java.query.QueryScanConsistency;
2526
import org.springframework.beans.factory.config.BeanDefinition;
2627
import org.springframework.context.annotation.Bean;
2728
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
@@ -156,7 +157,8 @@ protected void configureEnvironment(final ClusterEnvironment.Builder builder) {
156157
@Bean(name = BeanNames.COUCHBASE_TEMPLATE)
157158
public CouchbaseTemplate couchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory,
158159
MappingCouchbaseConverter mappingCouchbaseConverter, TranslationService couchbaseTranslationService) {
159-
return new CouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter, couchbaseTranslationService);
160+
return new CouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter, couchbaseTranslationService,
161+
getDefaultConsistency());
160162
}
161163

162164
public CouchbaseTemplate couchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory,
@@ -167,8 +169,8 @@ public CouchbaseTemplate couchbaseTemplate(CouchbaseClientFactory couchbaseClien
167169
@Bean(name = BeanNames.REACTIVE_COUCHBASE_TEMPLATE)
168170
public ReactiveCouchbaseTemplate reactiveCouchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory,
169171
MappingCouchbaseConverter mappingCouchbaseConverter, TranslationService couchbaseTranslationService) {
170-
return new ReactiveCouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter,
171-
couchbaseTranslationService);
172+
return new ReactiveCouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter, couchbaseTranslationService,
173+
getDefaultConsistency());
172174
}
173175

174176
public ReactiveCouchbaseTemplate reactiveCouchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory,
@@ -376,4 +378,8 @@ private boolean nonShadowedJacksonPresent() {
376378
}
377379
}
378380

381+
public QueryScanConsistency getDefaultConsistency() {
382+
return null;
383+
}
384+
379385
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import org.springframework.data.couchbase.CouchbaseClientFactory;
2020
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
2121

22+
import com.couchbase.client.java.query.QueryScanConsistency;
23+
2224
/**
2325
* Defines common operations on the Couchbase data source, most commonly implemented by {@link CouchbaseTemplate}.
2426
*/
@@ -44,4 +46,8 @@ public interface CouchbaseOperations extends FluentCouchbaseOperations {
4446
*/
4547
CouchbaseClientFactory getCouchbaseClientFactory();
4648

49+
/**
50+
* Returns the default consistency to use for queries
51+
*/
52+
QueryScanConsistency getConsistency();
4753
}

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.springframework.lang.Nullable;
3333

3434
import com.couchbase.client.java.Collection;
35+
import com.couchbase.client.java.query.QueryScanConsistency;
3536

3637
/**
3738
* Implements lower-level couchbase operations on top of the SDK with entity mapping capabilities.
@@ -49,18 +50,25 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationContex
4950
private final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
5051
private final ReactiveCouchbaseTemplate reactiveCouchbaseTemplate;
5152
private @Nullable CouchbasePersistentEntityIndexCreator indexCreator;
53+
private QueryScanConsistency scanConsistency;
5254

5355
public CouchbaseTemplate(final CouchbaseClientFactory clientFactory, final CouchbaseConverter converter) {
5456
this(clientFactory, converter, new JacksonTranslationService());
5557
}
5658

5759
public CouchbaseTemplate(final CouchbaseClientFactory clientFactory, final CouchbaseConverter converter,
5860
final TranslationService translationService) {
61+
this(clientFactory, converter, translationService, null);
62+
}
63+
64+
public CouchbaseTemplate(final CouchbaseClientFactory clientFactory, final CouchbaseConverter converter,
65+
final TranslationService translationService, QueryScanConsistency scanConsistency) {
5966
this.clientFactory = clientFactory;
6067
this.converter = converter;
6168
this.templateSupport = new CouchbaseTemplateSupport(this, converter, translationService);
62-
this.reactiveCouchbaseTemplate = new ReactiveCouchbaseTemplate(clientFactory, converter, translationService);
63-
69+
this.reactiveCouchbaseTemplate = new ReactiveCouchbaseTemplate(clientFactory, converter, translationService,
70+
scanConsistency);
71+
this.scanConsistency = scanConsistency;
6472
this.mappingContext = this.converter.getMappingContext();
6573
if (mappingContext instanceof CouchbaseMappingContext) {
6674
CouchbaseMappingContext cmc = (CouchbaseMappingContext) mappingContext;
@@ -147,6 +155,11 @@ public CouchbaseClientFactory getCouchbaseClientFactory() {
147155
return clientFactory;
148156
}
149157

158+
@Override
159+
public QueryScanConsistency getConsistency() {
160+
return scanConsistency;
161+
}
162+
150163
/**
151164
* Provides access to a {@link Collection} on the configured {@link CouchbaseClientFactory}.
152165
*

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717

1818
import org.springframework.data.couchbase.CouchbaseClientFactory;
1919
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
20+
import org.springframework.data.couchbase.core.support.PseudoArgs;
21+
22+
import com.couchbase.client.java.query.QueryScanConsistency;
2023

2124
/**
2225
* Defines common operations on the Couchbase data source, most commonly implemented by
@@ -47,4 +50,8 @@ public interface ReactiveCouchbaseOperations extends ReactiveFluentCouchbaseOper
4750
*/
4851
CouchbaseClientFactory getCouchbaseClientFactory();
4952

53+
/**
54+
* @return the default consistency to use for queries
55+
*/
56+
QueryScanConsistency getConsistency();
5057
}

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.data.couchbase.core.support.PseudoArgs;
2929

3030
import com.couchbase.client.java.Collection;
31+
import com.couchbase.client.java.query.QueryScanConsistency;
3132

3233
/**
3334
* template class for Reactive Couchbase operations
@@ -43,18 +44,25 @@ public class ReactiveCouchbaseTemplate implements ReactiveCouchbaseOperations, A
4344
private final CouchbaseConverter converter;
4445
private final PersistenceExceptionTranslator exceptionTranslator;
4546
private final ReactiveCouchbaseTemplateSupport templateSupport;
46-
private ThreadLocal<PseudoArgs<?>> threadLocalArgs = null;
47+
private ThreadLocal<PseudoArgs<?>> threadLocalArgs = new ThreadLocal<>();
48+
private QueryScanConsistency scanConsistency;
4749

4850
public ReactiveCouchbaseTemplate(final CouchbaseClientFactory clientFactory, final CouchbaseConverter converter) {
4951
this(clientFactory, converter, new JacksonTranslationService());
5052
}
5153

5254
public ReactiveCouchbaseTemplate(final CouchbaseClientFactory clientFactory, final CouchbaseConverter converter,
5355
final TranslationService translationService) {
56+
this(clientFactory, converter, translationService, null);
57+
}
58+
59+
public ReactiveCouchbaseTemplate(final CouchbaseClientFactory clientFactory, final CouchbaseConverter converter,
60+
final TranslationService translationService, QueryScanConsistency scanConsistency) {
5461
this.clientFactory = clientFactory;
5562
this.converter = converter;
5663
this.exceptionTranslator = clientFactory.getExceptionTranslator();
5764
this.templateSupport = new ReactiveCouchbaseTemplateSupport(this, converter, translationService);
65+
this.scanConsistency = scanConsistency;
5866
}
5967

6068
@Override
@@ -182,4 +190,12 @@ public void setPseudoArgs(PseudoArgs<?> threadLocalArgs) {
182190
this.threadLocalArgs.set(threadLocalArgs);
183191
}
184192

193+
/**
194+
* {@inheritDoc}
195+
*/
196+
@Override
197+
public QueryScanConsistency getConsistency() {
198+
return scanConsistency;
199+
}
200+
185201
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,10 @@ public Flux<T> all() {
206206
}));
207207
}
208208

209-
private QueryOptions buildOptions(QueryOptions options) {
210-
QueryOptions opts = query.buildQueryOptions(options, scanConsistency);
209+
@Override
210+
public QueryOptions buildOptions(QueryOptions options) {
211+
QueryScanConsistency qsc = scanConsistency != null ? scanConsistency : template.getConsistency();
212+
QueryOptions opts = query.buildQueryOptions(options, qsc);
211213
return opts;
212214
}
213215

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ public Flux<RemoveResult> all() {
9090
}
9191

9292
private QueryOptions buildQueryOptions(QueryOptions options) {
93-
return query.buildQueryOptions(options, scanConsistency);
93+
QueryScanConsistency qsc = scanConsistency != null ? scanConsistency : template.getConsistency();
94+
return query.buildQueryOptions(options, qsc);
9495
}
9596

9697
@Override

src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@
6060
// @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
6161
public interface AirportRepository extends CouchbaseRepository<Airport, String>, DynamicProxyable<AirportRepository> {
6262

63-
// override an annotate with REQUEST_PLUS
63+
// NOT_BOUNDED to test ScanConsistency
64+
// @ScanConsistency(query = QueryScanConsistency.NOT_BOUNDED)
65+
Airport iata(String iata);
66+
6467
@Override
6568
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
6669
List<Airport> findAll();

src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java

Lines changed: 122 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.data.couchbase.repository;
1818

19+
import static com.couchbase.client.java.query.QueryScanConsistency.NOT_BOUNDED;
20+
import static com.couchbase.client.java.query.QueryScanConsistency.REQUEST_PLUS;
1921
import static java.util.Arrays.asList;
2022
import static org.assertj.core.api.Assertions.assertThat;
2123
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -25,6 +27,7 @@
2527
import static org.junit.jupiter.api.Assertions.assertNull;
2628
import static org.junit.jupiter.api.Assertions.assertThrows;
2729
import static org.junit.jupiter.api.Assertions.assertTrue;
30+
import static org.springframework.data.couchbase.config.BeanNames.COUCHBASE_TEMPLATE;
2831

2932
import junit.framework.AssertionFailedError;
3033

@@ -46,6 +49,8 @@
4649

4750
import org.junit.jupiter.api.Test;
4851
import org.springframework.beans.factory.annotation.Autowired;
52+
import org.springframework.context.ApplicationContext;
53+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
4954
import org.springframework.context.annotation.Bean;
5055
import org.springframework.context.annotation.Configuration;
5156
import org.springframework.dao.DataIntegrityViolationException;
@@ -56,7 +61,6 @@
5661
import org.springframework.data.couchbase.core.CouchbaseQueryExecutionException;
5762
import org.springframework.data.couchbase.core.CouchbaseTemplate;
5863
import org.springframework.data.couchbase.core.RemoveResult;
59-
import org.springframework.data.couchbase.core.mapping.event.ValidatingCouchbaseEventListener;
6064
import org.springframework.data.couchbase.core.query.N1QLExpression;
6165
import org.springframework.data.couchbase.core.query.QueryCriteria;
6266
import org.springframework.data.couchbase.domain.Address;
@@ -267,6 +271,78 @@ void findBySimplePropertyReturnType() {
267271
}
268272
}
269273

274+
@Test
275+
public void saveNotBounded() {
276+
// save() followed by query with NOT_BOUNDED will result in not finding the document
277+
Airport vie = new Airport("airports::vie", "vie", "low9");
278+
Airport airport2 = null;
279+
for (int i = 1; i <= 100; i++) {
280+
// set version == 0 so save() will be an upsert, not a replace
281+
Airport saved = airportRepository.save(vie.clearVersion());
282+
try {
283+
airport2 = airportRepository.iata(saved.getIata());
284+
if (airport2 == null) {
285+
break;
286+
}
287+
} catch (DataRetrievalFailureException drfe) {
288+
airport2 = null; //
289+
} finally {
290+
// airportRepository.delete(vie);
291+
// instead of delete, use removeResult to test QueryOptions.consistentWith()
292+
RemoveResult removeResult = couchbaseTemplate.removeById().one(vie.getId());
293+
assertEquals(vie.getId(), removeResult.getId());
294+
assertTrue(removeResult.getCas() != 0);
295+
assertTrue(removeResult.getMutationToken().isPresent());
296+
Airport airport3 = airportRepository.iata(vie.getIata());
297+
assertNull(airport3, "should have been removed");
298+
}
299+
}
300+
assertNull(airport2, "airport2 should have likely been null at least once");
301+
Airport saved = airportRepository.save(vie.clearVersion());
302+
couchbaseTemplate.findByQuery(Airport.class).withConsistency(REQUEST_PLUS).all();
303+
airport2 = airportRepository.iata(vie.getIata());
304+
RemoveResult removeResult = couchbaseTemplate.removeById().one(saved.getId());
305+
assertNotNull(airport2, "airport2 should have been found");
306+
}
307+
308+
@Test
309+
public void saveNotBoundedRequestPlus() {
310+
ApplicationContext ac = new AnnotationConfigApplicationContext(ConfigRequestPlus.class);
311+
// the Config class has been modified, these need to be loaded again
312+
CouchbaseTemplate couchbaseTemplateRP = (CouchbaseTemplate) ac.getBean(COUCHBASE_TEMPLATE);
313+
AirportRepository airportRepositoryRP = (AirportRepository) ac.getBean("airportRepository");
314+
315+
// save() followed by query with NOT_BOUNDED will result in not finding the document
316+
Airport vie = new Airport("airports::vie", "vie", "low9");
317+
Airport airport2 = null;
318+
for (int i = 1; i <= 100; i++) {
319+
// set version == 0 so save() will be an upsert, not a replace
320+
Airport saved = airportRepositoryRP.save(vie.clearVersion());
321+
try {
322+
airport2 = airportRepositoryRP.iata(saved.getIata());
323+
if (airport2 == null) {
324+
break;
325+
}
326+
} catch (DataRetrievalFailureException drfe) {
327+
airport2 = null; //
328+
} finally {
329+
// airportRepository.delete(vie);
330+
// instead of delete, use removeResult to test QueryOptions.consistentWith()
331+
RemoveResult removeResult = couchbaseTemplateRP.removeById().one(vie.getId());
332+
assertEquals(vie.getId(), removeResult.getId());
333+
assertTrue(removeResult.getCas() != 0);
334+
assertTrue(removeResult.getMutationToken().isPresent());
335+
Airport airport3 = airportRepositoryRP.iata(vie.getIata());
336+
assertNull(airport3, "should have been removed");
337+
}
338+
}
339+
assertNotNull(airport2, "airport2 should have never been null");
340+
Airport saved = airportRepositoryRP.save(vie.clearVersion());
341+
List<Airport> airports = couchbaseTemplateRP.findByQuery(Airport.class).withConsistency(NOT_BOUNDED).all();
342+
RemoveResult removeResult = couchbaseTemplateRP.removeById().one(saved.getId());
343+
assertFalse(!airports.isEmpty(), "airports should have been empty");
344+
}
345+
270346
@Test
271347
void findByTypeAlias() {
272348
Airport vie = null;
@@ -474,7 +550,7 @@ void count() {
474550
airportRepository.saveAll(
475551
Arrays.stream(iatas).map((iata) -> new Airport("airports::" + iata, iata, iata.toLowerCase(Locale.ROOT)))
476552
.collect(Collectors.toSet()));
477-
couchbaseTemplate.findByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
553+
couchbaseTemplate.findByQuery(Airport.class).withConsistency(REQUEST_PLUS).all();
478554
Long count = airportRepository.countFancyExpression(asList("JFK"), asList("jfk"), false);
479555
assertEquals(1, count);
480556

@@ -677,7 +753,7 @@ void deleteAllById() {
677753
void couchbaseRepositoryQuery() throws Exception {
678754
User user = new User("1", "Dave", "Wilson");
679755
userRepository.save(user);
680-
couchbaseTemplate.findByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
756+
couchbaseTemplate.findByQuery(User.class).withConsistency(REQUEST_PLUS)
681757
.matching(QueryCriteria.where("firstname").is("Dave").and("`1`").is("`1`")).all();
682758
String input = "findByFirstname";
683759
Method method = UserRepository.class.getMethod(input, String.class);
@@ -809,4 +885,47 @@ public ValidatingCouchbaseEventListener validationEventListener() {
809885
return new ValidatingCouchbaseEventListener(validator());
810886
}
811887
}
888+
889+
}
890+
891+
@Configuration
892+
@EnableCouchbaseRepositories("org.springframework.data.couchbase")
893+
@EnableCouchbaseAuditing(auditorAwareRef = "auditorAwareRef", dateTimeProviderRef = "dateTimeProviderRef")
894+
static class ConfigRequestPlus extends AbstractCouchbaseConfiguration {
895+
896+
@Override
897+
public String getConnectionString() {
898+
return connectionString();
899+
}
900+
901+
@Override
902+
public String getUserName() {
903+
return config().adminUsername();
904+
}
905+
906+
@Override
907+
public String getPassword() {
908+
return config().adminPassword();
909+
}
910+
911+
@Override
912+
public String getBucketName() {
913+
return bucketName();
914+
}
915+
916+
@Bean(name = "auditorAwareRef")
917+
public NaiveAuditorAware testAuditorAware() {
918+
return new NaiveAuditorAware();
919+
}
920+
921+
@Bean(name = "dateTimeProviderRef")
922+
public DateTimeProvider testDateTimeProvider() {
923+
return new AuditingDateTimeProvider();
924+
}
925+
926+
@Override
927+
public QueryScanConsistency getDefaultConsistency() {
928+
return REQUEST_PLUS;
929+
}
930+
}
812931
}

0 commit comments

Comments
 (0)