15
15
*/
16
16
package org .springframework .data .couchbase .transaction ;
17
17
18
- import com .couchbase .client .core .transaction .CoreTransactionAttemptContext ;
19
18
import com .couchbase .client .java .transactions .AttemptContextReactiveAccessor ;
20
- import com .couchbase .client .java .transactions .ReactiveTransactionAttemptContext ;
21
19
import com .couchbase .client .java .transactions .TransactionAttemptContext ;
22
20
import com .couchbase .client .java .transactions .TransactionResult ;
23
21
import com .couchbase .client .java .transactions .config .TransactionOptions ;
24
22
import org .slf4j .Logger ;
25
23
import org .slf4j .LoggerFactory ;
26
- import org .springframework .data .couchbase .CouchbaseClientFactory ;
27
24
import org .springframework .data .couchbase .ReactiveCouchbaseClientFactory ;
28
25
import org .springframework .lang .Nullable ;
29
26
import org .springframework .transaction .IllegalTransactionStateException ;
30
- import org .springframework .transaction .InvalidTimeoutException ;
31
- import org .springframework .transaction .PlatformTransactionManager ;
32
27
import org .springframework .transaction .TransactionDefinition ;
33
28
import org .springframework .transaction .TransactionException ;
34
29
import org .springframework .transaction .TransactionStatus ;
35
- import org .springframework .transaction .reactive .TransactionContextManager ;
36
- import org .springframework .transaction .support .AbstractPlatformTransactionManager ;
37
30
import org .springframework .transaction .support .CallbackPreferringPlatformTransactionManager ;
38
31
import org .springframework .transaction .support .DefaultTransactionStatus ;
39
32
import org .springframework .transaction .support .TransactionCallback ;
40
33
import org .springframework .transaction .support .TransactionSynchronizationManager ;
41
- import org .springframework .util .Assert ;
42
- import reactor .core .publisher .Mono ;
43
34
44
- import java .lang .reflect .Field ;
45
35
import java .time .Duration ;
46
36
import java .util .concurrent .atomic .AtomicReference ;
47
37
48
38
public class CouchbaseSimpleCallbackTransactionManager implements CallbackPreferringPlatformTransactionManager {
49
39
50
- private static final Logger LOGGER = LoggerFactory .getLogger (CouchbaseTransactionManager .class );
40
+ private static final Logger LOGGER = LoggerFactory .getLogger (CouchbaseSimpleCallbackTransactionManager .class );
51
41
52
42
private final ReactiveCouchbaseClientFactory couchbaseClientFactory ;
53
43
private TransactionOptions options ;
@@ -59,19 +49,29 @@ public CouchbaseSimpleCallbackTransactionManager(ReactiveCouchbaseClientFactory
59
49
60
50
@ Override
61
51
public <T > T execute (TransactionDefinition definition , TransactionCallback <T > callback ) throws TransactionException {
62
- final AtomicReference < T > execResult = new AtomicReference <>( );
52
+ boolean createNewTransaction = handlePropagation ( definition );
63
53
64
54
setOptionsFromDefinition (definition );
65
55
56
+ if (createNewTransaction ) {
57
+ return executeNewTransaction (callback );
58
+ }
59
+ else {
60
+ return callback .doInTransaction (null );
61
+ }
62
+ }
63
+
64
+ private <T > T executeNewTransaction (TransactionCallback <T > callback ) {
65
+ final AtomicReference <T > execResult = new AtomicReference <>();
66
+
66
67
TransactionResult result = couchbaseClientFactory .getCluster ().block ().transactions ().run (ctx -> {
67
68
CouchbaseTransactionStatus status = new CouchbaseTransactionStatus (null , true , false , false , true , null , null );
68
69
69
70
populateTransactionSynchronizationManager (ctx );
70
71
71
72
try {
72
73
execResult .set (callback .doInTransaction (status ));
73
- }
74
- finally {
74
+ } finally {
75
75
TransactionSynchronizationManager .clear ();
76
76
}
77
77
}, this .options );
@@ -81,6 +81,61 @@ public <T> T execute(TransactionDefinition definition, TransactionCallback<T> ca
81
81
return execResult .get ();
82
82
}
83
83
84
+ // Propagation defines what happens when a @Transactional method is called from another @Transactional method.
85
+ private boolean handlePropagation (TransactionDefinition definition ) {
86
+ boolean isExistingTransaction = TransactionSynchronizationManager .isActualTransactionActive ();
87
+
88
+ LOGGER .trace ("Deciding propagation behaviour from {} and {}" , definition .getPropagationBehavior (), isExistingTransaction );
89
+
90
+ switch (definition .getPropagationBehavior ()) {
91
+ case TransactionDefinition .PROPAGATION_REQUIRED :
92
+ // Make a new transaction if required, else just execute the new method in the current transaction.
93
+ return !isExistingTransaction ;
94
+
95
+ case TransactionDefinition .PROPAGATION_SUPPORTS :
96
+ // Don't appear to have the ability to execute the callback non-transactionally in this layer.
97
+ throw new IllegalTransactionStateException (
98
+ "Propagation level 'support' has been specified which is not supported" );
99
+
100
+ case TransactionDefinition .PROPAGATION_MANDATORY :
101
+ if (!isExistingTransaction ) {
102
+ throw new IllegalTransactionStateException (
103
+ "Propagation level 'mandatory' is specified but not in an active transaction" );
104
+ }
105
+ return false ;
106
+
107
+ case TransactionDefinition .PROPAGATION_REQUIRES_NEW :
108
+ // This requires suspension of the active transaction. This will be possible to support in a future
109
+ // release, if required.
110
+ throw new IllegalTransactionStateException (
111
+ "Propagation level 'requires_new' has been specified which is not currently supported" );
112
+
113
+ case TransactionDefinition .PROPAGATION_NOT_SUPPORTED :
114
+ // Don't appear to have the ability to execute the callback non-transactionally in this layer.
115
+ throw new IllegalTransactionStateException (
116
+ "Propagation level 'not_supported' has been specified which is not supported" );
117
+
118
+ case TransactionDefinition .PROPAGATION_NEVER :
119
+ if (isExistingTransaction ) {
120
+ throw new IllegalTransactionStateException (
121
+ "Existing transaction found for transaction marked with propagation 'never'" );
122
+ }
123
+ return true ;
124
+
125
+ case TransactionDefinition .PROPAGATION_NESTED :
126
+ if (isExistingTransaction ) {
127
+ // Couchbase transactions cannot be nested.
128
+ throw new IllegalTransactionStateException (
129
+ "Propagation level 'nested' has been specified which is not supported" );
130
+ }
131
+ return true ;
132
+
133
+ default :
134
+ throw new IllegalTransactionStateException (
135
+ "Unknown propagation level " + definition .getPropagationBehavior () + " has been specified" );
136
+ }
137
+ }
138
+
84
139
/**
85
140
* @param definition reflects the @Transactional options
86
141
*/
@@ -96,8 +151,6 @@ private void setOptionsFromDefinition(TransactionDefinition definition) {
96
151
}
97
152
98
153
// readonly is ignored as it is documented as being a hint that won't necessarily cause writes to fail
99
-
100
- // todo gpx what about propagation?
101
154
}
102
155
103
156
}
0 commit comments