23
23
import com .arangodb .ArangoDBException ;
24
24
import com .arangodb .ArangoDatabase ;
25
25
import com .arangodb .DbName ;
26
- import com .arangodb .model .StreamTransactionOptions ;
27
26
import com .arangodb .springframework .core .ArangoOperations ;
28
27
import com .arangodb .springframework .repository .query .QueryTransactionBridge ;
28
+ import org .springframework .beans .factory .InitializingBean ;
29
+ import org .springframework .lang .Nullable ;
29
30
import org .springframework .transaction .*;
30
31
import org .springframework .transaction .support .AbstractPlatformTransactionManager ;
31
32
import org .springframework .transaction .support .DefaultTransactionStatus ;
32
33
import org .springframework .transaction .support .TransactionSynchronizationManager ;
33
34
35
+ import java .util .function .Function ;
36
+
34
37
/**
35
38
* Transaction manager using ArangoDB stream transactions on the
36
- * {@linkplain ArangoOperations#getDatabaseName()} current database} of the template.
37
- * Isolation level {@linkplain TransactionDefinition#ISOLATION_SERIALIZABLE serializable} is not supported.
39
+ * {@linkplain ArangoOperations#getDatabaseName() current database} of the
40
+ * template. A {@linkplain ArangoTransactionObject transaction object} using
41
+ * a shared {@linkplain ArangoTransactionHolder holder} is used for the
42
+ * {@link DefaultTransactionStatus}. Neither
43
+ * {@linkplain TransactionDefinition#getPropagationBehavior() propagation}
44
+ * {@linkplain TransactionDefinition#PROPAGATION_NESTED nested} nor
45
+ * {@linkplain TransactionDefinition#getIsolationLevel() isolation}
46
+ * {@linkplain TransactionDefinition#ISOLATION_SERIALIZABLE serializable} are
47
+ * supported.
38
48
*/
39
- public class ArangoTransactionManager extends AbstractPlatformTransactionManager {
49
+ public class ArangoTransactionManager extends AbstractPlatformTransactionManager implements InitializingBean {
40
50
41
51
private final ArangoOperations operations ;
42
52
private final QueryTransactionBridge bridge ;
43
53
44
54
public ArangoTransactionManager (ArangoOperations operations , QueryTransactionBridge bridge ) {
45
55
this .operations = operations ;
46
56
this .bridge = bridge ;
47
- setValidateExistingTransaction (true );
57
+ super .setGlobalRollbackOnParticipationFailure (true );
58
+ super .setTransactionSynchronization (SYNCHRONIZATION_ON_ACTUAL_TRANSACTION );
59
+ }
60
+
61
+ /**
62
+ * Check for supported property settings.
63
+ */
64
+ @ Override
65
+ public void afterPropertiesSet () {
66
+ if (isNestedTransactionAllowed ()) {
67
+ throw new IllegalStateException ("Nested transactions must not be allowed" );
68
+ }
69
+ if (!isGlobalRollbackOnParticipationFailure ()) {
70
+ throw new IllegalStateException ("Global rollback on participating failure is needed" );
71
+ }
72
+ if (getTransactionSynchronization () == SYNCHRONIZATION_NEVER ) {
73
+ throw new IllegalStateException ("Transaction synchronization is needed always" );
74
+ }
48
75
}
49
76
50
77
/**
51
- * Creates a new transaction object. Any synchronized resource will be reused.
78
+ * Creates a new transaction object. Any holder bound will be reused.
52
79
*/
53
80
@ Override
54
81
protected ArangoTransactionObject doGetTransaction () {
55
82
DbName database = operations .getDatabaseName ();
56
- if (logger .isDebugEnabled ()) {
57
- logger .debug ("Create new transaction for database " + database );
58
- }
83
+ ArangoTransactionHolder holder = (ArangoTransactionHolder ) TransactionSynchronizationManager .getResource (database );
59
84
try {
60
- ArangoTransactionResource resource = (ArangoTransactionResource ) TransactionSynchronizationManager .getResource (database );
61
- return new ArangoTransactionObject (operations .driver ().db (database ), getDefaultTimeout (), resource );
85
+ return new ArangoTransactionObject (operations .driver ().db (database ), getDefaultTimeout (), holder );
62
86
} catch (ArangoDBException error ) {
63
87
throw new TransactionSystemException ("Cannot create transaction object" , error );
64
88
}
65
89
}
66
90
67
91
/**
68
- * Configures the new transaction object. The resulting resource will be synchronized and the bridge will be initialized .
92
+ * Connect the new transaction object to the query bridge .
69
93
*
70
- * @see ArangoDatabase#beginStreamTransaction(StreamTransactionOptions)
71
- * @see QueryTransactionBridge
94
+ * @see QueryTransactionBridge#setCurrentTransaction(Function)
95
+ * @see #prepareSynchronization(DefaultTransactionStatus, TransactionDefinition)
96
+ * @throws InvalidIsolationLevelException for {@link TransactionDefinition#ISOLATION_SERIALIZABLE}
72
97
*/
73
98
@ Override
74
- protected void doBegin (Object transaction , TransactionDefinition definition ) throws TransactionUsageException {
99
+ protected void doBegin (Object transaction , TransactionDefinition definition ) throws InvalidIsolationLevelException {
75
100
int isolationLevel = definition .getIsolationLevel ();
76
- if (isolationLevel != - 1 && (isolationLevel & TransactionDefinition .ISOLATION_SERIALIZABLE ) != 0 ) {
101
+ if (isolationLevel != TransactionDefinition . ISOLATION_DEFAULT && (isolationLevel & TransactionDefinition .ISOLATION_SERIALIZABLE ) != 0 ) {
77
102
throw new InvalidIsolationLevelException ("ArangoDB does not support isolation level serializable" );
78
103
}
79
104
ArangoTransactionObject tx = (ArangoTransactionObject ) transaction ;
80
- tx .configure (definition );
81
105
bridge .setCurrentTransaction (collections -> {
82
106
try {
83
107
return tx .getOrBegin (collections ).getStreamTransactionId ();
@@ -88,9 +112,11 @@ protected void doBegin(Object transaction, TransactionDefinition definition) thr
88
112
}
89
113
90
114
/**
91
- * Commit the current stream transaction iff any. The bridge is cleared afterwards.
115
+ * Commit the current stream transaction. The query bridge is cleared
116
+ * afterwards.
92
117
*
93
118
* @see ArangoDatabase#commitStreamTransaction(String)
119
+ * @see QueryTransactionBridge#clearCurrentTransaction()
94
120
*/
95
121
@ Override
96
122
protected void doCommit (DefaultTransactionStatus status ) throws TransactionException {
@@ -100,16 +126,19 @@ protected void doCommit(DefaultTransactionStatus status) throws TransactionExcep
100
126
}
101
127
try {
102
128
tx .commit ();
103
- bridge .clearCurrentTransaction ();
104
129
} catch (ArangoDBException error ) {
105
130
throw new TransactionSystemException ("Cannot commit transaction " + tx , error );
131
+ } finally {
132
+ bridge .clearCurrentTransaction ();
106
133
}
107
134
}
108
135
109
136
/**
110
- * Roll back the current stream transaction iff any. The bridge is cleared afterwards.
137
+ * Roll back the current stream transaction. The query bridge is cleared
138
+ * afterwards.
111
139
*
112
140
* @see ArangoDatabase#abortStreamTransaction(String)
141
+ * @see QueryTransactionBridge#clearCurrentTransaction()
113
142
*/
114
143
@ Override
115
144
protected void doRollback (DefaultTransactionStatus status ) throws TransactionException {
@@ -119,58 +148,68 @@ protected void doRollback(DefaultTransactionStatus status) throws TransactionExc
119
148
}
120
149
try {
121
150
tx .rollback ();
122
- bridge .clearCurrentTransaction ();
123
151
} catch (ArangoDBException error ) {
124
152
throw new TransactionSystemException ("Cannot roll back transaction " + tx , error );
153
+ } finally {
154
+ bridge .clearCurrentTransaction ();
125
155
}
126
156
}
127
157
128
158
/**
129
- * Check if the transaction objects has an underlying stream transaction.
130
- *
131
- * @see ArangoDatabase#getStreamTransaction(String)
159
+ * Check if the transaction object has the bound holder. For new
160
+ * transactions the holder will be bound afterwards.
132
161
*/
133
162
@ Override
134
163
protected boolean isExistingTransaction (Object transaction ) throws TransactionException {
135
- return ((ArangoTransactionObject ) transaction ).exists ();
164
+ ArangoTransactionHolder holder = ((ArangoTransactionObject ) transaction ).getHolder ();
165
+ return holder == TransactionSynchronizationManager .getResource (operations .getDatabaseName ());
136
166
}
137
167
168
+ /**
169
+ * Mark the transaction as global rollback only.
170
+ *
171
+ * @see #isGlobalRollbackOnParticipationFailure()
172
+ */
138
173
@ Override
139
174
protected void doSetRollbackOnly (DefaultTransactionStatus status ) throws TransactionException {
140
175
ArangoTransactionObject tx = (ArangoTransactionObject ) status .getTransaction ();
141
- tx .setRollbackOnly ();
176
+ tx .getHolder (). setRollbackOnly ();
142
177
}
143
178
179
+ /**
180
+ * Any transaction object is configured according to the definition upfront.
181
+ *
182
+ * @see ArangoTransactionObject#configure(TransactionDefinition)
183
+ */
144
184
@ Override
145
- protected DefaultTransactionStatus newTransactionStatus (TransactionDefinition definition , Object transaction , boolean newTransaction , boolean newSynchronization , boolean debug , Object suspendedResources ) {
185
+ protected DefaultTransactionStatus newTransactionStatus (TransactionDefinition definition , @ Nullable Object transaction , boolean newTransaction , boolean newSynchronization , boolean debug , @ Nullable Object suspendedResources ) {
186
+ if (transaction instanceof ArangoTransactionObject ) {
187
+ ((ArangoTransactionObject ) transaction ).configure (definition );
188
+ }
146
189
return super .newTransactionStatus (definition , transaction , newTransaction , newSynchronization , debug , suspendedResources );
147
190
}
148
191
149
192
/**
150
- * Bind the resource for the first new transaction created.
193
+ * Bind the holder for the first new transaction created.
194
+ *
195
+ * @see ArangoTransactionHolder
151
196
*/
152
197
@ Override
153
198
protected void prepareSynchronization (DefaultTransactionStatus status , TransactionDefinition definition ) {
154
199
super .prepareSynchronization (status , definition );
155
- if (status .isNewTransaction ()) {
156
- ArangoTransactionResource resource = ((ArangoTransactionObject ) status .getTransaction ()).getResource ();
157
- resource .increaseReferences ();
158
- if (resource .isSingleReference ()) {
159
- TransactionSynchronizationManager .bindResource (operations .getDatabaseName (), resource );
160
- }
200
+ if (status .isNewSynchronization ()) {
201
+ ArangoTransactionHolder holder = ((ArangoTransactionObject ) status .getTransaction ()).getHolder ();
202
+ TransactionSynchronizationManager .bindResource (operations .getDatabaseName (), holder );
161
203
}
162
204
}
163
205
164
206
/**
165
- * Unbind the resource for the last transaction completed.
207
+ * Unbind the holder from the last transaction completed.
208
+ *
209
+ * @see ArangoTransactionHolder
166
210
*/
167
211
@ Override
168
212
protected void doCleanupAfterCompletion (Object transaction ) {
169
- ArangoTransactionResource resource = ((ArangoTransactionObject ) transaction ).getResource ();
170
- if (resource .isSingleReference ()) {
171
- TransactionSynchronizationManager .unbindResource (operations .getDatabaseName ());
172
- }
173
- resource .decreasedReferences ();
213
+ TransactionSynchronizationManager .unbindResource (operations .getDatabaseName ());
174
214
}
175
-
176
215
}
0 commit comments