Extend transaction attributes with labels

TransactionAttribute now exposes a labels attribute that associates a
descriptive array of labels with a transaction.

Labels may be of a pure descriptive nature or may get evaluated by
transaction managers to associate technology-specific behavior
with the actual transaction.
This commit is contained in:
Mark Paluch
2020-04-09 14:55:18 +02:00
committed by Juergen Hoeller
parent 3e736898a6
commit 2aa8aef216
7 changed files with 102 additions and 6 deletions

View File

@@ -19,6 +19,7 @@ package org.springframework.transaction.annotation;
import java.io.Serializable;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.core.annotation.AnnotatedElementUtils;
@@ -34,6 +35,7 @@ import org.springframework.transaction.interceptor.TransactionAttribute;
* Strategy implementation for parsing Spring's {@link Transactional} annotation.
*
* @author Juergen Hoeller
* @author Mark Paluch
* @since 2.5
*/
@SuppressWarnings("serial")
@@ -71,6 +73,7 @@ public class SpringTransactionAnnotationParser implements TransactionAnnotationP
rbta.setTimeout(attributes.getNumber("timeout").intValue());
rbta.setReadOnly(attributes.getBoolean("readOnly"));
rbta.setQualifier(attributes.getString("value"));
rbta.setLabels(Arrays.asList(attributes.getStringArray("label")));
List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {

View File

@@ -51,6 +51,7 @@ import org.springframework.transaction.TransactionDefinition;
* @author Colin Sampaleanu
* @author Juergen Hoeller
* @author Sam Brannen
* @author Mark Paluch
* @since 1.2
* @see org.springframework.transaction.interceptor.TransactionAttribute
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute
@@ -187,4 +188,17 @@ public @interface Transactional {
*/
String[] noRollbackForClassName() default {};
/**
* Defines zero (0) or more transaction labels. Labels may be used to
* describe a transaction and they can be evaluated by individual transaction
* manager. Labels may serve a solely descriptive purpose or map to
* pre-defined transaction manager-specific options.
* <p>See the description of the actual transaction manager implementation
* how it evaluates transaction labels.
*
* @since 5.3
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#getLabels()
*/
String[] label() default {};
}

View File

@@ -16,6 +16,9 @@
package org.springframework.transaction.interceptor;
import java.util.Collection;
import java.util.Collections;
import org.springframework.lang.Nullable;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.StringUtils;
@@ -26,6 +29,7 @@ import org.springframework.util.StringUtils;
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Mark Paluch
* @since 16.03.2003
*/
@SuppressWarnings("serial")
@@ -37,6 +41,8 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im
@Nullable
private String descriptor;
private Collection<String> labels = Collections.emptyList();
/**
* Create a new DefaultTransactionAttribute, with default settings.
@@ -97,6 +103,21 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im
return this.qualifier;
}
/**
* Associate one or more labels with this transaction attribute.
* <p>This may be used for applying specific transactional behavior
* or follow a purely descriptive nature.
* @since 5.3
*/
public void setLabels(Collection<String> labels) {
this.labels = labels;
}
@Override
public Collection<String> getLabels() {
return this.labels;
}
/**
* Set a descriptor for this transaction attribute,
* e.g. indicating where the attribute is applying.
@@ -145,6 +166,9 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im
if (StringUtils.hasText(this.qualifier)) {
result.append("; '").append(this.qualifier).append("'");
}
if (!this.labels.isEmpty()) {
result.append("; ").append(this.labels);
}
return result;
}

View File

@@ -17,6 +17,7 @@
package org.springframework.transaction.interceptor;
import java.io.Serializable;
import java.util.Collection;
import org.springframework.lang.Nullable;
import org.springframework.transaction.support.DelegatingTransactionDefinition;
@@ -28,6 +29,7 @@ import org.springframework.transaction.support.DelegatingTransactionDefinition;
* to the target instance.
*
* @author Juergen Hoeller
* @author Mark Paluch
* @since 1.2
*/
@SuppressWarnings("serial")
@@ -53,6 +55,11 @@ public abstract class DelegatingTransactionAttribute extends DelegatingTransacti
return this.targetAttribute.getQualifier();
}
@Override
public Collection<String> getLabels() {
return this.targetAttribute.getLabels();
}
@Override
public boolean rollbackOn(Throwable ex) {
return this.targetAttribute.rollbackOn(ex);

View File

@@ -16,6 +16,8 @@
package org.springframework.transaction.interceptor;
import java.util.Collection;
import org.springframework.lang.Nullable;
import org.springframework.transaction.TransactionDefinition;
@@ -26,6 +28,7 @@ import org.springframework.transaction.TransactionDefinition;
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Mark Paluch
* @since 16.03.2003
* @see DefaultTransactionAttribute
* @see RuleBasedTransactionAttribute
@@ -41,6 +44,14 @@ public interface TransactionAttribute extends TransactionDefinition {
@Nullable
String getQualifier();
/**
* Return labels associated with this transaction attribute.
* <p>This may be used for applying specific transactional behavior
* or follow a purely descriptive nature.
* @since 5.3
*/
Collection<String> getLabels();
/**
* Should we roll back on the given exception?
* @param ex the exception to evaluate

View File

@@ -45,6 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Colin Sampaleanu
* @author Juergen Hoeller
* @author Sam Brannen
* @author Mark Paluch
*/
public class AnnotationTransactionAttributeSourceTests {
@@ -183,6 +184,21 @@ public class AnnotationTransactionAttributeSourceTests {
assertThat(actual.rollbackOn(new IOException())).isFalse();
}
@Test
public void labelsAreApplied() throws Exception {
Method method = TestBean11.class.getMethod("getAge");
AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource();
TransactionAttribute actual = atas.getTransactionAttribute(method, TestBean11.class);
assertThat(actual.getLabels()).containsOnly("retryable", "long-running");
method = TestBean11.class.getMethod("setAge", Integer.TYPE);
actual = atas.getTransactionAttribute(method, method.getDeclaringClass());
assertThat(actual.getLabels()).containsOnly("short-running");
}
/**
* Test that transaction attribute is inherited from class
* if not specified on method.
@@ -693,6 +709,21 @@ public class AnnotationTransactionAttributeSourceTests {
}
}
@Transactional(label = {"retryable", "long-running"})
static class TestBean11 {
private int age = 10;
@Transactional(label = "short-running")
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
static class Ejb3AnnotatedBean1 implements ITestBean1 {