Skip to content

Commit a2a6bc3

Browse files
committed
Introduce TransactionDefinition.withDefaults() shortcut
Such a static unmodifiable TransactionDefinition with defaults can be used for getTransaction(null) calls, now also possible for getReactiveTransaction. Furthermore, it can be used for a simple TransactionalOperator.create(ReactiveTransactionManager) method without an internal dependency on the transaction.support package. See gh-22646
1 parent eaa9a78 commit a2a6bc3

File tree

8 files changed

+154
-76
lines changed

8 files changed

+154
-76
lines changed

spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessor.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -41,9 +41,9 @@
4141
import org.springframework.core.io.ResourceLoader;
4242
import org.springframework.lang.Nullable;
4343
import org.springframework.transaction.PlatformTransactionManager;
44+
import org.springframework.transaction.TransactionDefinition;
4445
import org.springframework.transaction.TransactionException;
4546
import org.springframework.transaction.TransactionStatus;
46-
import org.springframework.transaction.support.DefaultTransactionDefinition;
4747

4848
/**
4949
* Common base class for accessing a Quartz Scheduler, i.e. for registering jobs,
@@ -207,7 +207,7 @@ public void setResourceLoader(ResourceLoader resourceLoader) {
207207
protected void registerJobsAndTriggers() throws SchedulerException {
208208
TransactionStatus transactionStatus = null;
209209
if (this.transactionManager != null) {
210-
transactionStatus = this.transactionManager.getTransaction(new DefaultTransactionDefinition());
210+
transactionStatus = this.transactionManager.getTransaction(TransactionDefinition.withDefaults());
211211
}
212212

213213
try {

spring-tx/src/main/java/org/springframework/transaction/ReactiveTransactionManager.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import reactor.core.publisher.Mono;
2020

21+
import org.springframework.lang.Nullable;
22+
2123
/**
2224
* This is the central interface in Spring's reactive transaction infrastructure.
2325
* Applications can use this directly, but it is not primarily meant as API:
@@ -27,6 +29,8 @@
2729
* @author Mark Paluch
2830
* @author Juergen Hoeller
2931
* @since 5.2
32+
* @see org.springframework.transaction.reactive.TransactionalOperator
33+
* @see org.springframework.transaction.interceptor.TransactionInterceptor
3034
*/
3135
public interface ReactiveTransactionManager extends TransactionManager {
3236

@@ -53,7 +57,8 @@ public interface ReactiveTransactionManager extends TransactionManager {
5357
* @see TransactionDefinition#getTimeout
5458
* @see TransactionDefinition#isReadOnly
5559
*/
56-
Mono<ReactiveTransaction> getReactiveTransaction(TransactionDefinition definition) throws TransactionException;
60+
Mono<ReactiveTransaction> getReactiveTransaction(@Nullable TransactionDefinition definition)
61+
throws TransactionException;
5762

5863
/**
5964
* Commit the given transaction, with regard to its status. If the transaction
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2002-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.transaction;
18+
19+
/**
20+
* A static unmodifiable transaction definition.
21+
*
22+
* @author Juergen Hoeller
23+
* @since 5.2
24+
* @see TransactionDefinition#withDefaults()
25+
*/
26+
final class StaticTransactionDefinition implements TransactionDefinition {
27+
28+
static final StaticTransactionDefinition INSTANCE = new StaticTransactionDefinition();
29+
30+
private StaticTransactionDefinition() {
31+
}
32+
33+
}

spring-tx/src/main/java/org/springframework/transaction/TransactionDefinition.java

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -195,11 +195,14 @@ public interface TransactionDefinition {
195195
* Return the propagation behavior.
196196
* <p>Must return one of the {@code PROPAGATION_XXX} constants
197197
* defined on {@link TransactionDefinition this interface}.
198+
* <p>The default is {@link #PROPAGATION_REQUIRED}.
198199
* @return the propagation behavior
199200
* @see #PROPAGATION_REQUIRED
200201
* @see org.springframework.transaction.support.TransactionSynchronizationManager#isActualTransactionActive()
201202
*/
202-
int getPropagationBehavior();
203+
default int getPropagationBehavior() {
204+
return PROPAGATION_REQUIRED;
205+
}
203206

204207
/**
205208
* Return the isolation level.
@@ -212,13 +215,16 @@ public interface TransactionDefinition {
212215
* "true" on your transaction manager if you'd like isolation level declarations
213216
* to get rejected when participating in an existing transaction with a different
214217
* isolation level.
215-
* <p>Note that a transaction manager that does not support custom isolation levels
216-
* will throw an exception when given any other level than {@link #ISOLATION_DEFAULT}.
218+
* <p>The default is {@link #ISOLATION_DEFAULT}. Note that a transaction manager
219+
* that does not support custom isolation levels will throw an exception when
220+
* given any other level than {@link #ISOLATION_DEFAULT}.
217221
* @return the isolation level
218222
* @see #ISOLATION_DEFAULT
219223
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setValidateExistingTransaction
220224
*/
221-
int getIsolationLevel();
225+
default int getIsolationLevel() {
226+
return ISOLATION_DEFAULT;
227+
}
222228

223229
/**
224230
* Return the transaction timeout.
@@ -228,9 +234,12 @@ public interface TransactionDefinition {
228234
* transactions.
229235
* <p>Note that a transaction manager that does not support timeouts will throw
230236
* an exception when given any other timeout than {@link #TIMEOUT_DEFAULT}.
237+
* <p>The default is {@link #TIMEOUT_DEFAULT}.
231238
* @return the transaction timeout
232239
*/
233-
int getTimeout();
240+
default int getTimeout() {
241+
return TIMEOUT_DEFAULT;
242+
}
234243

235244
/**
236245
* Return whether to optimize as a read-only transaction.
@@ -245,22 +254,41 @@ public interface TransactionDefinition {
245254
* A transaction manager which cannot interpret the read-only hint will
246255
* <i>not</i> throw an exception when asked for a read-only transaction.
247256
* @return {@code true} if the transaction is to be optimized as read-only
257+
* ({@code false} by default)
248258
* @see org.springframework.transaction.support.TransactionSynchronization#beforeCommit(boolean)
249259
* @see org.springframework.transaction.support.TransactionSynchronizationManager#isCurrentTransactionReadOnly()
250260
*/
251-
boolean isReadOnly();
261+
default boolean isReadOnly() {
262+
return false;
263+
}
252264

253265
/**
254266
* Return the name of this transaction. Can be {@code null}.
255267
* <p>This will be used as the transaction name to be shown in a
256268
* transaction monitor, if applicable (for example, WebLogic's).
257269
* <p>In case of Spring's declarative transactions, the exposed name will be
258270
* the {@code fully-qualified class name + "." + method name} (by default).
259-
* @return the name of this transaction
271+
* @return the name of this transaction ({@code null} by default}
260272
* @see org.springframework.transaction.interceptor.TransactionAspectSupport
261273
* @see org.springframework.transaction.support.TransactionSynchronizationManager#getCurrentTransactionName()
262274
*/
263275
@Nullable
264-
String getName();
276+
default String getName() {
277+
return null;
278+
}
279+
280+
281+
// Static builder methods
282+
283+
/**
284+
* Return an unmodifiable {@code TransactionDefinition} with defaults.
285+
* <p>For customization purposes, use the modifiable
286+
* {@link org.springframework.transaction.support.DefaultTransactionDefinition}
287+
* instead.
288+
* @since 5.2
289+
*/
290+
static TransactionDefinition withDefaults() {
291+
return StaticTransactionDefinition.INSTANCE;
292+
}
265293

266294
}

spring-tx/src/main/java/org/springframework/transaction/jta/WebSphereUowTransactionManager.java

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,7 +34,6 @@
3434
import org.springframework.transaction.TransactionException;
3535
import org.springframework.transaction.TransactionSystemException;
3636
import org.springframework.transaction.support.CallbackPreferringPlatformTransactionManager;
37-
import org.springframework.transaction.support.DefaultTransactionDefinition;
3837
import org.springframework.transaction.support.DefaultTransactionStatus;
3938
import org.springframework.transaction.support.SmartTransactionObject;
4039
import org.springframework.transaction.support.TransactionCallback;
@@ -233,17 +232,15 @@ public boolean supportsResourceAdapterManagedTransactions() {
233232
public <T> T execute(@Nullable TransactionDefinition definition, TransactionCallback<T> callback)
234233
throws TransactionException {
235234

236-
if (definition == null) {
237-
// Use defaults if no transaction definition given.
238-
definition = new DefaultTransactionDefinition();
239-
}
235+
// Use defaults if no transaction definition given.
236+
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
240237

241-
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
242-
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
238+
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
239+
throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
243240
}
244241

245242
UOWManager uowManager = obtainUOWManager();
246-
int pb = definition.getPropagationBehavior();
243+
int pb = def.getPropagationBehavior();
247244
boolean existingTx = (uowManager.getUOWStatus() != UOWSynchronizationRegistry.UOW_STATUS_NONE &&
248245
uowManager.getUOWType() != UOWSynchronizationRegistry.UOW_TYPE_LOCAL_TRANSACTION);
249246

@@ -292,19 +289,19 @@ else if (pb == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
292289

293290
boolean debug = logger.isDebugEnabled();
294291
if (debug) {
295-
logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
292+
logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
296293
}
297294
SuspendedResourcesHolder suspendedResources = (!joinTx ? suspend(null) : null);
298295
UOWActionAdapter<T> action = null;
299296
try {
300-
if (definition.getTimeout() > TransactionDefinition.TIMEOUT_DEFAULT) {
301-
uowManager.setUOWTimeout(uowType, definition.getTimeout());
297+
if (def.getTimeout() > TransactionDefinition.TIMEOUT_DEFAULT) {
298+
uowManager.setUOWTimeout(uowType, def.getTimeout());
302299
}
303300
if (debug) {
304301
logger.debug("Invoking WebSphere UOW action: type=" + uowType + ", join=" + joinTx);
305302
}
306303
action = new UOWActionAdapter<>(
307-
definition, callback, (uowType == UOWManager.UOW_TYPE_GLOBAL_TRANSACTION), !joinTx, newSynch, debug);
304+
def, callback, (uowType == UOWManager.UOW_TYPE_GLOBAL_TRANSACTION), !joinTx, newSynch, debug);
308305
uowManager.runUnderUOW(uowType, joinTx, action);
309306
if (debug) {
310307
logger.debug("Returned from WebSphere UOW action: type=" + uowType + ", join=" + joinTx);

spring-tx/src/main/java/org/springframework/transaction/reactive/AbstractReactiveTransactionManager.java

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,12 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
9595
* @see #doBegin
9696
*/
9797
@Override
98-
public final Mono<ReactiveTransaction> getReactiveTransaction(TransactionDefinition definition) throws TransactionException {
98+
public final Mono<ReactiveTransaction> getReactiveTransaction(@Nullable TransactionDefinition definition)
99+
throws TransactionException {
100+
101+
// Use defaults if no transaction definition given.
102+
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
103+
99104
return TransactionSynchronizationManager.currentTransaction()
100105
.flatMap(synchronizationManager -> {
101106

@@ -106,22 +111,22 @@ public final Mono<ReactiveTransaction> getReactiveTransaction(TransactionDefinit
106111

107112
if (isExistingTransaction(transaction)) {
108113
// Existing transaction found -> check propagation behavior to find out how to behave.
109-
return handleExistingTransaction(synchronizationManager, definition, transaction, debugEnabled);
114+
return handleExistingTransaction(synchronizationManager, def, transaction, debugEnabled);
110115
}
111116

112117
// Check definition settings for new transaction.
113-
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
114-
return Mono.error(new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout()));
118+
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
119+
return Mono.error(new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout()));
115120
}
116121

117122
// No existing transaction found -> check propagation behavior to find out how to proceed.
118-
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
123+
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
119124
return Mono.error(new IllegalTransactionStateException(
120125
"No existing transaction found for transaction marked with propagation 'mandatory'"));
121126
}
122-
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
123-
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
124-
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
127+
else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
128+
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
129+
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
125130

126131
return TransactionContextManager.currentContext()
127132
.map(TransactionSynchronizationManager::new)
@@ -131,14 +136,14 @@ else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATIO
131136
.defaultIfEmpty(Optional.empty())
132137
.flatMap(suspendedResources -> {
133138
if (debugEnabled) {
134-
logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
139+
logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
135140
}
136141
return Mono.defer(() -> {
137142
GenericReactiveTransaction status = newReactiveTransaction(
138-
nestedSynchronizationManager, definition, transaction, true,
143+
nestedSynchronizationManager, def, transaction, true,
139144
debugEnabled, suspendedResources.orElse(null));
140-
return doBegin(nestedSynchronizationManager, transaction, definition)
141-
.doOnSuccess(ignore -> prepareSynchronization(nestedSynchronizationManager, status, definition))
145+
return doBegin(nestedSynchronizationManager, transaction, def)
146+
.doOnSuccess(ignore -> prepareSynchronization(nestedSynchronizationManager, status, def))
142147
.thenReturn(status);
143148
}).onErrorResume(ErrorPredicates.RUNTIME_OR_ERROR,
144149
ex -> resume(nestedSynchronizationManager, null, suspendedResources.orElse(null))
@@ -147,11 +152,11 @@ else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATIO
147152
}
148153
else {
149154
// Create "empty" transaction: no actual transaction, but potentially synchronization.
150-
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
155+
if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
151156
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
152-
"isolation level will effectively be ignored: " + definition);
157+
"isolation level will effectively be ignored: " + def);
153158
}
154-
return Mono.just(prepareReactiveTransaction(synchronizationManager, definition, null, true, debugEnabled, null));
159+
return Mono.just(prepareReactiveTransaction(synchronizationManager, def, null, true, debugEnabled, null));
155160
}
156161
});
157162
}

spring-tx/src/main/java/org/springframework/transaction/reactive/TransactionalOperator.java

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,6 @@
4646
*/
4747
public interface TransactionalOperator {
4848

49-
/**
50-
* Create a new {@link TransactionalOperator} using {@link ReactiveTransactionManager}
51-
* and {@link TransactionDefinition}.
52-
* @param transactionManager the transaction management strategy to be used
53-
* @param transactionDefinition the transaction definition to apply
54-
* @return the transactional operator
55-
*/
56-
static TransactionalOperator create(
57-
ReactiveTransactionManager transactionManager, TransactionDefinition transactionDefinition){
58-
59-
return new TransactionalOperatorImpl(transactionManager, transactionDefinition);
60-
}
61-
62-
6349
/**
6450
* Wrap the functional sequence specified by the given Flux within a transaction.
6551
* @param flux the Flux that should be executed within the transaction
@@ -95,4 +81,30 @@ default <T> Mono<T> transactional(Mono<T> mono) {
9581
*/
9682
<T> Flux<T> execute(TransactionCallback<T> action) throws TransactionException;
9783

84+
85+
// Static builder methods
86+
87+
/**
88+
* Create a new {@link TransactionalOperator} using {@link ReactiveTransactionManager},
89+
* using a default transaction.
90+
* @param transactionManager the transaction management strategy to be used
91+
* @return the transactional operator
92+
*/
93+
static TransactionalOperator create(ReactiveTransactionManager transactionManager){
94+
return create(transactionManager, TransactionDefinition.withDefaults());
95+
}
96+
97+
/**
98+
* Create a new {@link TransactionalOperator} using {@link ReactiveTransactionManager}
99+
* and {@link TransactionDefinition}.
100+
* @param transactionManager the transaction management strategy to be used
101+
* @param transactionDefinition the transaction definition to apply
102+
* @return the transactional operator
103+
*/
104+
static TransactionalOperator create(
105+
ReactiveTransactionManager transactionManager, TransactionDefinition transactionDefinition){
106+
107+
return new TransactionalOperatorImpl(transactionManager, transactionDefinition);
108+
}
109+
98110
}

0 commit comments

Comments
 (0)