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:
committed by
Juergen Hoeller
parent
3e736898a6
commit
2aa8aef216
@@ -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")) {
|
||||
|
||||
@@ -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 {};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -1423,6 +1423,11 @@ properties of the `@Transactional` annotation:
|
||||
| `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.
|
||||
|===
|
||||
|
||||
Currently, you cannot have explicit control over the name of a transaction, where 'name'
|
||||
@@ -1508,13 +1513,13 @@ following annotation definitions:
|
||||
----
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Transactional("order")
|
||||
@Transactional(value = "order", label = "causal-consistency")
|
||||
public @interface OrderTx {
|
||||
}
|
||||
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Transactional("account")
|
||||
@Transactional("account", label = "retryable")
|
||||
public @interface AccountTx {
|
||||
}
|
||||
----
|
||||
@@ -1523,12 +1528,12 @@ following annotation definitions:
|
||||
----
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Transactional("order")
|
||||
@Transactional(value = "order", label = ["causal-consistency"])
|
||||
annotation class OrderTx
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Transactional("account")
|
||||
@Transactional(value = "account", label = ["retryable"])
|
||||
annotation class AccountTx
|
||||
----
|
||||
|
||||
@@ -1567,8 +1572,9 @@ The preceding annotations lets us write the example from the previous section as
|
||||
}
|
||||
----
|
||||
|
||||
In the preceding example, we used the syntax to define the transaction manager qualifier, but we could also
|
||||
have included propagation behavior, rollback rules, timeouts, and other features.
|
||||
In the preceding example, we used the syntax to define the transaction manager qualifier
|
||||
and transactional labels, but we could also have included propagation behavior,
|
||||
rollback rules, timeouts, and other features.
|
||||
|
||||
|
||||
[[tx-propagation]]
|
||||
|
||||
Reference in New Issue
Block a user