Support type-safe transaction rollback rules
Prior to this commit, there was no way to configure type-safe rollback rules for transactions. Even though a rollback rule could be defined using a Class reference via the `rollbackFor` and `noRollbackFor` attributes in @Transactional, those Class references got converted to Strings (as the fully qualified class names of the exception types) in RollbackRuleAttribute which then applied a pattern-based matching algorithm as if the Class references had been supplied as Strings/patterns to begin with, thereby losing the type information. Pattern-based rollback rules suffer from the following three categories of unintentional matches. - identically named exceptions in different packages when the pattern does not include the package name -- for example, example.client.WebException and example.server.WebException both match against a "WebException" pattern. - similarly named exceptions in the same package when a given exception name starts with the name of another exception -- for example, example.BusinessException and example.BusinessExceptionWithDetails both match against an "example.BusinessException" pattern. - nested exceptions when an exception type is declared in another exception -- for example, example.BusinessException and example.BusinessException$NestedException both match against an "example.BusinessException" pattern. This commit prevents the latter two categories of unintentional matches for rollback rules defined using a Class reference by storing the exceptionType in RollbackRuleAttribute and using that type in the implementation of RollbackRuleAttribute.getDepth(Class, int), resulting in type-safe rollback rules whenever the `rollbackFor` and `noRollbackFor` attributes in `@Transactional` are used. Note that the first category of unintentional matches never applied to rollback rules created from a Class reference since the fully qualified name of a Class reference always includes the package name. Closes gh-28098
This commit is contained in:
@@ -51,15 +51,6 @@ class RollbackRuleAttributeTests {
|
||||
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());
|
||||
@@ -74,6 +65,9 @@ class RollbackRuleAttributeTests {
|
||||
|
||||
@Test
|
||||
void foundImmediatelyWhenNameOfExceptionThrownStartsWithNameOfRegisteredException() {
|
||||
// Precondition for this use case.
|
||||
assertThat(MyException.class.isAssignableFrom(MyException2.class)).isFalse();
|
||||
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(MyException.class.getName());
|
||||
assertThat(rr.getDepth(new MyException2())).isEqualTo(0);
|
||||
}
|
||||
@@ -85,6 +79,15 @@ class RollbackRuleAttributeTests {
|
||||
assertThat(rr.getDepth(new MyRuntimeException())).isEqualTo(3);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
@@ -103,12 +106,18 @@ class RollbackRuleAttributeTests {
|
||||
}
|
||||
|
||||
@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);
|
||||
void notFoundWhenNameOfExceptionThrownStartsWithNameOfRegisteredException() {
|
||||
// Precondition for this use case.
|
||||
assertThat(MyException.class.isAssignableFrom(MyException2.class)).isFalse();
|
||||
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(MyException.class);
|
||||
assertThat(rr.getDepth(new MyException2())).isEqualTo(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void notFoundWhenExceptionThrownIsNestedTypeOfRegisteredException() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(EnclosingException.class);
|
||||
assertThat(rr.getDepth(new EnclosingException.NestedException())).isEqualTo(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -117,18 +126,6 @@ class RollbackRuleAttributeTests {
|
||||
assertThat(rr.getDepth(new Exception())).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void foundImmediatelyWhenExceptionThrownIsNestedTypeOfRegisteredException() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(EnclosingException.class);
|
||||
assertThat(rr.getDepth(new EnclosingException.NestedException())).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void foundImmediatelyWhenNameOfExceptionThrownStartsWithNameOfRegisteredException() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(MyException.class);
|
||||
assertThat(rr.getDepth(new MyException2())).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void foundInSuperclassHierarchy() {
|
||||
RollbackRuleAttribute rr = new RollbackRuleAttribute(Exception.class);
|
||||
@@ -136,6 +133,15 @@ class RollbackRuleAttributeTests {
|
||||
assertThat(rr.getDepth(new MyRuntimeException())).isEqualTo(3);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user