diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessor.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessor.java index 496ae94fd9c..434cd49a3b1 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessor.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,9 +41,9 @@ import org.springframework.context.ResourceLoaderAware; import org.springframework.core.io.ResourceLoader; import org.springframework.lang.Nullable; import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.support.DefaultTransactionDefinition; /** * Common base class for accessing a Quartz Scheduler, i.e. for registering jobs, @@ -207,7 +207,7 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware { protected void registerJobsAndTriggers() throws SchedulerException { TransactionStatus transactionStatus = null; if (this.transactionManager != null) { - transactionStatus = this.transactionManager.getTransaction(new DefaultTransactionDefinition()); + transactionStatus = this.transactionManager.getTransaction(TransactionDefinition.withDefaults()); } try { diff --git a/spring-tx/src/main/java/org/springframework/transaction/ReactiveTransactionManager.java b/spring-tx/src/main/java/org/springframework/transaction/ReactiveTransactionManager.java index d9d5b26835d..47e423d946b 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/ReactiveTransactionManager.java +++ b/spring-tx/src/main/java/org/springframework/transaction/ReactiveTransactionManager.java @@ -18,6 +18,8 @@ package org.springframework.transaction; import reactor.core.publisher.Mono; +import org.springframework.lang.Nullable; + /** * This is the central interface in Spring's reactive transaction infrastructure. * Applications can use this directly, but it is not primarily meant as API: @@ -27,6 +29,8 @@ import reactor.core.publisher.Mono; * @author Mark Paluch * @author Juergen Hoeller * @since 5.2 + * @see org.springframework.transaction.reactive.TransactionalOperator + * @see org.springframework.transaction.interceptor.TransactionInterceptor */ public interface ReactiveTransactionManager extends TransactionManager { @@ -53,7 +57,8 @@ public interface ReactiveTransactionManager extends TransactionManager { * @see TransactionDefinition#getTimeout * @see TransactionDefinition#isReadOnly */ - Mono getReactiveTransaction(TransactionDefinition definition) throws TransactionException; + Mono getReactiveTransaction(@Nullable TransactionDefinition definition) + throws TransactionException; /** * Commit the given transaction, with regard to its status. If the transaction diff --git a/spring-tx/src/main/java/org/springframework/transaction/StaticTransactionDefinition.java b/spring-tx/src/main/java/org/springframework/transaction/StaticTransactionDefinition.java new file mode 100644 index 00000000000..a9af57c4baf --- /dev/null +++ b/spring-tx/src/main/java/org/springframework/transaction/StaticTransactionDefinition.java @@ -0,0 +1,33 @@ +/* + * Copyright 2002-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.transaction; + +/** + * A static unmodifiable transaction definition. + * + * @author Juergen Hoeller + * @since 5.2 + * @see TransactionDefinition#withDefaults() + */ +final class StaticTransactionDefinition implements TransactionDefinition { + + static final StaticTransactionDefinition INSTANCE = new StaticTransactionDefinition(); + + private StaticTransactionDefinition() { + } + +} diff --git a/spring-tx/src/main/java/org/springframework/transaction/TransactionDefinition.java b/spring-tx/src/main/java/org/springframework/transaction/TransactionDefinition.java index 33138d16c34..1e2a44e8fe1 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/TransactionDefinition.java +++ b/spring-tx/src/main/java/org/springframework/transaction/TransactionDefinition.java @@ -195,11 +195,14 @@ public interface TransactionDefinition { * Return the propagation behavior. *

Must return one of the {@code PROPAGATION_XXX} constants * defined on {@link TransactionDefinition this interface}. + *

The default is {@link #PROPAGATION_REQUIRED}. * @return the propagation behavior * @see #PROPAGATION_REQUIRED * @see org.springframework.transaction.support.TransactionSynchronizationManager#isActualTransactionActive() */ - int getPropagationBehavior(); + default int getPropagationBehavior() { + return PROPAGATION_REQUIRED; + } /** * Return the isolation level. @@ -212,13 +215,16 @@ public interface TransactionDefinition { * "true" on your transaction manager if you'd like isolation level declarations * to get rejected when participating in an existing transaction with a different * isolation level. - *

Note that a transaction manager that does not support custom isolation levels - * will throw an exception when given any other level than {@link #ISOLATION_DEFAULT}. + *

The default is {@link #ISOLATION_DEFAULT}. Note that a transaction manager + * that does not support custom isolation levels will throw an exception when + * given any other level than {@link #ISOLATION_DEFAULT}. * @return the isolation level * @see #ISOLATION_DEFAULT * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setValidateExistingTransaction */ - int getIsolationLevel(); + default int getIsolationLevel() { + return ISOLATION_DEFAULT; + } /** * Return the transaction timeout. @@ -228,9 +234,12 @@ public interface TransactionDefinition { * transactions. *

Note that a transaction manager that does not support timeouts will throw * an exception when given any other timeout than {@link #TIMEOUT_DEFAULT}. + *

The default is {@link #TIMEOUT_DEFAULT}. * @return the transaction timeout */ - int getTimeout(); + default int getTimeout() { + return TIMEOUT_DEFAULT; + } /** * Return whether to optimize as a read-only transaction. @@ -245,10 +254,13 @@ public interface TransactionDefinition { * A transaction manager which cannot interpret the read-only hint will * not throw an exception when asked for a read-only transaction. * @return {@code true} if the transaction is to be optimized as read-only + * ({@code false} by default) * @see org.springframework.transaction.support.TransactionSynchronization#beforeCommit(boolean) * @see org.springframework.transaction.support.TransactionSynchronizationManager#isCurrentTransactionReadOnly() */ - boolean isReadOnly(); + default boolean isReadOnly() { + return false; + } /** * Return the name of this transaction. Can be {@code null}. @@ -256,11 +268,27 @@ public interface TransactionDefinition { * transaction monitor, if applicable (for example, WebLogic's). *

In case of Spring's declarative transactions, the exposed name will be * the {@code fully-qualified class name + "." + method name} (by default). - * @return the name of this transaction + * @return the name of this transaction ({@code null} by default} * @see org.springframework.transaction.interceptor.TransactionAspectSupport * @see org.springframework.transaction.support.TransactionSynchronizationManager#getCurrentTransactionName() */ @Nullable - String getName(); + default String getName() { + return null; + } + + + // Static builder methods + + /** + * Return an unmodifiable {@code TransactionDefinition} with defaults. + *

For customization purposes, use the modifiable + * {@link org.springframework.transaction.support.DefaultTransactionDefinition} + * instead. + * @since 5.2 + */ + static TransactionDefinition withDefaults() { + return StaticTransactionDefinition.INSTANCE; + } } diff --git a/spring-tx/src/main/java/org/springframework/transaction/jta/WebSphereUowTransactionManager.java b/spring-tx/src/main/java/org/springframework/transaction/jta/WebSphereUowTransactionManager.java index 30ae675c6b5..b728671202e 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/jta/WebSphereUowTransactionManager.java +++ b/spring-tx/src/main/java/org/springframework/transaction/jta/WebSphereUowTransactionManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,6 @@ import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionSystemException; import org.springframework.transaction.support.CallbackPreferringPlatformTransactionManager; -import org.springframework.transaction.support.DefaultTransactionDefinition; import org.springframework.transaction.support.DefaultTransactionStatus; import org.springframework.transaction.support.SmartTransactionObject; import org.springframework.transaction.support.TransactionCallback; @@ -233,17 +232,15 @@ public class WebSphereUowTransactionManager extends JtaTransactionManager public T execute(@Nullable TransactionDefinition definition, TransactionCallback callback) throws TransactionException { - if (definition == null) { - // Use defaults if no transaction definition given. - definition = new DefaultTransactionDefinition(); - } + // Use defaults if no transaction definition given. + TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults()); - if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { - throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout()); + if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { + throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout()); } UOWManager uowManager = obtainUOWManager(); - int pb = definition.getPropagationBehavior(); + int pb = def.getPropagationBehavior(); boolean existingTx = (uowManager.getUOWStatus() != UOWSynchronizationRegistry.UOW_STATUS_NONE && uowManager.getUOWType() != UOWSynchronizationRegistry.UOW_TYPE_LOCAL_TRANSACTION); @@ -292,19 +289,19 @@ public class WebSphereUowTransactionManager extends JtaTransactionManager boolean debug = logger.isDebugEnabled(); if (debug) { - logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition); + logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def); } SuspendedResourcesHolder suspendedResources = (!joinTx ? suspend(null) : null); UOWActionAdapter action = null; try { - if (definition.getTimeout() > TransactionDefinition.TIMEOUT_DEFAULT) { - uowManager.setUOWTimeout(uowType, definition.getTimeout()); + if (def.getTimeout() > TransactionDefinition.TIMEOUT_DEFAULT) { + uowManager.setUOWTimeout(uowType, def.getTimeout()); } if (debug) { logger.debug("Invoking WebSphere UOW action: type=" + uowType + ", join=" + joinTx); } action = new UOWActionAdapter<>( - definition, callback, (uowType == UOWManager.UOW_TYPE_GLOBAL_TRANSACTION), !joinTx, newSynch, debug); + def, callback, (uowType == UOWManager.UOW_TYPE_GLOBAL_TRANSACTION), !joinTx, newSynch, debug); uowManager.runUnderUOW(uowType, joinTx, action); if (debug) { logger.debug("Returned from WebSphere UOW action: type=" + uowType + ", join=" + joinTx); diff --git a/spring-tx/src/main/java/org/springframework/transaction/reactive/AbstractReactiveTransactionManager.java b/spring-tx/src/main/java/org/springframework/transaction/reactive/AbstractReactiveTransactionManager.java index 6652f44e675..a24889943f7 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/reactive/AbstractReactiveTransactionManager.java +++ b/spring-tx/src/main/java/org/springframework/transaction/reactive/AbstractReactiveTransactionManager.java @@ -95,7 +95,12 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran * @see #doBegin */ @Override - public final Mono getReactiveTransaction(TransactionDefinition definition) throws TransactionException { + public final Mono getReactiveTransaction(@Nullable TransactionDefinition definition) + throws TransactionException { + + // Use defaults if no transaction definition given. + TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults()); + return TransactionSynchronizationManager.currentTransaction() .flatMap(synchronizationManager -> { @@ -106,22 +111,22 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran if (isExistingTransaction(transaction)) { // Existing transaction found -> check propagation behavior to find out how to behave. - return handleExistingTransaction(synchronizationManager, definition, transaction, debugEnabled); + return handleExistingTransaction(synchronizationManager, def, transaction, debugEnabled); } // Check definition settings for new transaction. - if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { - return Mono.error(new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout())); + if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { + return Mono.error(new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout())); } // No existing transaction found -> check propagation behavior to find out how to proceed. - if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { + if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { return Mono.error(new IllegalTransactionStateException( "No existing transaction found for transaction marked with propagation 'mandatory'")); } - else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || - definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || - definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { + else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || + def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || + def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { return TransactionContextManager.currentContext() .map(TransactionSynchronizationManager::new) @@ -131,14 +136,14 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran .defaultIfEmpty(Optional.empty()) .flatMap(suspendedResources -> { if (debugEnabled) { - logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition); + logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def); } return Mono.defer(() -> { GenericReactiveTransaction status = newReactiveTransaction( - nestedSynchronizationManager, definition, transaction, true, + nestedSynchronizationManager, def, transaction, true, debugEnabled, suspendedResources.orElse(null)); - return doBegin(nestedSynchronizationManager, transaction, definition) - .doOnSuccess(ignore -> prepareSynchronization(nestedSynchronizationManager, status, definition)) + return doBegin(nestedSynchronizationManager, transaction, def) + .doOnSuccess(ignore -> prepareSynchronization(nestedSynchronizationManager, status, def)) .thenReturn(status); }).onErrorResume(ErrorPredicates.RUNTIME_OR_ERROR, ex -> resume(nestedSynchronizationManager, null, suspendedResources.orElse(null)) @@ -147,11 +152,11 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran } else { // Create "empty" transaction: no actual transaction, but potentially synchronization. - if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) { + if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) { logger.warn("Custom isolation level specified but no actual transaction initiated; " + - "isolation level will effectively be ignored: " + definition); + "isolation level will effectively be ignored: " + def); } - return Mono.just(prepareReactiveTransaction(synchronizationManager, definition, null, true, debugEnabled, null)); + return Mono.just(prepareReactiveTransaction(synchronizationManager, def, null, true, debugEnabled, null)); } }); } diff --git a/spring-tx/src/main/java/org/springframework/transaction/reactive/TransactionalOperator.java b/spring-tx/src/main/java/org/springframework/transaction/reactive/TransactionalOperator.java index 557d3e90f0c..a1e753500de 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/reactive/TransactionalOperator.java +++ b/spring-tx/src/main/java/org/springframework/transaction/reactive/TransactionalOperator.java @@ -46,20 +46,6 @@ import org.springframework.transaction.TransactionException; */ public interface TransactionalOperator { - /** - * Create a new {@link TransactionalOperator} using {@link ReactiveTransactionManager} - * and {@link TransactionDefinition}. - * @param transactionManager the transaction management strategy to be used - * @param transactionDefinition the transaction definition to apply - * @return the transactional operator - */ - static TransactionalOperator create( - ReactiveTransactionManager transactionManager, TransactionDefinition transactionDefinition){ - - return new TransactionalOperatorImpl(transactionManager, transactionDefinition); - } - - /** * Wrap the functional sequence specified by the given Flux within a transaction. * @param flux the Flux that should be executed within the transaction @@ -95,4 +81,30 @@ public interface TransactionalOperator { */ Flux execute(TransactionCallback action) throws TransactionException; + + // Static builder methods + + /** + * Create a new {@link TransactionalOperator} using {@link ReactiveTransactionManager}, + * using a default transaction. + * @param transactionManager the transaction management strategy to be used + * @return the transactional operator + */ + static TransactionalOperator create(ReactiveTransactionManager transactionManager){ + return create(transactionManager, TransactionDefinition.withDefaults()); + } + + /** + * Create a new {@link TransactionalOperator} using {@link ReactiveTransactionManager} + * and {@link TransactionDefinition}. + * @param transactionManager the transaction management strategy to be used + * @param transactionDefinition the transaction definition to apply + * @return the transactional operator + */ + static TransactionalOperator create( + ReactiveTransactionManager transactionManager, TransactionDefinition transactionDefinition){ + + return new TransactionalOperatorImpl(transactionManager, transactionDefinition); + } + } diff --git a/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java b/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java index 809a854aad2..af8495c008b 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java +++ b/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java @@ -338,45 +338,43 @@ public abstract class AbstractPlatformTransactionManager implements PlatformTran * @see #doBegin */ @Override - public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException { + public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) + throws TransactionException { + + // Use defaults if no transaction definition given. + TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults()); + Object transaction = doGetTransaction(); - - // Cache debug flag to avoid repeated checks. boolean debugEnabled = logger.isDebugEnabled(); - if (definition == null) { - // Use defaults if no transaction definition given. - definition = new DefaultTransactionDefinition(); - } - if (isExistingTransaction(transaction)) { // Existing transaction found -> check propagation behavior to find out how to behave. - return handleExistingTransaction(definition, transaction, debugEnabled); + return handleExistingTransaction(def, transaction, debugEnabled); } // Check definition settings for new transaction. - if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { - throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout()); + if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { + throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout()); } // No existing transaction found -> check propagation behavior to find out how to proceed. - if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { + if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { throw new IllegalTransactionStateException( "No existing transaction found for transaction marked with propagation 'mandatory'"); } - else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || - definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || - definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { + else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || + def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || + def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { SuspendedResourcesHolder suspendedResources = suspend(null); if (debugEnabled) { - logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition); + logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def); } try { boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus( - definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); - doBegin(transaction, definition); - prepareSynchronization(status, definition); + def, transaction, true, newSynchronization, debugEnabled, suspendedResources); + doBegin(transaction, def); + prepareSynchronization(status, def); return status; } catch (RuntimeException | Error ex) { @@ -386,12 +384,12 @@ public abstract class AbstractPlatformTransactionManager implements PlatformTran } else { // Create "empty" transaction: no actual transaction, but potentially synchronization. - if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) { + if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) { logger.warn("Custom isolation level specified but no actual transaction initiated; " + - "isolation level will effectively be ignored: " + definition); + "isolation level will effectively be ignored: " + def); } boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); - return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null); + return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null); } }