Skip to content

Commit edadede

Browse files
committed
Add support for Couchbase Transactions.
The fluent operations are common for options that are common to operations with and without transactions. Once there is a transaction(ctx), or an option specific to without-transactions (such as scanConsistency), the interfaces are bifurcated, so that an non-transaction option cannot be applied to a transaction operation, and a transaction(ctx) cannot be applied where a non-transaction option has already been applied. Closes #1145. Support for @transactional for blocking and reactive Transactionmanager. Closes 1145. Transaction Support. Transaction Support. Porting to SDK-integrated version of transactions The transactions logic exists in the Java SDK as of 3.3.0, with a slightly different API. This is the first effort at the port, which literally just compiles. It will not run as crucial code has been commented and todo-ed. There is work remaining to figure out how to complete the port, as some crucial parts (such as ctx.commit() and ctx.rollback()) have been intentionally removed. Continuing work to get the ExtSDKIntegration port working Trying to transition to CallbackPreferring manager. Added CouchbaseSimpleCallbackTransactionManager, the simplest possible implementation of CallbackPreferringTransactionManager, combined with a simpler approach to ThreadLocal storage in ReactiveInsertByIdSupport. Test 'commitShouldPersistTxEntriesOfTxAnnotatedMethod' is now passing. Adding WIP get-and-replace @transactional support (Not yet working as CAS/version field in Person is not populated correctly.) Datacouch 1145 transaction support (#1423) * Porting to SDK-integrated version of transactions The transactions logic exists in the Java SDK as of 3.3.0, with a slightly different API. This is the first effort at the port, which literally just compiles. It will not run as crucial code has been commented and todo-ed. There is work remaining to figure out how to complete the port, as some crucial parts (such as ctx.commit() and ctx.rollback()) have been intentionally removed. * Continuing work to get the ExtSDKIntegration port working Trying to transition to CallbackPreferring manager. * Added CouchbaseSimpleCallbackTransactionManager, the simplest possible implementation of CallbackPreferringTransactionManager, combined with a simpler approach to ThreadLocal storage in ReactiveInsertByIdSupport. Test 'commitShouldPersistTxEntriesOfTxAnnotatedMethod' is now passing. * Adding WIP get-and-replace @transactional support (Not yet working as CAS/version field in Person is not populated correctly.) Commit before pulling Graham's changes. Merge branch 'datacouch_1145_transaction_support' of github.com:spring-projects/spring-data-couchbase into datacouch_1145_transaction_support Transitioning to use CoreTransactionAttemptContext. Tests may fail. Removing AttemptContextReactiveAccessor Don't think we need this, as we can pass around CoreTransactionAttemptContext instead, which gives access to a lot of internals. Removing TransactionsReactive Would prefer not to C&P a huge class out of the transaction internals, and don't think we need it. Removing some files not currently used To reduce & simplify the amount of code to look at. Some don't seem to be used in any branch, some just aren't used in this branch. Removing CouchbaseTransactionInterceptor As per offline discussion, CallbackPreferringPlatformTransactionManager is perhaps the optimal solution. Copying @transactional tests out into separate class Tidyup Tidyup test names Verify GenericSupport is on same thread before and after transactional operation Refactoring CouchbaseSimpleCallbackTransactionManager ThreadLocalStorage management Using latest java-client ReactiveReplaceByIdSupport - Fixing use of CAS now have CoreTransactionAttemptContext. Removing unused code. ReactiveInsertByIdSupport - fixing use of reactive vs non-reactive, and CAS Merging upstream Remove incorrect thread check (.doOnNext could execute on a different thread) Get scope and collection from pseudoArgs and some cleanup. Completing merge from upstream Removing unused classes Give GenericSupport a better name Reject at runtime options that aren't supported in a transaction Fixing some small todos, partly by removing unused coe Fix runtime option checks Simplifying CouchbaseSimpleCallbackTransactionManager ThreadLocalStorage Standardising on ReactiveCouchbaseResourceHolder rather than holding CoreTransactionAttemptContext too Removing version from CouchbaseDocument Can't recall why I added this, and tests pass without it Improving CouchbaseTransactionalIntegrationTests and adding more tests Reject operations that aren't allowed in a transaction (upsertById etc.) Improve handling of CAS mismatch By calling CoreTransactionAttemptContext.operationFailed, it ensures that internal state is set. So even if the user catches the exception, the transaction still behaves as it should. Removing a now-redundant non-transactional check on upsertById I missed this when adding TransactionalSupport.verifyNotInTransaction here. Support @transactional options timeout and isolation level Add ReactiveTransactionWrapper/TransactionWrapper and a bunch of cleanup. Merge branch 'datacouch_1145_transaction_support' of https://github.com/programmatix/spring-data-couchbase into programmatix-datacouch_1145_transaction_support Fixed up merge issues. Datacouch 1145 transaction support (#1447) * Move CouchbaseTransactionalOperator to use SLF4J, same as rest of the code. * Handle all propagation levels * Adding new tests for repository calls inside @transactional One test is failure due to what looks like a bug elsewhere. * Rename CouchbaseTransactionalIntegrationTests, and check after each test that we're not in a transaction. Remove unnecessary methods From ReactiveCouchbaseClientFactory. Also rationalized naming of methods and other changes. Cleanup of test classes. Datacouch 1145 transaction support (#1448) * Move CouchbaseTransactionalOperator to use SLF4J, same as rest of the code. * Handle all propagation levels * Adding new tests for repository calls inside @transactional One test is failure due to what looks like a bug elsewhere. * Rename CouchbaseTransactionalIntegrationTests, and check after each test that we're not in a transaction. * Remove unnecessary methods From ReactiveCouchbaseClientFactory. Also rationalized naming of methods and other changes. * Cleanup of test classes. * Removing unused classes (Reducing the cognitive burden) * Removing version from CouchbaseDocument This change was done previously - it must have slipped back in a merge. * Adding and removing TODOs * Adding and removing TODOs * DRYing CouchbaseTransactionalPropagationIntegrationTests * Check propagation tests retry as expected * Tidy up PersonWithoutVersion to the minimum required * Removing unused code This should all be non-destructive. Just removing code IntelliJ declares unused. Intent is to make it easier to figure out how the CoreTransactionAttemptContext TLS is working. * Adding tests for @transactional removeByQuery and findByQuery Failing as they aren't being executed transactionally - investigating why. Co-authored-by: Michael Reiche <[email protected]> change references to resource holder change refs to resource holder Merge branch 'programmatix-datacouch_1145_transaction_support' of github.com:spring-projects/spring-data-couchbase into programmatix-datacouch_1145_transaction_support Removing core transaction attempt context bound couchbase client factory rebased (#1449) * Move ReactiveTransactionsWrapper tests into a new file * (Temporarily?) disabling tests using CouchbaseTransactionOperation or TransactionalOperator As I feel we should be removing/not-supporting these, and on this branch I've broken them. * Make all transaction tests call assertNotInTransaction * Removing unused code * Instead of binding the transaction AttemptContext to a CouchbaseClientFactory, fetch it from ThreadLocalStorage (or the reactive context) instead. This allows a lot of simplifying: * The non-trivial ReactiveCouchbaseClientUtils can be removed * As can CoreTransactionAttemptContextBoundCouchbaseClientFactory Also removing TransactionalSupport.one as it wasn't providing as much DRY utility as I thought it would - only used in two places. This change won't compile on its own. To reduce the complexity of this patchset, the Reactive*OperationSupport changes will go into a separate commit. * Reactive*OperationSupport changes to support the previous commit. * Fixing ReactiveRemoveByQuerySupport. Both to support the changes to TransactionalSupport. And to fix the TODO where the query resuls were not being handled. * Disabling a test * Adding CouchbaseTransactionsWrapperTemplateIntegrationTests * Another advantage of removing CoreTransactionAttemptContextBoundCouchbaseClientFactory is we can remove Cluster and ClusterInterface. * Adding CouchbaseReactiveTransactionsWrapperTemplateIntegrationTests Some of these tests are currently failing - tracking down where the issue is. Merge branch 'programmatix-datacouch_1145_transaction_support' of github.com:spring-projects/spring-data-couchbase into programmatix-datacouch_1145_transaction_support manual merges for PR manual merges for PR Fix a bunch of test cases and remove unused bits. Removing CouchbaseTransactionInterceptor As discussed on Slack. Reenabling some tests that are passing (Unclear why these were disabled?) Verified that CallbackPreferringPlatformTransactionManager getTransaction/commit/rollback are never called Adding tests for TransactionTemplate, which works fine with CouchbaseSimpleCallbackTransactionManager Whether we actually document this support is another matter - it's yet another way of doing transactions. Small fixes to support TransactionTemplate * Handle if the user has set isRollbackOnly on the TransactionStatus (which is only available - I think - when using TransactionTemplate) * Supply a `transaction` object to CouchbaseTransactionStatus so that status.isNewTransaction() correctly returns true. (This method requires that transaction to be set.) Clarifying that direct use of PlatformTransactionManager is not supported Adding further TransactionTemplateIntegrationTests tests Fixing removeByQuery queryOptions creation Removing now-fixed TODO (no longer key off Cluster) Adding overload to CouchbaseSimpleCallbackTransactionManager to allow it to be constructed without a TransactionOptions. Adding CouchbaseSimpleTransactionalOperator, the simplest possible implementation of TransactionalOperator. Adding retry tests Just making sure that error handling and retries are done correctly throughout. Updating and adding some TODOs Have CouchbaseTransactionManager support CouchbaseResourceHolder.class binding Adding more tests for CouchbaseTransactionManager. These tests fail, and are known to fail. I'm adding them as a solid demonstration of why I don't feel we can have this CouchbaseTransactionManager: it doesn't provide the crucial 'core loop' functionality, including error handling and retries. We should standardise on CouchbaseSimpleCallbackTransactionManager instead. CouchbaseSimpleCallbackTransactionManager.executeNewReactiveTransaction now buffers results rather than trying to stream a Flux from out of a completed lambda (which I doubt is even possible.) Removing comment that has been resolved. (As per Slack, we will live with this limitation.) Adding CouchbaseSimpleTransactionInterceptor, a very simple TransactionInterceptor implemention that defers to CouchbaseSimpleCallbackTransactionManager if that is the provided TransactionManager, and otherwise just calls super. This allows reactive @transactional - though all @transactional methods including blocking will now flow through it. There are two rather divergent approaches in the code currently: 1. CouchbaseTransactionManager, ReactiveTransactionManager, CouchbaseTransactionalOperator, CouchbaseTransactionInterceptor 2. CouchbaseSimpleCallbackTransactionManager, CouchbaseSimpleTransactionalOperator, CouchbaseSimpleTransactionInterceptor I know the intent is to remove some aspects of (1), but until that's done it's proving tricky to have tests for both concurrently - I've hit several issues on adding CouchbaseSimpleTransactionInterceptor, with 'multiple transaction manager beans in config' being common. So, temporarily moving some beans from AbstractCouchbaseConfiguration into the test Config class, renaming it, and having two separately TransactionsConfig classes for the two approaches. Once we've aligned the approaches more, can move what beans survive back into AbstractCouchbaseConfiguration. Safety check in CouchbaseSimpleCallbackTransactionManager that the blocking run is not accidentally running a reactive @transactional somehow. Adding tests for reactive @transactional, which now works (including error handling and retries) as of the CouchbaseSimpleTransactionInterceptor. With TransactionsConfigCouchbaseSimpleTransactionManager change, can now simplify @transactional(transactionManager = ...) to just @transactional. Tidying TODOs I saw in a PR comment that getResources no longer uses TransactionOptions Removing configureTransactions from config As per PR discussion Removing some code that has now been refactored into 3.3.1 SDK Removing TODOs that are TODONE already Removing transactionsOptions() bean from AbstractCouchbaseConfiguration. As per comment, this feels unnecessary: any options you'd configure at this config level, you'd surely provide at the global (Cluster) level instead? Reinstating some commented-out code This looks pretty crucial - can't recall why I commented it in first place Removing TransactionResult This was from a now-abandoned idea of storing transactional metadata in the entity class. Fix recent removal of TransactionOptions bean Switch some IllegalStateException for more accurate UnsupportedOperationException Tidying tests to remove old GenericApplicationContext method Tidying some TODOs (todo gp == in code that I think we should remove) (todo gpx == needs looking at) Tidying TransactionsConfigCouchbaseSimpleTransactionManager Provide SpringTransactionAttemptContext and ReactiveSpringTransactionAttemptContext wrappers For use by TransactionsWrapper and reactive equivalent. Pro: it's an abstraction layer. It lets us add Spring-specific functionality, or hide functionality that for whatever reason doesn't work with Spring (which is why it's composition rather than inheritance, beyond that being a best practice anyway). Con: any new API added will also have to be added to these wrappers. But that's a small amount of work and API is added very infrequently. Cleanup tests mostly. Temporary fix for CouchbaseSimpleCallbackTransactionManager. Merge branch 'programmatix-datacouch_1145_transaction_support' of github.com:spring-projects/spring-data-couchbase into programmatix-datacouch_1145_transaction_support Tidying up test cases. Fixes to over-zealous manual merging. - keep CouchbaseTransactionManager as Graham is still using it. - fix tests that were expecting SimulateFailure to be nested. Add ReactiveTransactionWrapper and enable tests that use it. More tidying. Removed CouchbaseTransactionManager. Only wrap exceptions in CouchbaseSimpleTransactionInterceptor if they are not RuntimeExceptions. Removed CouchbaseTransactionalOperator and ReactiveCouchbaseTransactionalOperator. Fixed non-transactions regression introduced in previous commit. Removing the two transactions config classes Now we've landed on a single agreed approach there's no need for this separation any more, and any beans can be moved back into AbstractCouchbaseConfiguration. Rename CouchbaseSimpleTransactionInterceptor The "Simple" moniker is no longer useful since we now only have one of these. Rename CouchbaseSimpleCallbackTransactionManager The "Simple" moniker is no longer useful since we now only have one of these. Rename CouchbaseSimpleTransactionalOperator The "Simple" moniker is no longer useful since we now only have one of these. Change CouchbaseTransactionalOperator construction to static As per Slack discussion. Remove comments related to another database Removing a test comment that doesn't seem to apply anymore Test passes for me at least Simplify TransactionsWrapper and AttemptContextReactiveAccessor newCoreTranactionAttemptContext is reimplementing some code from core plus has some config bugs to resolve. I think it's simpler to remove it and replace TransactionsWrapper (the only code still using this method) with the simple code seen now. (Note this is similar to how I had it before 1701183 - not sure if that commit intentionally reverted things?) This change also petmits a lot of tidyup & simplification throughout the codebase. We can just create CouchbaseResourceHolders directly now. Tidying a test No longer needs retryWhen now using the new approaches Remove ReactiveCouchbaseClientFactory. It was added to support getting the transaction from the TransactionSynchronizationManager.forCurrent() and providing a template with a session containing the transaction. Fixing some code warnings Mostly removing unused code Mark internal classes @Stability.Internal Removing some tests that have already been previously moved into another file. Removing the transaction wrappers Requires JVMCBC-1105 and 3.3.2 Add more tests for native SDK transactions Move all tests related to native SDK transactions into their own package It makes it easier to test just that functionality while iterating. Removing CouchbaseTransactionManagerTransactionalTemplateIntegrationTests This test is now redundant. It was created to show why the original CouchbaseTransactionManager couldn't work (no retries). Now that has been replaced, this test is just duplicating others. Adding some minimal JavaDocs and comments. Tidying up after moving ThreadLocalStorage into SDK Deleting CouchbaseTemplateTransactionIntegrationTests As this relies on Spring test @transactional, which we do not support as that Spring logic is not aware of CallbackPreferringTransactionManager. Removed some redundant bean names Tidying up tests and comments Removing some now-unused reflection code Starting with mapping TransactionFailedException and TransactionCommitAmbiguousException, into new errors TransactionSystemUnambiguousException and TransactionSystemAmbiguousException. These will be raised from an @transactional transaction. E.g. to do error handling the user would do: ``` try { service.transactionalMethod(); } catch (TransactionSystemAmbiguousException ex) { // app-specific handling } catch (TransactionSystemUnambiguousException ex) { // app-specific handling } class Service { @transactional void transactionalMethod() { // ... } } ``` Mapping TransactionOperationFailedException, which is an opaque signal raised from transaction operations, to new exception UncategorizedTransactionDataAccessException. This depends on some new functionality added into Java SDK 3.3.2, WrappedTransactionOperationFailedException. Minor tidyuo Improving tests for correct operation-level errors
1 parent b02959d commit edadede

File tree

136 files changed

+8634
-1287
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

136 files changed

+8634
-1287
lines changed

.mvn/wrapper/maven-wrapper.jar

-47.2 KB
Binary file not shown.

pom.xml

+15-6
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
</parent>
1919

2020
<properties>
21-
<couchbase>3.3.0</couchbase>
22-
<couchbase.osgi>3.3.0</couchbase.osgi>
21+
<couchbase>3.3.2-SNAPSHOT</couchbase>
22+
<couchbase.osgi>3.3.1</couchbase.osgi>
2323
<springdata.commons>3.0.0-SNAPSHOT</springdata.commons>
2424
<java-module-name>spring.data.couchbase</java-module-name>
2525
<jodatime>2.10.13</jodatime>
@@ -233,6 +233,18 @@
233233
<version>4.0.3</version>
234234
<scope>test</scope>
235235
</dependency>
236+
<dependency>
237+
<groupId>org.testcontainers</groupId>
238+
<artifactId>testcontainers</artifactId>
239+
</dependency>
240+
241+
<dependency>
242+
<groupId>ch.qos.logback</groupId>
243+
<artifactId>logback-classic</artifactId>
244+
<version>1.2.5</version>
245+
<scope>compile</scope>
246+
</dependency>
247+
236248

237249
</dependencies>
238250

@@ -251,10 +263,6 @@
251263
<enabled>false</enabled>
252264
</releases>
253265
</repository>
254-
<repository>
255-
<id>jitpack.io</id>
256-
<url>https://jitpack.io</url>
257-
</repository>
258266
</repositories>
259267

260268
<pluginRepositories>
@@ -286,6 +294,7 @@
286294
<groupId>org.apache.maven.plugins</groupId>
287295
<artifactId>maven-failsafe-plugin</artifactId>
288296
<configuration>
297+
<useModulePath>false</useModulePath>
289298
<includes>
290299
<include>**/*IntegrationTest.java</include>
291300
<include>**/*IntegrationTests.java</include>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
/*
3+
* Copyright 2021 the original author or authors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package com.couchbase.client.java.transactions;
18+
19+
import java.lang.reflect.Field;
20+
21+
import com.couchbase.client.core.annotation.Stability;
22+
import com.couchbase.client.core.transaction.CoreTransactionAttemptContext;
23+
import com.couchbase.client.java.codec.JsonSerializer;
24+
25+
/**
26+
* To access the ReactiveTransactionAttemptContext held by TransactionAttemptContext
27+
*
28+
* @author Michael Reiche
29+
*/
30+
@Stability.Internal
31+
public class AttemptContextReactiveAccessor {
32+
public static ReactiveTransactionAttemptContext createReactiveTransactionAttemptContext(
33+
CoreTransactionAttemptContext core, JsonSerializer jsonSerializer) {
34+
return new ReactiveTransactionAttemptContext(core, jsonSerializer);
35+
}
36+
}

src/main/java/org/springframework/data/couchbase/CouchbaseClientFactory.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818

1919
import java.io.Closeable;
2020

21+
import com.couchbase.client.core.transaction.CoreTransactionAttemptContext;
22+
import com.couchbase.client.java.transactions.ReactiveTransactionAttemptContext;
23+
import com.couchbase.client.java.transactions.config.TransactionOptions;
2124
import org.springframework.dao.support.PersistenceExceptionTranslator;
2225

2326
import com.couchbase.client.java.Bucket;
@@ -72,5 +75,4 @@ public interface CouchbaseClientFactory extends Closeable {
7275
* The exception translator used on the factory.
7376
*/
7477
PersistenceExceptionTranslator getExceptionTranslator();
75-
7678
}

src/main/java/org/springframework/data/couchbase/SimpleCouchbaseClientFactory.java

+18-6
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@
1515
*/
1616
package org.springframework.data.couchbase;
1717

18+
import java.time.Duration;
19+
import java.time.temporal.ChronoUnit;
1820
import java.util.function.Supplier;
1921

22+
import com.couchbase.client.core.transaction.CoreTransactionAttemptContext;
23+
import com.couchbase.client.java.transactions.config.TransactionOptions;
2024
import org.springframework.dao.support.PersistenceExceptionTranslator;
2125
import org.springframework.data.couchbase.core.CouchbaseExceptionTranslator;
2226

@@ -28,7 +32,11 @@
2832
import com.couchbase.client.java.ClusterOptions;
2933
import com.couchbase.client.java.Collection;
3034
import com.couchbase.client.java.Scope;
35+
import com.couchbase.client.java.codec.JsonSerializer;
3136
import com.couchbase.client.java.env.ClusterEnvironment;
37+
import com.couchbase.client.java.transactions.AttemptContextReactiveAccessor;
38+
import com.couchbase.client.java.transactions.config.TransactionsCleanupConfig;
39+
import com.couchbase.client.java.transactions.config.TransactionsConfig;
3240

3341
/**
3442
* The default implementation of a {@link CouchbaseClientFactory}.
@@ -44,18 +52,18 @@ public class SimpleCouchbaseClientFactory implements CouchbaseClientFactory {
4452
private final PersistenceExceptionTranslator exceptionTranslator;
4553

4654
public SimpleCouchbaseClientFactory(final String connectionString, final Authenticator authenticator,
47-
final String bucketName) {
55+
final String bucketName) {
4856
this(connectionString, authenticator, bucketName, null);
4957
}
5058

5159
public SimpleCouchbaseClientFactory(final String connectionString, final Authenticator authenticator,
52-
final String bucketName, final String scopeName) {
60+
final String bucketName, final String scopeName) {
5361
this(new OwnedSupplier<>(Cluster.connect(connectionString, ClusterOptions.clusterOptions(authenticator))),
5462
bucketName, scopeName);
5563
}
5664

5765
public SimpleCouchbaseClientFactory(final String connectionString, final Authenticator authenticator,
58-
final String bucketName, final String scopeName, final ClusterEnvironment environment) {
66+
final String bucketName, final String scopeName, final ClusterEnvironment environment) {
5967
this(
6068
new OwnedSupplier<>(
6169
Cluster.connect(connectionString, ClusterOptions.clusterOptions(authenticator).environment(environment))),
@@ -67,7 +75,7 @@ public SimpleCouchbaseClientFactory(final Cluster cluster, final String bucketNa
6775
}
6876

6977
private SimpleCouchbaseClientFactory(final Supplier<Cluster> cluster, final String bucketName,
70-
final String scopeName) {
78+
final String scopeName) {
7179
this.cluster = cluster;
7280
this.bucket = cluster.get().bucket(bucketName);
7381
this.scope = scopeName == null ? bucket.defaultScope() : bucket.scope(scopeName);
@@ -97,9 +105,9 @@ public Scope getScope() {
97105
@Override
98106
public Collection getCollection(final String collectionName) {
99107
final Scope scope = getScope();
100-
if (collectionName == null) {
108+
if (collectionName == null || CollectionIdentifier.DEFAULT_COLLECTION.equals(collectionName)) {
101109
if (!scope.name().equals(CollectionIdentifier.DEFAULT_SCOPE)) {
102-
throw new IllegalStateException("A collectionName must be provided if a non-default scope is used!");
110+
throw new IllegalStateException("A collectionName must be provided if a non-default scope is used");
103111
}
104112
return getBucket().defaultCollection();
105113
}
@@ -123,4 +131,8 @@ public void close() {
123131
}
124132
}
125133

134+
private static Duration now() {
135+
return Duration.of(System.nanoTime(), ChronoUnit.NANOS);
136+
}
137+
126138
}

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

+71-16
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,18 @@
1717
package org.springframework.data.couchbase.config;
1818

1919
import static com.couchbase.client.java.ClusterOptions.clusterOptions;
20+
import static org.springframework.data.couchbase.config.BeanNames.COUCHBASE_MAPPING_CONTEXT;
2021

2122
import java.util.Collections;
2223
import java.util.HashSet;
2324
import java.util.Set;
2425

26+
import com.couchbase.client.java.query.QueryScanConsistency;
2527
import org.springframework.beans.factory.config.BeanDefinition;
2628
import org.springframework.context.annotation.Bean;
2729
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
2830
import org.springframework.context.annotation.Configuration;
31+
import org.springframework.context.annotation.Role;
2932
import org.springframework.core.type.filter.AnnotationTypeFilter;
3033
import org.springframework.data.convert.CustomConversions;
3134
import org.springframework.data.couchbase.CouchbaseClientFactory;
@@ -40,9 +43,16 @@
4043
import org.springframework.data.couchbase.core.mapping.Document;
4144
import org.springframework.data.couchbase.repository.config.ReactiveRepositoryOperationsMapping;
4245
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
46+
import org.springframework.data.couchbase.transaction.CouchbaseCallbackTransactionManager;
47+
import org.springframework.data.couchbase.transaction.CouchbaseTransactionInterceptor;
48+
import org.springframework.data.couchbase.transaction.CouchbaseTransactionalOperator;
4349
import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy;
4450
import org.springframework.data.mapping.model.FieldNamingStrategy;
4551
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
52+
import org.springframework.transaction.TransactionManager;
53+
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
54+
import org.springframework.transaction.interceptor.TransactionAttributeSource;
55+
import org.springframework.transaction.interceptor.TransactionInterceptor;
4656
import org.springframework.util.ClassUtils;
4757
import org.springframework.util.StringUtils;
4858

@@ -57,7 +67,6 @@
5767
import com.couchbase.client.java.env.ClusterEnvironment;
5868
import com.couchbase.client.java.json.JacksonTransformers;
5969
import com.couchbase.client.java.json.JsonValueModule;
60-
import com.couchbase.client.java.query.QueryScanConsistency;
6170
import com.fasterxml.jackson.databind.ObjectMapper;
6271

6372
/**
@@ -123,15 +132,21 @@ protected Authenticator authenticator() {
123132
* @param couchbaseCluster the cluster reference from the SDK.
124133
* @return the initialized factory.
125134
*/
126-
@Bean
135+
@Bean(name = BeanNames.COUCHBASE_CLIENT_FACTORY)
127136
public CouchbaseClientFactory couchbaseClientFactory(final Cluster couchbaseCluster) {
128137
return new SimpleCouchbaseClientFactory(couchbaseCluster, getBucketName(), getScopeName());
129138
}
130-
139+
/*
140+
@Bean
141+
public ReactiveCouchbaseClientFactory reactiveCouchbaseClientFactory(final Cluster couchbaseCluster) {
142+
return new SimpleReactiveCouchbaseClientFactory(couchbaseCluster, getBucketName(), getScopeName());
143+
}
144+
*/
131145
@Bean(destroyMethod = "disconnect")
132146
public Cluster couchbaseCluster(ClusterEnvironment couchbaseClusterEnvironment) {
133-
return Cluster.connect(getConnectionString(),
147+
Cluster c = Cluster.connect(getConnectionString(),
134148
clusterOptions(authenticator()).environment(couchbaseClusterEnvironment));
149+
return c;
135150
}
136151

137152
@Bean(destroyMethod = "shutdown")
@@ -156,24 +171,29 @@ protected void configureEnvironment(final ClusterEnvironment.Builder builder) {
156171

157172
@Bean(name = BeanNames.COUCHBASE_TEMPLATE)
158173
public CouchbaseTemplate couchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory,
159-
MappingCouchbaseConverter mappingCouchbaseConverter, TranslationService couchbaseTranslationService) {
160-
return new CouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter, couchbaseTranslationService,
161-
getDefaultConsistency());
174+
MappingCouchbaseConverter mappingCouchbaseConverter, TranslationService couchbaseTranslationService) {
175+
return new CouchbaseTemplate(couchbaseClientFactory,
176+
mappingCouchbaseConverter,
177+
couchbaseTranslationService, getDefaultConsistency());
162178
}
163179

164180
public CouchbaseTemplate couchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory,
165-
MappingCouchbaseConverter mappingCouchbaseConverter) {
166-
return couchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter, new JacksonTranslationService());
181+
MappingCouchbaseConverter mappingCouchbaseConverter) {
182+
return couchbaseTemplate(couchbaseClientFactory,
183+
mappingCouchbaseConverter,
184+
new JacksonTranslationService());
167185
}
168186

169187
@Bean(name = BeanNames.REACTIVE_COUCHBASE_TEMPLATE)
170-
public ReactiveCouchbaseTemplate reactiveCouchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory,
188+
public ReactiveCouchbaseTemplate reactiveCouchbaseTemplate(
189+
CouchbaseClientFactory couchbaseClientFactory,
171190
MappingCouchbaseConverter mappingCouchbaseConverter, TranslationService couchbaseTranslationService) {
172-
return new ReactiveCouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter, couchbaseTranslationService,
173-
getDefaultConsistency());
191+
return new ReactiveCouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter,
192+
couchbaseTranslationService, getDefaultConsistency());
174193
}
175194

176-
public ReactiveCouchbaseTemplate reactiveCouchbaseTemplate(CouchbaseClientFactory couchbaseClientFactory,
195+
public ReactiveCouchbaseTemplate reactiveCouchbaseTemplate(
196+
CouchbaseClientFactory couchbaseClientFactory,
177197
MappingCouchbaseConverter mappingCouchbaseConverter) {
178198
return reactiveCouchbaseTemplate(couchbaseClientFactory, mappingCouchbaseConverter,
179199
new JacksonTranslationService());
@@ -280,9 +300,8 @@ public TranslationService couchbaseTranslationService() {
280300

281301
/**
282302
* Creates a {@link CouchbaseMappingContext} equipped with entity classes scanned from the mapping base package.
283-
*
284303
*/
285-
@Bean
304+
@Bean(COUCHBASE_MAPPING_CONTEXT)
286305
public CouchbaseMappingContext couchbaseMappingContext(CustomConversions customConversions) throws Exception {
287306
CouchbaseMappingContext mappingContext = new CouchbaseMappingContext();
288307
mappingContext.setInitialEntitySet(getInitialEntitySet());
@@ -310,6 +329,43 @@ public ObjectMapper couchbaseObjectMapper() {
310329
return mapper;
311330
}
312331

332+
/**
333+
* The default blocking transaction manager. It is an implementation of CallbackPreferringTransactionManager
334+
* CallbackPreferrringTransactionmanagers do not play well with test-cases that rely
335+
* on @TestTransaction/@BeforeTransaction/@AfterTransaction
336+
*
337+
* @param clientFactory
338+
* @return
339+
*/
340+
@Bean(BeanNames.COUCHBASE_TRANSACTION_MANAGER)
341+
CouchbaseCallbackTransactionManager callbackTransactionManager(CouchbaseClientFactory clientFactory) {
342+
return new CouchbaseCallbackTransactionManager(clientFactory);
343+
}
344+
345+
/**
346+
* The default TransactionalOperator.
347+
*
348+
* @param couchbaseCallbackTransactionManager
349+
* @return
350+
*/
351+
@Bean(BeanNames.COUCHBASE_TRANSACTIONAL_OPERATOR)
352+
public CouchbaseTransactionalOperator transactionalOperator(
353+
CouchbaseCallbackTransactionManager couchbaseCallbackTransactionManager) {
354+
return CouchbaseTransactionalOperator.create(couchbaseCallbackTransactionManager);
355+
}
356+
357+
@Bean
358+
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
359+
public TransactionInterceptor transactionInterceptor(TransactionManager couchbaseTransactionManager) {
360+
TransactionAttributeSource transactionAttributeSource = new AnnotationTransactionAttributeSource();
361+
TransactionInterceptor interceptor = new CouchbaseTransactionInterceptor(couchbaseTransactionManager, transactionAttributeSource);
362+
interceptor.setTransactionAttributeSource(transactionAttributeSource);
363+
if (couchbaseTransactionManager != null) {
364+
interceptor.setTransactionManager(couchbaseTransactionManager);
365+
}
366+
return interceptor;
367+
}
368+
313369
/**
314370
* Configure whether to automatically create indices for domain types by deriving the from the entity or not.
315371
*/
@@ -375,5 +431,4 @@ private boolean nonShadowedJacksonPresent() {
375431
public QueryScanConsistency getDefaultConsistency() {
376432
return null;
377433
}
378-
379434
}

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

+6
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,10 @@ public class BeanNames {
5959
* The name for the bean that will handle reactive audit trail marking of entities.
6060
*/
6161
public static final String REACTIVE_COUCHBASE_AUDITING_HANDLER = "reactiveCouchbaseAuditingHandler";
62+
63+
public static final String COUCHBASE_CLIENT_FACTORY = "couchbaseClientFactory";
64+
65+
public static final String COUCHBASE_TRANSACTION_MANAGER = "couchbaseTransactionManager";
66+
67+
public static final String COUCHBASE_TRANSACTIONAL_OPERATOR = "couchbaseTransactionalOperator" ;
6268
}

0 commit comments

Comments
 (0)