From cc77b4b9955c01a6904b2d4d6c9b13958246e75e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 5 Feb 2018 22:51:43 +0100 Subject: [PATCH] Explicit notes on isolation level handling in participating transactions Issue: SPR-16463 (cherry picked from commit 0ac117f) --- .../transaction/TransactionDefinition.java | 33 ++++++++------ .../transaction/annotation/Transactional.java | 16 ++++++- .../AbstractPlatformTransactionManager.java | 6 ++- .../support/DefaultTransactionDefinition.java | 45 ++++++++++++++++--- 4 files changed, 77 insertions(+), 23 deletions(-) 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 a45c096e1ba..53a377f5d22 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/TransactionDefinition.java +++ b/spring-tx/src/main/java/org/springframework/transaction/TransactionDefinition.java @@ -203,21 +203,29 @@ public interface TransactionDefinition { /** * Return the isolation level. - *

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

Only makes sense in combination with {@link #PROPAGATION_REQUIRED} - * or {@link #PROPAGATION_REQUIRES_NEW}. + *

Must return one of the {@code ISOLATION_XXX} constants defined on + * {@link TransactionDefinition this interface}. Those constants are designed + * to match the values of the same constants on {@link java.sql.Connection}. + *

Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or + * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started + * transactions. Consider switching the "validateExistingTransactions" flag to + * "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}. * @return the isolation level + * @see #ISOLATION_DEFAULT + * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setValidateExistingTransaction */ int getIsolationLevel(); /** * Return the transaction timeout. *

Must return a number of seconds, or {@link #TIMEOUT_DEFAULT}. - *

Only makes sense in combination with {@link #PROPAGATION_REQUIRED} - * or {@link #PROPAGATION_REQUIRES_NEW}. + *

Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or + * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started + * transactions. *

Note that a transaction manager that does not support timeouts will throw * an exception when given any other timeout than {@link #TIMEOUT_DEFAULT}. * @return the transaction timeout @@ -226,13 +234,12 @@ public interface TransactionDefinition { /** * Return whether to optimize as a read-only transaction. - *

The read-only flag applies to any transaction context, whether - * backed by an actual resource transaction - * ({@link #PROPAGATION_REQUIRED}/{@link #PROPAGATION_REQUIRES_NEW}) or - * operating non-transactionally at the resource level - * ({@link #PROPAGATION_SUPPORTS}). In the latter case, the flag will - * only apply to managed resources within the application, such as a - * Hibernate {@code Session}. + *

The read-only flag applies to any transaction context, whether backed + * by an actual resource transaction ({@link #PROPAGATION_REQUIRED}/ + * {@link #PROPAGATION_REQUIRES_NEW}) or operating non-transactionally at + * the resource level ({@link #PROPAGATION_SUPPORTS}). In the latter case, + * the flag will only apply to managed resources within the application, + * such as a Hibernate {@code Session}. *

This just serves as a hint for the actual transaction subsystem; * it will not necessarily cause failure of write access attempts. * A transaction manager which cannot interpret the read-only hint will diff --git a/spring-tx/src/main/java/org/springframework/transaction/annotation/Transactional.java b/spring-tx/src/main/java/org/springframework/transaction/annotation/Transactional.java index 024e376adaf..d2c762e1c04 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/annotation/Transactional.java +++ b/spring-tx/src/main/java/org/springframework/transaction/annotation/Transactional.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 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. @@ -86,19 +86,30 @@ public @interface Transactional { /** * The transaction isolation level. *

Defaults to {@link Isolation#DEFAULT}. + *

Exclusively designed for use with {@link Propagation#REQUIRED} or + * {@link Propagation#REQUIRES_NEW} since it only applies to newly started + * transactions. Consider switching the "validateExistingTransactions" flag to + * "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. * @see org.springframework.transaction.interceptor.TransactionAttribute#getIsolationLevel() + * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setValidateExistingTransaction */ Isolation isolation() default Isolation.DEFAULT; /** * The timeout for this transaction. *

Defaults to the default timeout of the underlying transaction system. + *

Exclusively designed for use with {@link Propagation#REQUIRED} or + * {@link Propagation#REQUIRES_NEW} since it only applies to newly started + * transactions. * @see org.springframework.transaction.interceptor.TransactionAttribute#getTimeout() */ int timeout() default TransactionDefinition.TIMEOUT_DEFAULT; /** - * {@code true} if the transaction is read-only. + * A boolean flag that can be set to {@code true} if the transaction is + * effectively read-only, allowing for corresponding optimizations at runtime. *

Defaults to {@code false}. *

This just serves as a hint for the actual transaction subsystem; * it will not necessarily cause failure of write access attempts. @@ -106,6 +117,7 @@ public @interface Transactional { * not throw an exception when asked for a read-only transaction * but rather silently ignore the hint. * @see org.springframework.transaction.interceptor.TransactionAttribute#isReadOnly() + * @see org.springframework.transaction.support.TransactionSynchronizationManager#isCurrentTransactionReadOnly() */ boolean readOnly() default false; 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 26f69f0b57b..39e412a12e9 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 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. @@ -214,6 +214,7 @@ public abstract class AbstractPlatformTransactionManager implements PlatformTran *

Default is "false", leniently ignoring inner transaction settings, * simply overriding them with the outer transaction's characteristics. * Switch this flag to "true" in order to enforce strict validation. + * @since 2.5.1 */ public final void setValidateExistingTransaction(boolean validateExistingTransaction) { this.validateExistingTransaction = validateExistingTransaction; @@ -222,6 +223,7 @@ public abstract class AbstractPlatformTransactionManager implements PlatformTran /** * Return whether existing transactions should be validated before participating * in them. + * @since 2.5.1 */ public final boolean isValidateExistingTransaction() { return this.validateExistingTransaction; @@ -284,6 +286,7 @@ public abstract class AbstractPlatformTransactionManager implements PlatformTran * boundary. This allows, for example, to continue unit tests even after an * operation failed and the transaction will never be completed. All transaction * managers will only fail earlier if this flag has explicitly been set to "true". + * @since 2.0 * @see org.springframework.transaction.UnexpectedRollbackException */ public final void setFailEarlyOnGlobalRollbackOnly(boolean failEarlyOnGlobalRollbackOnly) { @@ -293,6 +296,7 @@ public abstract class AbstractPlatformTransactionManager implements PlatformTran /** * Return whether to fail early in case of the transaction being globally marked * as rollback-only. + * @since 2.0 */ public final boolean isFailEarlyOnGlobalRollbackOnly() { return this.failEarlyOnGlobalRollbackOnly; diff --git a/spring-tx/src/main/java/org/springframework/transaction/support/DefaultTransactionDefinition.java b/spring-tx/src/main/java/org/springframework/transaction/support/DefaultTransactionDefinition.java index 830e37ad72e..98788fdc299 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/support/DefaultTransactionDefinition.java +++ b/spring-tx/src/main/java/org/springframework/transaction/support/DefaultTransactionDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 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. @@ -108,7 +108,7 @@ public class DefaultTransactionDefinition implements TransactionDefinition, Seri * Set the propagation behavior by the name of the corresponding constant in * TransactionDefinition, e.g. "PROPAGATION_REQUIRED". * @param constantName name of the constant - * @exception IllegalArgumentException if the supplied value is not resolvable + * @throws IllegalArgumentException if the supplied value is not resolvable * to one of the {@code PROPAGATION_} constants or is {@code null} * @see #setPropagationBehavior * @see #PROPAGATION_REQUIRED @@ -123,8 +123,16 @@ public class DefaultTransactionDefinition implements TransactionDefinition, Seri /** * Set the propagation behavior. Must be one of the propagation constants * in the TransactionDefinition interface. Default is PROPAGATION_REQUIRED. - * @exception IllegalArgumentException if the supplied value is not - * one of the {@code PROPAGATION_} constants + *

Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or + * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started + * transactions. Consider switching the "validateExistingTransactions" flag to + * "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}. + * @throws IllegalArgumentException if the supplied value is not one of the + * {@code PROPAGATION_} constants * @see #PROPAGATION_REQUIRED */ public final void setPropagationBehavior(int propagationBehavior) { @@ -143,7 +151,7 @@ public class DefaultTransactionDefinition implements TransactionDefinition, Seri * Set the isolation level by the name of the corresponding constant in * TransactionDefinition, e.g. "ISOLATION_DEFAULT". * @param constantName name of the constant - * @exception IllegalArgumentException if the supplied value is not resolvable + * @throws IllegalArgumentException if the supplied value is not resolvable * to one of the {@code ISOLATION_} constants or is {@code null} * @see #setIsolationLevel * @see #ISOLATION_DEFAULT @@ -158,8 +166,16 @@ public class DefaultTransactionDefinition implements TransactionDefinition, Seri /** * Set the isolation level. Must be one of the isolation constants * in the TransactionDefinition interface. Default is ISOLATION_DEFAULT. - * @exception IllegalArgumentException if the supplied value is not - * one of the {@code ISOLATION_} constants + *

Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or + * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started + * transactions. Consider switching the "validateExistingTransactions" flag to + * "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}. + * @throws IllegalArgumentException if the supplied value is not one of the + * {@code ISOLATION_} constants * @see #ISOLATION_DEFAULT */ public final void setIsolationLevel(int isolationLevel) { @@ -177,6 +193,11 @@ public class DefaultTransactionDefinition implements TransactionDefinition, Seri /** * Set the timeout to apply, as number of seconds. * Default is TIMEOUT_DEFAULT (-1). + *

Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or + * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started + * transactions. + *

Note that a transaction manager that does not support timeouts will throw + * an exception when given any other timeout than {@link #TIMEOUT_DEFAULT}. * @see #TIMEOUT_DEFAULT */ public final void setTimeout(int timeout) { @@ -194,6 +215,16 @@ public class DefaultTransactionDefinition implements TransactionDefinition, Seri /** * Set whether to optimize as read-only transaction. * Default is "false". + *

The read-only flag applies to any transaction context, whether backed + * by an actual resource transaction ({@link #PROPAGATION_REQUIRED}/ + * {@link #PROPAGATION_REQUIRES_NEW}) or operating non-transactionally at + * the resource level ({@link #PROPAGATION_SUPPORTS}). In the latter case, + * the flag will only apply to managed resources within the application, + * such as a Hibernate {@code Session}. + *

This just serves as a hint for the actual transaction subsystem; + * it will not necessarily cause failure of write access attempts. + * A transaction manager which cannot interpret the read-only hint will + * not throw an exception when asked for a read-only transaction. */ public final void setReadOnly(boolean readOnly) { this.readOnly = readOnly;