Merge branch '5.3.x'
# Conflicts: # spring-tx/src/main/java/org/springframework/transaction/annotation/Transactional.java # spring-tx/src/main/java/org/springframework/transaction/interceptor/RollbackRuleAttribute.java
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -37,17 +37,57 @@ import org.springframework.transaction.TransactionDefinition;
|
||||
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction">Transaction Management</a>
|
||||
* section of the reference manual.
|
||||
*
|
||||
* <p>This annotation type is generally directly comparable to Spring's
|
||||
* <p>This annotation is generally directly comparable to Spring's
|
||||
* {@link org.springframework.transaction.interceptor.RuleBasedTransactionAttribute}
|
||||
* class, and in fact {@link AnnotationTransactionAttributeSource} will directly
|
||||
* convert the data to the latter class, so that Spring's transaction support code
|
||||
* does not have to know about annotations. If no custom rollback rules apply,
|
||||
* the transaction will roll back on {@link RuntimeException} and {@link Error}
|
||||
* but not on checked exceptions.
|
||||
* convert this annotation's attributes to properties in {@code RuleBasedTransactionAttribute},
|
||||
* so that Spring's transaction support code does not have to know about annotations.
|
||||
*
|
||||
* <p>For specific information about the semantics of this annotation's attributes,
|
||||
* consult the {@link org.springframework.transaction.TransactionDefinition} and
|
||||
* {@link org.springframework.transaction.interceptor.TransactionAttribute} javadocs.
|
||||
* <h3>Attribute Semantics</h3>
|
||||
*
|
||||
* <p>If no custom rollback rules are configured in this annotation, the transaction
|
||||
* will roll back on {@link RuntimeException} and {@link Error} but not on checked
|
||||
* exceptions.
|
||||
*
|
||||
* <p>Rollback rules determine if a transaction should be rolled back when a given
|
||||
* exception is thrown, and the rules are based on patterns. A pattern can be a
|
||||
* fully qualified class name or a substring of a fully qualified class name for
|
||||
* an exception type (which must be a subclass of {@code Throwable}), with no
|
||||
* wildcard support at present. For example, a value of
|
||||
* {@code "jakarta.servlet.ServletException"} or {@code "ServletException"} will
|
||||
* match {@code jakarta.servlet.ServletException} and its subclasses.
|
||||
*
|
||||
* <p>Rollback rules may be configured via {@link #rollbackFor}/{@link #noRollbackFor}
|
||||
* and {@link #rollbackForClassName}/{@link #noRollbackForClassName}, which allow
|
||||
* patterns to be specified as {@link Class} references or {@linkplain String
|
||||
* strings}, respectively. When an exception type is specified as a class reference
|
||||
* its fully qualified name will be used as the pattern. Consequently,
|
||||
* {@code @Transactional(rollbackFor = example.CustomException.class)} is equivalent
|
||||
* to {@code @Transactional(rollbackForClassName = "example.CustomException")}.
|
||||
*
|
||||
* <p><strong>WARNING:</strong> You must carefully consider how specific the pattern
|
||||
* is and whether to include package information (which isn't mandatory). For example,
|
||||
* {@code "Exception"} will match nearly anything and will probably hide other
|
||||
* rules. {@code "java.lang.Exception"} would be correct if {@code "Exception"}
|
||||
* were meant to define a rule for all checked exceptions. With more unique
|
||||
* exception names such as {@code "BaseBusinessException"} there is likely no
|
||||
* need to use the fully qualified class name for the exception pattern. Furthermore,
|
||||
* rollback rules may result in unintentional matches for similarly named exceptions
|
||||
* and nested classes. This is due to the fact that a thrown exception is considered
|
||||
* to be a match for a given rollback rule if the name of thrown exception contains
|
||||
* the exception pattern configured for the rollback rule. For example, given a
|
||||
* rule configured to match on {@code com.example.CustomException}, that rule
|
||||
* would match against an exception named
|
||||
* {@code com.example.CustomExceptionV2} (an exception in the same package as
|
||||
* {@code CustomException} but with an additional suffix) or an exception named
|
||||
* {@code com.example.CustomException$AnotherException}
|
||||
* (an exception declared as a nested class in {@code CustomException}).
|
||||
*
|
||||
* <p>For specific information about the semantics of other attributes in this
|
||||
* annotation, consult the {@link org.springframework.transaction.TransactionDefinition}
|
||||
* and {@link org.springframework.transaction.interceptor.TransactionAttribute} javadocs.
|
||||
*
|
||||
* <h3>Transaction Management</h3>
|
||||
*
|
||||
* <p>This annotation commonly works with thread-bound transactions managed by a
|
||||
* {@link org.springframework.transaction.PlatformTransactionManager}, exposing a
|
||||
@@ -167,37 +207,33 @@ public @interface Transactional {
|
||||
boolean readOnly() default false;
|
||||
|
||||
/**
|
||||
* Defines zero (0) or more exception {@link Class classes}, which must be
|
||||
* Defines zero (0) or more exception {@linkplain Class classes}, which must be
|
||||
* subclasses of {@link Throwable}, indicating which exception types must cause
|
||||
* a transaction rollback.
|
||||
* <p>By default, a transaction will be rolling back on {@link RuntimeException}
|
||||
* <p>By default, a transaction will be rolled back on {@link RuntimeException}
|
||||
* and {@link Error} but not on checked exceptions (business exceptions). See
|
||||
* {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)}
|
||||
* for a detailed explanation.
|
||||
* <p>This is the preferred way to construct a rollback rule (in contrast to
|
||||
* {@link #rollbackForClassName}), matching the exception class and its subclasses.
|
||||
* <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)}.
|
||||
* {@link #rollbackForClassName}), matching the exception type, its subclasses,
|
||||
* and its nested classes. See the {@linkplain Transactional class-level javadocs}
|
||||
* for further details on rollback rule semantics and warnings regarding possible
|
||||
* unintentional matches.
|
||||
* @see #rollbackForClassName
|
||||
* @see org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class)
|
||||
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)
|
||||
*/
|
||||
Class<? extends Throwable>[] rollbackFor() default {};
|
||||
|
||||
/**
|
||||
* Defines zero (0) or more exception names (for exceptions which must be a
|
||||
* Defines zero (0) or more exception name patterns (for exceptions which must be a
|
||||
* subclass of {@link Throwable}), indicating which exception types must cause
|
||||
* a transaction rollback.
|
||||
* <p>This can be a substring of a fully qualified class name, with no wildcard
|
||||
* support at present. For example, a value of {@code "ServletException"} would
|
||||
* match {@code jakarta.servlet.ServletException} and its subclasses.
|
||||
* <p><b>NB:</b> Consider carefully how specific the pattern is and whether
|
||||
* to include package information (which isn't mandatory). For example,
|
||||
* {@code "Exception"} will match nearly anything and will probably hide other
|
||||
* rules. {@code "java.lang.Exception"} would be correct if {@code "Exception"}
|
||||
* were meant to define a rule for all checked exceptions. With more unusual
|
||||
* {@link Exception} names such as {@code "BaseBusinessException"} there is no
|
||||
* need to use a FQN.
|
||||
* <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(String exceptionName)}.
|
||||
* <p>See the {@linkplain Transactional class-level javadocs} for further details
|
||||
* on rollback rule semantics, patterns, and warnings regarding possible
|
||||
* unintentional matches.
|
||||
* @see #rollbackFor
|
||||
* @see org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(String)
|
||||
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)
|
||||
*/
|
||||
String[] rollbackForClassName() default {};
|
||||
@@ -206,23 +242,26 @@ public @interface Transactional {
|
||||
* Defines zero (0) or more exception {@link Class Classes}, which must be
|
||||
* subclasses of {@link Throwable}, indicating which exception types must
|
||||
* <b>not</b> cause a transaction rollback.
|
||||
* <p>This is the preferred way to construct a rollback rule (in contrast
|
||||
* to {@link #noRollbackForClassName}), matching the exception class and
|
||||
* its subclasses.
|
||||
* <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(Class clazz)}.
|
||||
* <p>This is the preferred way to construct a rollback rule (in contrast to
|
||||
* {@link #noRollbackForClassName}), matching the exception type, its subclasses,
|
||||
* and its nested classes. See the {@linkplain Transactional class-level javadocs}
|
||||
* for further details on rollback rule semantics and warnings regarding possible
|
||||
* unintentional matches.
|
||||
* @see #noRollbackForClassName
|
||||
* @see org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(Class)
|
||||
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)
|
||||
*/
|
||||
Class<? extends Throwable>[] noRollbackFor() default {};
|
||||
|
||||
/**
|
||||
* Defines zero (0) or more exception names (for exceptions which must be a
|
||||
* Defines zero (0) or more exception name patterns (for exceptions which must be a
|
||||
* subclass of {@link Throwable}) indicating which exception types must <b>not</b>
|
||||
* cause a transaction rollback.
|
||||
* <p>See the description of {@link #rollbackForClassName} for further
|
||||
* information on how the specified names are treated.
|
||||
* <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(String exceptionName)}.
|
||||
* <p>See the {@linkplain Transactional class-level javadocs} for further details
|
||||
* on rollback rule semantics, patterns, and warnings regarding possible
|
||||
* unintentional matches.
|
||||
* @see #noRollbackFor
|
||||
* @see org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(String)
|
||||
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)
|
||||
*/
|
||||
String[] noRollbackForClassName() default {};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -21,6 +21,7 @@ package org.springframework.transaction.interceptor;
|
||||
* to the {@code RollbackRuleAttribute} superclass.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Sam Brannen
|
||||
* @since 09.04.2003
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@@ -28,22 +29,28 @@ public class NoRollbackRuleAttribute extends RollbackRuleAttribute {
|
||||
|
||||
/**
|
||||
* Create a new instance of the {@code NoRollbackRuleAttribute} class
|
||||
* for the supplied {@link Throwable} class.
|
||||
* @param clazz the {@code Throwable} class
|
||||
* for the given {@code exceptionType}.
|
||||
* @param exceptionType exception type; must be {@link Throwable} or a subclass
|
||||
* of {@code Throwable}
|
||||
* @throws IllegalArgumentException if the supplied {@code exceptionType} is
|
||||
* not a {@code Throwable} type or is {@code null}
|
||||
* @see RollbackRuleAttribute#RollbackRuleAttribute(Class)
|
||||
*/
|
||||
public NoRollbackRuleAttribute(Class<?> clazz) {
|
||||
super(clazz);
|
||||
public NoRollbackRuleAttribute(Class<?> exceptionType) {
|
||||
super(exceptionType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the {@code NoRollbackRuleAttribute} class
|
||||
* for the supplied {@code exceptionName}.
|
||||
* @param exceptionName the exception name pattern
|
||||
* for the supplied {@code exceptionPattern}.
|
||||
* @param exceptionPattern the exception name pattern; can also be a fully
|
||||
* package-qualified class name
|
||||
* @throws IllegalArgumentException if the supplied {@code exceptionPattern}
|
||||
* is {@code null} or empty
|
||||
* @see RollbackRuleAttribute#RollbackRuleAttribute(String)
|
||||
*/
|
||||
public NoRollbackRuleAttribute(String exceptionName) {
|
||||
super(exceptionName);
|
||||
public NoRollbackRuleAttribute(String exceptionPattern) {
|
||||
super(exceptionPattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -22,13 +22,29 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Rule determining whether or not a given exception (and any subclasses)
|
||||
* should cause a rollback.
|
||||
* Rule determining whether or not a given exception should cause a rollback.
|
||||
*
|
||||
* <p>Multiple such rules can be applied to determine whether a transaction
|
||||
* should commit or rollback after an exception has been thrown.
|
||||
*
|
||||
* <p>Each rule is based on an exception pattern which can be a fully qualified
|
||||
* class name or a substring of a fully qualified class name for an exception
|
||||
* type (which must be a subclass of {@code Throwable}), with no wildcard support
|
||||
* at present. For example, a value of {@code "jakarta.servlet.ServletException"}
|
||||
* or {@code "ServletException"} would match {@code jakarta.servlet.ServletException}
|
||||
* and its subclasses.
|
||||
*
|
||||
* <p>An exception pattern can be specified as a {@link Class} reference or a
|
||||
* {@link String} in {@link #RollbackRuleAttribute(Class)} and
|
||||
* {@link #RollbackRuleAttribute(String)}, respectively. When an exception type
|
||||
* is specified as a class reference its fully qualified name will be used as the
|
||||
* pattern. See the javadocs for
|
||||
* {@link org.springframework.transaction.annotation.Transactional @Transactional}
|
||||
* for further details on rollback rule semantics, patterns, and warnings regarding
|
||||
* possible unintentional matches.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Sam Brannen
|
||||
* @since 09.04.2003
|
||||
* @see NoRollbackRuleAttribute
|
||||
*/
|
||||
@@ -36,7 +52,7 @@ import org.springframework.util.Assert;
|
||||
public class RollbackRuleAttribute implements Serializable{
|
||||
|
||||
/**
|
||||
* The {@link RollbackRuleAttribute rollback rule} for
|
||||
* The {@linkplain RollbackRuleAttribute rollback rule} for
|
||||
* {@link RuntimeException RuntimeExceptions}.
|
||||
*/
|
||||
public static final RollbackRuleAttribute ROLLBACK_ON_RUNTIME_EXCEPTIONS =
|
||||
@@ -48,79 +64,90 @@ public class RollbackRuleAttribute implements Serializable{
|
||||
* This way does multiple string comparisons, but how often do we decide
|
||||
* whether to roll back a transaction following an exception?
|
||||
*/
|
||||
private final String exceptionName;
|
||||
private final String exceptionPattern;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance of the {@code RollbackRuleAttribute} class.
|
||||
* Create a new instance of the {@code RollbackRuleAttribute} class
|
||||
* for the given {@code exceptionType}.
|
||||
* <p>This is the preferred way to construct a rollback rule that matches
|
||||
* the supplied {@link Exception} class, its subclasses, and its nested classes.
|
||||
* @param clazz throwable class; must be {@link Throwable} or a subclass
|
||||
* the supplied exception type, its subclasses, and its nested classes.
|
||||
* <p>See the javadocs for
|
||||
* {@link org.springframework.transaction.annotation.Transactional @Transactional}
|
||||
* for further details on rollback rule semantics, patterns, and warnings regarding
|
||||
* possible unintentional matches.
|
||||
* @param exceptionType exception type; must be {@link Throwable} or a subclass
|
||||
* of {@code Throwable}
|
||||
* @throws IllegalArgumentException if the supplied {@code clazz} is
|
||||
* @throws IllegalArgumentException if the supplied {@code exceptionType} is
|
||||
* not a {@code Throwable} type or is {@code null}
|
||||
*/
|
||||
public RollbackRuleAttribute(Class<?> clazz) {
|
||||
Assert.notNull(clazz, "'clazz' cannot be null");
|
||||
if (!Throwable.class.isAssignableFrom(clazz)) {
|
||||
public RollbackRuleAttribute(Class<?> exceptionType) {
|
||||
Assert.notNull(exceptionType, "'exceptionType' cannot be null");
|
||||
if (!Throwable.class.isAssignableFrom(exceptionType)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot construct rollback rule from [" + clazz.getName() + "]: it's not a Throwable");
|
||||
"Cannot construct rollback rule from [" + exceptionType.getName() + "]: it's not a Throwable");
|
||||
}
|
||||
this.exceptionName = clazz.getName();
|
||||
this.exceptionPattern = exceptionType.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the {@code RollbackRuleAttribute} class
|
||||
* for the given {@code exceptionName}.
|
||||
* <p>This can be a substring, with no wildcard support at present. A value
|
||||
* of "ServletException" would match
|
||||
* {@code jakarta.servlet.ServletException} and subclasses, for example.
|
||||
* <p><b>NB:</b> Consider carefully how specific the pattern is, and
|
||||
* whether to include package information (which is not mandatory). For
|
||||
* example, "Exception" will match nearly anything, and will probably hide
|
||||
* other rules. "java.lang.Exception" would be correct if "Exception" was
|
||||
* meant to define a rule for all checked exceptions. With more unusual
|
||||
* exception names such as "BaseBusinessException" there's no need to use a
|
||||
* fully package-qualified name.
|
||||
* @param exceptionName the exception name pattern; can also be a fully
|
||||
* for the given {@code exceptionPattern}.
|
||||
* <p>See the javadocs for
|
||||
* {@link org.springframework.transaction.annotation.Transactional @Transactional}
|
||||
* for further details on rollback rule semantics, patterns, and warnings regarding
|
||||
* possible unintentional matches.
|
||||
* @param exceptionPattern the exception name pattern; can also be a fully
|
||||
* package-qualified class name
|
||||
* @throws IllegalArgumentException if the supplied
|
||||
* {@code exceptionName} is {@code null} or empty
|
||||
* @throws IllegalArgumentException if the supplied {@code exceptionPattern}
|
||||
* is {@code null} or empty
|
||||
*/
|
||||
public RollbackRuleAttribute(String exceptionName) {
|
||||
Assert.hasText(exceptionName, "'exceptionName' cannot be null or empty");
|
||||
this.exceptionName = exceptionName;
|
||||
public RollbackRuleAttribute(String exceptionPattern) {
|
||||
Assert.hasText(exceptionPattern, "'exceptionPattern' cannot be null or empty");
|
||||
this.exceptionPattern = exceptionPattern;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the pattern for the exception name.
|
||||
* Get the configured exception name pattern that this rule uses for matching.
|
||||
* @see #getDepth(Throwable)
|
||||
*/
|
||||
public String getExceptionName() {
|
||||
return this.exceptionName;
|
||||
return this.exceptionPattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the depth of the superclass matching.
|
||||
* <p>{@code 0} means {@code ex} matches exactly. Returns
|
||||
* {@code -1} if there is no match. Otherwise, returns depth with the
|
||||
* lowest depth winning.
|
||||
* Return the depth of the superclass matching, with the following semantics.
|
||||
* <ul>
|
||||
* <li>{@code -1} means this rule does not match the supplied {@code exception}.</li>
|
||||
* <li>{@code 0} means this rule matches the supplied {@code exception} directly.</li>
|
||||
* <li>Any other positive value means this rule matches the supplied {@code exception}
|
||||
* within the superclass hierarchy, where the value is the number of levels in the
|
||||
* class hierarchy between the supplied {@code exception} and the exception against
|
||||
* which this rule matches directly.</li>
|
||||
* </ul>
|
||||
* <p>When comparing roll back rules that match against a given exception, a rule
|
||||
* with a lower matching depth wins. For example, a direct match ({@code depth == 0})
|
||||
* wins over a match in the superclass hierarchy ({@code depth > 0}).
|
||||
* <p>A match against a nested exception type or similarly named exception type
|
||||
* will return a depth signifying a match at the corresponding level in the
|
||||
* class hierarchy as if there had been a direct match.
|
||||
*/
|
||||
public int getDepth(Throwable ex) {
|
||||
return getDepth(ex.getClass(), 0);
|
||||
public int getDepth(Throwable exception) {
|
||||
return getDepth(exception.getClass(), 0);
|
||||
}
|
||||
|
||||
|
||||
private int getDepth(Class<?> exceptionClass, int depth) {
|
||||
if (exceptionClass.getName().contains(this.exceptionName)) {
|
||||
private int getDepth(Class<?> exceptionType, int depth) {
|
||||
if (exceptionType.getName().contains(this.exceptionPattern)) {
|
||||
// Found it!
|
||||
return depth;
|
||||
}
|
||||
// If we've gone as far as we can go and haven't found it...
|
||||
if (exceptionClass == Throwable.class) {
|
||||
if (exceptionType == Throwable.class) {
|
||||
return -1;
|
||||
}
|
||||
return getDepth(exceptionClass.getSuperclass(), depth + 1);
|
||||
return getDepth(exceptionType.getSuperclass(), depth + 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -132,17 +159,17 @@ public class RollbackRuleAttribute implements Serializable{
|
||||
if (!(other instanceof RollbackRuleAttribute rhs)) {
|
||||
return false;
|
||||
}
|
||||
return this.exceptionName.equals(rhs.exceptionName);
|
||||
return this.exceptionPattern.equals(rhs.exceptionPattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.exceptionName.hashCode();
|
||||
return this.exceptionPattern.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RollbackRuleAttribute with pattern [" + this.exceptionName + "]";
|
||||
return "RollbackRuleAttribute with pattern [" + this.exceptionPattern + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -25,7 +25,13 @@ import org.springframework.core.NestedRuntimeException;
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class MyRuntimeException extends NestedRuntimeException {
|
||||
|
||||
public MyRuntimeException() {
|
||||
super("");
|
||||
}
|
||||
|
||||
public MyRuntimeException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.transaction.interceptor;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
@@ -36,65 +37,105 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
*/
|
||||
class RollbackRuleAttributeTests {
|
||||
|
||||
@Test
|
||||
void constructorArgumentMustBeThrowableClassWithNonThrowableType() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new RollbackRuleAttribute(Object.class));
|
||||
@Nested
|
||||
class ExceptionPatternTests {
|
||||
|
||||
@Test
|
||||
void constructorPreconditions() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new RollbackRuleAttribute((String) null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void notFound() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(IOException.class.getName());
|
||||
assertThat(rr.getDepth(new MyRuntimeException())).isEqualTo(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void alwaysFoundForThrowable() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(Throwable.class.getName());
|
||||
assertThat(rr.getDepth(new MyRuntimeException())).isGreaterThan(0);
|
||||
assertThat(rr.getDepth(new IOException())).isGreaterThan(0);
|
||||
assertThat(rr.getDepth(new FatalBeanException(null, null))).isGreaterThan(0);
|
||||
assertThat(rr.getDepth(new RuntimeException())).isGreaterThan(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void foundImmediatelyWhenDirectMatch() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(Exception.class.getName());
|
||||
assertThat(rr.getDepth(new Exception())).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void foundImmediatelyWhenExceptionThrownIsNestedTypeOfRegisteredException() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(EnclosingException.class.getName());
|
||||
assertThat(rr.getDepth(new EnclosingException.NestedException())).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void foundImmediatelyWhenNameOfExceptionThrownStartsWithNameOfRegisteredException() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(MyException.class.getName());
|
||||
assertThat(rr.getDepth(new MyException2())).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void foundInSuperclassHierarchy() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(Exception.class.getName());
|
||||
// Exception -> RuntimeException -> NestedRuntimeException -> MyRuntimeException
|
||||
assertThat(rr.getDepth(new MyRuntimeException())).isEqualTo(3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorArgumentMustBeThrowableClassWithNullThrowableType() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new RollbackRuleAttribute((Class<?>) null));
|
||||
}
|
||||
@Nested
|
||||
class ExceptionTypeTests {
|
||||
|
||||
@Test
|
||||
void constructorArgumentMustBeStringWithNull() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new RollbackRuleAttribute((String) null));
|
||||
}
|
||||
@Test
|
||||
void constructorPreconditions() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new RollbackRuleAttribute(Object.class));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new RollbackRuleAttribute((Class<?>) null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void notFound() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(IOException.class);
|
||||
assertThat(rr.getDepth(new MyRuntimeException(""))).isEqualTo(-1);
|
||||
}
|
||||
@Test
|
||||
void notFound() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(IOException.class);
|
||||
assertThat(rr.getDepth(new MyRuntimeException())).isEqualTo(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void foundImmediatelyWithString() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(Exception.class.getName());
|
||||
assertThat(rr.getDepth(new Exception())).isEqualTo(0);
|
||||
}
|
||||
@Test
|
||||
void alwaysFoundForThrowable() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(Throwable.class);
|
||||
assertThat(rr.getDepth(new MyRuntimeException())).isGreaterThan(0);
|
||||
assertThat(rr.getDepth(new IOException())).isGreaterThan(0);
|
||||
assertThat(rr.getDepth(new FatalBeanException(null, null))).isGreaterThan(0);
|
||||
assertThat(rr.getDepth(new RuntimeException())).isGreaterThan(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void foundImmediatelyWithClass() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(Exception.class);
|
||||
assertThat(rr.getDepth(new Exception())).isEqualTo(0);
|
||||
}
|
||||
@Test
|
||||
void foundImmediatelyWhenDirectMatch() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(Exception.class);
|
||||
assertThat(rr.getDepth(new Exception())).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void foundInSuperclassHierarchy() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(Exception.class);
|
||||
// Exception -> RuntimeException -> NestedRuntimeException -> MyRuntimeException
|
||||
assertThat(rr.getDepth(new MyRuntimeException(""))).isEqualTo(3);
|
||||
}
|
||||
@Test
|
||||
void foundImmediatelyWhenExceptionThrownIsNestedTypeOfRegisteredException() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(EnclosingException.class);
|
||||
assertThat(rr.getDepth(new EnclosingException.NestedException())).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void alwaysFoundForThrowable() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(Throwable.class);
|
||||
assertThat(rr.getDepth(new MyRuntimeException(""))).isGreaterThan(0);
|
||||
assertThat(rr.getDepth(new IOException())).isGreaterThan(0);
|
||||
assertThat(rr.getDepth(new FatalBeanException(null, null))).isGreaterThan(0);
|
||||
assertThat(rr.getDepth(new RuntimeException())).isGreaterThan(0);
|
||||
}
|
||||
@Test
|
||||
void foundImmediatelyWhenNameOfExceptionThrownStartsWithNameOfRegisteredException() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(MyException.class);
|
||||
assertThat(rr.getDepth(new MyException2())).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void foundNestedExceptionInEnclosingException() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(EnclosingException.class);
|
||||
assertThat(rr.getDepth(new EnclosingException.NestedException())).isEqualTo(0);
|
||||
}
|
||||
@Test
|
||||
void foundInSuperclassHierarchy() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(Exception.class);
|
||||
// Exception -> RuntimeException -> NestedRuntimeException -> MyRuntimeException
|
||||
assertThat(rr.getDepth(new MyRuntimeException())).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void foundWhenNameOfExceptionThrownStartsWithTheNameOfTheRegisteredExceptionType() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(MyException.class);
|
||||
assertThat(rr.getDepth(new MyException2())).isEqualTo(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -35,13 +35,13 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Chris Beams
|
||||
* @since 09.04.2003
|
||||
*/
|
||||
public class RuleBasedTransactionAttributeTests {
|
||||
class RuleBasedTransactionAttributeTests {
|
||||
|
||||
@Test
|
||||
public void testDefaultRule() {
|
||||
void defaultRule() {
|
||||
RuleBasedTransactionAttribute rta = new RuleBasedTransactionAttribute();
|
||||
assertThat(rta.rollbackOn(new RuntimeException())).isTrue();
|
||||
assertThat(rta.rollbackOn(new MyRuntimeException(""))).isTrue();
|
||||
assertThat(rta.rollbackOn(new MyRuntimeException())).isTrue();
|
||||
assertThat(rta.rollbackOn(new Exception())).isFalse();
|
||||
assertThat(rta.rollbackOn(new IOException())).isFalse();
|
||||
}
|
||||
@@ -50,20 +50,20 @@ public class RuleBasedTransactionAttributeTests {
|
||||
* Test one checked exception that should roll back.
|
||||
*/
|
||||
@Test
|
||||
public void testRuleForRollbackOnChecked() {
|
||||
void ruleForRollbackOnChecked() {
|
||||
List<RollbackRuleAttribute> list = new ArrayList<>();
|
||||
list.add(new RollbackRuleAttribute(IOException.class.getName()));
|
||||
RuleBasedTransactionAttribute rta = new RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED, list);
|
||||
|
||||
assertThat(rta.rollbackOn(new RuntimeException())).isTrue();
|
||||
assertThat(rta.rollbackOn(new MyRuntimeException(""))).isTrue();
|
||||
assertThat(rta.rollbackOn(new MyRuntimeException())).isTrue();
|
||||
assertThat(rta.rollbackOn(new Exception())).isFalse();
|
||||
// Check that default behaviour is overridden
|
||||
assertThat(rta.rollbackOn(new IOException())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRuleForCommitOnUnchecked() {
|
||||
void ruleForCommitOnUnchecked() {
|
||||
List<RollbackRuleAttribute> list = new ArrayList<>();
|
||||
list.add(new NoRollbackRuleAttribute(MyRuntimeException.class.getName()));
|
||||
list.add(new RollbackRuleAttribute(IOException.class.getName()));
|
||||
@@ -71,14 +71,14 @@ public class RuleBasedTransactionAttributeTests {
|
||||
|
||||
assertThat(rta.rollbackOn(new RuntimeException())).isTrue();
|
||||
// Check default behaviour is overridden
|
||||
assertThat(rta.rollbackOn(new MyRuntimeException(""))).isFalse();
|
||||
assertThat(rta.rollbackOn(new MyRuntimeException())).isFalse();
|
||||
assertThat(rta.rollbackOn(new Exception())).isFalse();
|
||||
// Check that default behaviour is overridden
|
||||
assertThat(rta.rollbackOn(new IOException())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRuleForSelectiveRollbackOnCheckedWithString() {
|
||||
void ruleForSelectiveRollbackOnCheckedWithString() {
|
||||
List<RollbackRuleAttribute> l = new ArrayList<>();
|
||||
l.add(new RollbackRuleAttribute(java.rmi.RemoteException.class.getName()));
|
||||
RuleBasedTransactionAttribute rta = new RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED, l);
|
||||
@@ -86,7 +86,7 @@ public class RuleBasedTransactionAttributeTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRuleForSelectiveRollbackOnCheckedWithClass() {
|
||||
void ruleForSelectiveRollbackOnCheckedWithClass() {
|
||||
List<RollbackRuleAttribute> l = Collections.singletonList(new RollbackRuleAttribute(RemoteException.class));
|
||||
RuleBasedTransactionAttribute rta = new RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED, l);
|
||||
doTestRuleForSelectiveRollbackOnChecked(rta);
|
||||
@@ -105,7 +105,7 @@ public class RuleBasedTransactionAttributeTests {
|
||||
* when Exception prompts a rollback.
|
||||
*/
|
||||
@Test
|
||||
public void testRuleForCommitOnSubclassOfChecked() {
|
||||
void ruleForCommitOnSubclassOfChecked() {
|
||||
List<RollbackRuleAttribute> list = new ArrayList<>();
|
||||
// Note that it's important to ensure that we have this as
|
||||
// a FQN: otherwise it will match everything!
|
||||
@@ -120,20 +120,20 @@ public class RuleBasedTransactionAttributeTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRollbackNever() {
|
||||
void rollbackNever() {
|
||||
List<RollbackRuleAttribute> list = new ArrayList<>();
|
||||
list.add(new NoRollbackRuleAttribute("Throwable"));
|
||||
RuleBasedTransactionAttribute rta = new RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED, list);
|
||||
|
||||
assertThat(rta.rollbackOn(new Throwable())).isFalse();
|
||||
assertThat(rta.rollbackOn(new RuntimeException())).isFalse();
|
||||
assertThat(rta.rollbackOn(new MyRuntimeException(""))).isFalse();
|
||||
assertThat(rta.rollbackOn(new MyRuntimeException())).isFalse();
|
||||
assertThat(rta.rollbackOn(new Exception())).isFalse();
|
||||
assertThat(rta.rollbackOn(new IOException())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToStringMatchesEditor() {
|
||||
void toStringMatchesEditor() {
|
||||
List<RollbackRuleAttribute> list = new ArrayList<>();
|
||||
list.add(new NoRollbackRuleAttribute("Throwable"));
|
||||
RuleBasedTransactionAttribute rta = new RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED, list);
|
||||
@@ -144,7 +144,7 @@ public class RuleBasedTransactionAttributeTests {
|
||||
|
||||
assertThat(rta.rollbackOn(new Throwable())).isFalse();
|
||||
assertThat(rta.rollbackOn(new RuntimeException())).isFalse();
|
||||
assertThat(rta.rollbackOn(new MyRuntimeException(""))).isFalse();
|
||||
assertThat(rta.rollbackOn(new MyRuntimeException())).isFalse();
|
||||
assertThat(rta.rollbackOn(new Exception())).isFalse();
|
||||
assertThat(rta.rollbackOn(new IOException())).isFalse();
|
||||
}
|
||||
@@ -153,7 +153,7 @@ public class RuleBasedTransactionAttributeTests {
|
||||
* See <a href="https://forum.springframework.org/showthread.php?t=41350">this forum post</a>.
|
||||
*/
|
||||
@Test
|
||||
public void testConflictingRulesToDetermineExactContract() {
|
||||
void conflictingRulesToDetermineExactContract() {
|
||||
List<RollbackRuleAttribute> list = new ArrayList<>();
|
||||
list.add(new NoRollbackRuleAttribute(MyBusinessWarningException.class));
|
||||
list.add(new RollbackRuleAttribute(MyBusinessException.class));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.transaction.interceptor;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -27,72 +26,65 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests to check conversion from String to TransactionAttribute.
|
||||
* Tests to check conversion from String to TransactionAttribute using
|
||||
* a {@link TransactionAttributeEditor}.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 26.04.2003
|
||||
*/
|
||||
public class TransactionAttributeEditorTests {
|
||||
class TransactionAttributeEditorTests {
|
||||
|
||||
private final TransactionAttributeEditor pe = new TransactionAttributeEditor();
|
||||
|
||||
|
||||
@Test
|
||||
public void testNull() {
|
||||
TransactionAttributeEditor pe = new TransactionAttributeEditor();
|
||||
void nullText() {
|
||||
pe.setAsText(null);
|
||||
TransactionAttribute ta = (TransactionAttribute) pe.getValue();
|
||||
assertThat(ta == null).isTrue();
|
||||
assertThat(pe.getValue()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyString() {
|
||||
TransactionAttributeEditor pe = new TransactionAttributeEditor();
|
||||
void emptyString() {
|
||||
pe.setAsText("");
|
||||
TransactionAttribute ta = (TransactionAttribute) pe.getValue();
|
||||
assertThat(ta == null).isTrue();
|
||||
assertThat(pe.getValue()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidPropagationCodeOnly() {
|
||||
TransactionAttributeEditor pe = new TransactionAttributeEditor();
|
||||
void validPropagationCodeOnly() {
|
||||
pe.setAsText("PROPAGATION_REQUIRED");
|
||||
TransactionAttribute ta = (TransactionAttribute) pe.getValue();
|
||||
assertThat(ta != null).isTrue();
|
||||
assertThat(ta.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED).isTrue();
|
||||
assertThat(ta.getIsolationLevel() == TransactionDefinition.ISOLATION_DEFAULT).isTrue();
|
||||
boolean condition = !ta.isReadOnly();
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(ta).isNotNull();
|
||||
assertThat(ta.getPropagationBehavior()).isEqualTo(TransactionDefinition.PROPAGATION_REQUIRED);
|
||||
assertThat(ta.getIsolationLevel()).isEqualTo(TransactionDefinition.ISOLATION_DEFAULT);
|
||||
assertThat(ta.isReadOnly()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidPropagationCodeOnly() {
|
||||
TransactionAttributeEditor pe = new TransactionAttributeEditor();
|
||||
void invalidPropagationCodeOnly() {
|
||||
// should have failed with bogus propagation code
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
pe.setAsText("XXPROPAGATION_REQUIRED"));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> pe.setAsText("XXPROPAGATION_REQUIRED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidPropagationCodeAndIsolationCode() {
|
||||
TransactionAttributeEditor pe = new TransactionAttributeEditor();
|
||||
void validPropagationCodeAndIsolationCode() {
|
||||
pe.setAsText("PROPAGATION_REQUIRED, ISOLATION_READ_UNCOMMITTED");
|
||||
TransactionAttribute ta = (TransactionAttribute) pe.getValue();
|
||||
assertThat(ta != null).isTrue();
|
||||
assertThat(ta.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED).isTrue();
|
||||
assertThat(ta.getIsolationLevel() == TransactionDefinition.ISOLATION_READ_UNCOMMITTED).isTrue();
|
||||
assertThat(ta).isNotNull();
|
||||
assertThat(ta.getPropagationBehavior()).isEqualTo(TransactionDefinition.PROPAGATION_REQUIRED);
|
||||
assertThat(ta.getIsolationLevel()).isEqualTo(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidPropagationAndIsolationCodesAndInvalidRollbackRule() {
|
||||
TransactionAttributeEditor pe = new TransactionAttributeEditor();
|
||||
void validPropagationAndIsolationCodesAndInvalidRollbackRule() {
|
||||
// should fail with bogus rollback rule
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
pe.setAsText("PROPAGATION_REQUIRED,ISOLATION_READ_UNCOMMITTED,XXX"));
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> pe.setAsText("PROPAGATION_REQUIRED,ISOLATION_READ_UNCOMMITTED,XXX"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidPropagationCodeAndIsolationCodeAndRollbackRules1() {
|
||||
TransactionAttributeEditor pe = new TransactionAttributeEditor();
|
||||
void validPropagationCodeAndIsolationCodeAndRollbackRules1() {
|
||||
pe.setAsText("PROPAGATION_MANDATORY,ISOLATION_REPEATABLE_READ,timeout_10,-IOException,+MyRuntimeException");
|
||||
TransactionAttribute ta = (TransactionAttribute) pe.getValue();
|
||||
assertThat(ta).isNotNull();
|
||||
@@ -104,13 +96,11 @@ public class TransactionAttributeEditorTests {
|
||||
assertThat(ta.rollbackOn(new Exception())).isFalse();
|
||||
// Check for our bizarre customized rollback rules
|
||||
assertThat(ta.rollbackOn(new IOException())).isTrue();
|
||||
boolean condition = !ta.rollbackOn(new MyRuntimeException(""));
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(ta.rollbackOn(new MyRuntimeException())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidPropagationCodeAndIsolationCodeAndRollbackRules2() {
|
||||
TransactionAttributeEditor pe = new TransactionAttributeEditor();
|
||||
void validPropagationCodeAndIsolationCodeAndRollbackRules2() {
|
||||
pe.setAsText("+IOException,readOnly,ISOLATION_READ_COMMITTED,-MyRuntimeException,PROPAGATION_SUPPORTS");
|
||||
TransactionAttribute ta = (TransactionAttribute) pe.getValue();
|
||||
assertThat(ta).isNotNull();
|
||||
@@ -122,18 +112,17 @@ public class TransactionAttributeEditorTests {
|
||||
assertThat(ta.rollbackOn(new Exception())).isFalse();
|
||||
// Check for our bizarre customized rollback rules
|
||||
assertThat(ta.rollbackOn(new IOException())).isFalse();
|
||||
assertThat(ta.rollbackOn(new MyRuntimeException(""))).isTrue();
|
||||
assertThat(ta.rollbackOn(new MyRuntimeException())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultTransactionAttributeToString() {
|
||||
void defaultTransactionAttributeToString() {
|
||||
DefaultTransactionAttribute source = new DefaultTransactionAttribute();
|
||||
source.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
|
||||
source.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
|
||||
source.setTimeout(10);
|
||||
source.setReadOnly(true);
|
||||
|
||||
TransactionAttributeEditor pe = new TransactionAttributeEditor();
|
||||
pe.setAsText(source.toString());
|
||||
TransactionAttribute ta = (TransactionAttribute) pe.getValue();
|
||||
assertThat(source).isEqualTo(ta);
|
||||
@@ -151,7 +140,7 @@ public class TransactionAttributeEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRuleBasedTransactionAttributeToString() {
|
||||
void ruleBasedTransactionAttributeToString() {
|
||||
RuleBasedTransactionAttribute source = new RuleBasedTransactionAttribute();
|
||||
source.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
|
||||
source.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
|
||||
@@ -160,7 +149,6 @@ public class TransactionAttributeEditorTests {
|
||||
source.getRollbackRules().add(new RollbackRuleAttribute("IllegalArgumentException"));
|
||||
source.getRollbackRules().add(new NoRollbackRuleAttribute("IllegalStateException"));
|
||||
|
||||
TransactionAttributeEditor pe = new TransactionAttributeEditor();
|
||||
pe.setAsText(source.toString());
|
||||
TransactionAttribute ta = (TransactionAttribute) pe.getValue();
|
||||
assertThat(source).isEqualTo(ta);
|
||||
|
||||
@@ -1015,9 +1015,11 @@ to ensure completion and buffer results in the calling code.
|
||||
==== Rolling Back a Declarative Transaction
|
||||
|
||||
The previous section outlined the basics of how to specify transactional settings for
|
||||
classes, typically service layer classes, declaratively in your application. This
|
||||
section describes how you can control the rollback of transactions in a simple,
|
||||
declarative fashion.
|
||||
classes, typically service layer classes, declaratively in your application. This section
|
||||
describes how you can control the rollback of transactions in a simple, declarative
|
||||
fashion in XML configuration. For details on controlling rollback semantics declaratively
|
||||
with the `@Transactional` annotation, see
|
||||
<<transaction-declarative-attransactional-settings>>.
|
||||
|
||||
The recommended way to indicate to the Spring Framework's transaction infrastructure
|
||||
that a transaction's work is to be rolled back is to throw an `Exception` from code that
|
||||
@@ -1027,14 +1029,59 @@ the call stack and makes a determination whether to mark the transaction for rol
|
||||
|
||||
In its default configuration, the Spring Framework's transaction infrastructure code
|
||||
marks a transaction for rollback only in the case of runtime, unchecked exceptions.
|
||||
That is, when the thrown exception is an instance or subclass of `RuntimeException`. (
|
||||
`Error` instances also, by default, result in a rollback). Checked exceptions that are
|
||||
That is, when the thrown exception is an instance or subclass of `RuntimeException`.
|
||||
(`Error` instances also, by default, result in a rollback). Checked exceptions that are
|
||||
thrown from a transactional method do not result in rollback in the default
|
||||
configuration.
|
||||
|
||||
You can configure exactly which `Exception` types mark a transaction for rollback,
|
||||
including checked exceptions. The following XML snippet demonstrates how you configure
|
||||
rollback for a checked, application-specific `Exception` type:
|
||||
including checked exceptions by specifying _rollback rules_.
|
||||
|
||||
.Rollback rules
|
||||
[[transaction-declarative-rollback-rules]]
|
||||
[NOTE]
|
||||
====
|
||||
Rollback rules determine if a transaction should be rolled back when a given exception is
|
||||
thrown, and the rules are based on patterns. A pattern can be a fully qualified class
|
||||
name or a substring of a fully qualified class name for an exception type (which must be
|
||||
a subclass of `Throwable`), with no wildcard support at present. For example, a value of
|
||||
`"jakarta.servlet.ServletException"` or `"ServletException"` will match
|
||||
`jakarta.servlet.ServletException` and its subclasses.
|
||||
|
||||
Rollback rules may be configured in XML via the `rollback-for` and `no-rollback-for`
|
||||
attributes, which allow patterns to be specified as strings. When using
|
||||
<<transaction-declarative-attransactional-settings,`@Transactional`>>, rollback rules may
|
||||
be configured via the `rollbackFor`/`noRollbackFor` and
|
||||
`rollbackForClassName`/`noRollbackForClassName` attributes, which allow patterns to be
|
||||
specified as `Class` references or strings, respectively. When an exception type is
|
||||
specified as a class reference its fully qualified name will be used as the pattern.
|
||||
Consequently, `@Transactional(rollbackFor = example.CustomException.class)` is equivalent
|
||||
to `@Transactional(rollbackForClassName = "example.CustomException")`.
|
||||
|
||||
[WARNING]
|
||||
=====
|
||||
You must carefully consider how specific the pattern is and whether to include package
|
||||
information (which isn't mandatory). For example, `"Exception"` will match nearly
|
||||
anything and will probably hide other rules. `"java.lang.Exception"` would be correct if
|
||||
`"Exception"` were meant to define a rule for all checked exceptions. With more unique
|
||||
exception names such as `"BaseBusinessException"` there is likely no need to use the
|
||||
fully qualified class name for the exception pattern.
|
||||
|
||||
Furthermore, rollback rules may result in unintentional matches for similarly named
|
||||
exceptions and nested classes. This is due to the fact that a thrown exception is
|
||||
considered to be a match for a given rollback rule if the name of thrown exception
|
||||
contains the exception pattern configured for the rollback rule. For example, given a
|
||||
rule configured to match on `com.example.CustomException`, that rule would match against
|
||||
an exception named `com.example.CustomExceptionV2` (an exception in the same package as
|
||||
`CustomException` but with an additional suffix) or an exception named
|
||||
`com.example.CustomException$AnotherException` (an exception declared as a nested class
|
||||
in `CustomException`).
|
||||
=====
|
||||
====
|
||||
|
||||
The following XML snippet demonstrates how to configure rollback for a checked,
|
||||
application-specific `Exception` type by supplying an _exception pattern_ via the
|
||||
`rollback-for` attribute:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -1046,8 +1093,8 @@ rollback for a checked, application-specific `Exception` type:
|
||||
</tx:advice>
|
||||
----
|
||||
|
||||
If you do not want a transaction rolled
|
||||
back when an exception is thrown, you can also specify 'no rollback rules'. The following example tells the Spring Framework's
|
||||
If you do not want a transaction rolled back when an exception is thrown, you can also
|
||||
specify 'no rollback' rules. The following example tells the Spring Framework's
|
||||
transaction infrastructure to commit the attendant transaction even in the face of an
|
||||
unhandled `InstrumentNotFoundException`:
|
||||
|
||||
@@ -1061,11 +1108,11 @@ unhandled `InstrumentNotFoundException`:
|
||||
</tx:advice>
|
||||
----
|
||||
|
||||
When the Spring Framework's transaction infrastructure catches an exception and it
|
||||
consults the configured rollback rules to determine whether to mark the transaction for
|
||||
rollback, the strongest matching rule wins. So, in the case of the following
|
||||
configuration, any exception other than an `InstrumentNotFoundException` results in a
|
||||
rollback of the attendant transaction:
|
||||
When the Spring Framework's transaction infrastructure catches an exception and consults
|
||||
the configured rollback rules to determine whether to mark the transaction for rollback,
|
||||
the strongest matching rule wins. So, in the case of the following configuration, any
|
||||
exception other than an `InstrumentNotFoundException` results in a rollback of the
|
||||
attendant transaction:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -1076,10 +1123,10 @@ rollback of the attendant transaction:
|
||||
</tx:advice>
|
||||
----
|
||||
|
||||
You can also indicate a required rollback programmatically. Although simple,
|
||||
this process is quite invasive and tightly couples your code to the Spring Framework's
|
||||
transaction infrastructure. The following example shows how to programmatically indicate
|
||||
a required rollback:
|
||||
You can also indicate a required rollback programmatically. Although simple, this process
|
||||
is quite invasive and tightly couples your code to the Spring Framework's transaction
|
||||
infrastructure. The following example shows how to programmatically indicate a required
|
||||
rollback:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
@@ -1661,7 +1708,8 @@ The default `@Transactional` settings are as follows:
|
||||
* The transaction is read-write.
|
||||
* The transaction timeout defaults to the default timeout of the underlying transaction
|
||||
system, or to none if timeouts are not supported.
|
||||
* Any `RuntimeException` triggers rollback, and any checked `Exception` does not.
|
||||
* Any `RuntimeException` or `Error` triggers rollback, and any checked `Exception` does
|
||||
not.
|
||||
|
||||
You can change these default settings. The following table summarizes the various
|
||||
properties of the `@Transactional` annotation:
|
||||
@@ -1675,6 +1723,14 @@ properties of the `@Transactional` annotation:
|
||||
| `String`
|
||||
| Optional qualifier that specifies the transaction manager to be used.
|
||||
|
||||
| `transactionManager`
|
||||
| `String`
|
||||
| Alias for `value`.
|
||||
|
||||
| `label`
|
||||
| Array of `String` labels to add an expressive description to the transaction.
|
||||
| Labels may be evaluated by transaction managers to associate implementation-specific behavior with the actual transaction.
|
||||
|
||||
| <<tx-propagation,propagation>>
|
||||
| `enum`: `Propagation`
|
||||
| Optional propagation setting.
|
||||
@@ -1687,32 +1743,35 @@ properties of the `@Transactional` annotation:
|
||||
| `int` (in seconds of granularity)
|
||||
| Optional transaction timeout. Applies only to propagation values of `REQUIRED` or `REQUIRES_NEW`.
|
||||
|
||||
| `timeoutString`
|
||||
| `String` (in seconds of granularity)
|
||||
| Alternative for specifying the `timeout` in seconds as a `String` value -- for example, as a placeholder.
|
||||
|
||||
| `readOnly`
|
||||
| `boolean`
|
||||
| Read-write versus read-only transaction. Only applicable to values of `REQUIRED` or `REQUIRES_NEW`.
|
||||
|
||||
| `rollbackFor`
|
||||
| Array of `Class` objects, which must be derived from `Throwable.`
|
||||
| Optional array of exception classes that must cause rollback.
|
||||
| Optional array of exception types that must cause rollback.
|
||||
|
||||
| `rollbackForClassName`
|
||||
| Array of class names. The classes must be derived from `Throwable.`
|
||||
| Optional array of names of exception classes that must cause rollback.
|
||||
| Array of exception name patterns.
|
||||
| Optional array of exception name patterns that must cause rollback.
|
||||
|
||||
| `noRollbackFor`
|
||||
| Array of `Class` objects, which must be derived from `Throwable.`
|
||||
| Optional array of exception classes that must not cause rollback.
|
||||
| Optional array of exception types that must not cause rollback.
|
||||
|
||||
| `noRollbackForClassName`
|
||||
| Array of `String` class names, which must be derived from `Throwable.`
|
||||
| Optional array of names of exception classes that must not cause rollback.
|
||||
|
||||
| `label`
|
||||
| Array of `String` labels to add an expressive description to the transaction.
|
||||
| Labels may be evaluated by transaction managers to associate
|
||||
implementation-specific behavior with the actual transaction.
|
||||
| Array of exception name patterns.
|
||||
| Optional array of exception name patterns that must not cause rollback.
|
||||
|===
|
||||
|
||||
TIP: See <<transaction-declarative-rollback-rules, Rollback rules>> for further details
|
||||
on rollback rule semantics, patterns, and warnings regarding possible unintentional
|
||||
matches.
|
||||
|
||||
Currently, you cannot have explicit control over the name of a transaction, where 'name'
|
||||
means the transaction name that appears in a transaction monitor, if applicable
|
||||
(for example, WebLogic's transaction monitor), and in logging output. For declarative
|
||||
|
||||
Reference in New Issue
Block a user