|
对于最新的稳定版本,请使用 Spring Framework 7.0.6! |
使用 @Transactional
除了基于 XML 的声明式事务配置方法外,您还可以使用基于注解的方法。将事务语义直接声明在 Java 源代码中,可以使这些声明更接近受影响的代码。由于旨在以事务方式使用的代码通常无论如何都会这样部署,因此不太可能造成不恰当的耦合。
标准的jakarta.transaction.Transactional注解也可作为Spring自带注解的直接替代品使用。更多详情请参阅JTA文档。 |
通过使用@Transactional注解所带来的易用性最好通过一个示例来说明,该示例在下面的文本中进行了解释。
请考虑以下类定义:
-
Java
-
Kotlin
// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {
@Override
public Foo getFoo(String fooName) {
// ...
}
@Override
public Foo getFoo(String fooName, String barName) {
// ...
}
@Override
public void insertFoo(Foo foo) {
// ...
}
@Override
public void updateFoo(Foo foo) {
// ...
}
}
// the service class that we want to make transactional
@Transactional
class DefaultFooService : FooService {
override fun getFoo(fooName: String): Foo {
// ...
}
override fun getFoo(fooName: String, barName: String): Foo {
// ...
}
override fun insertFoo(foo: Foo) {
// ...
}
override fun updateFoo(foo: Foo) {
// ...
}
}
如上所述在类级别使用时,该注解为声明类(及其子类)的所有方法提供了默认配置。此外,每个方法可以单独添加注解。有关Spring认定哪些方法具有事务性的详细信息,请参阅方法可见性。请注意,类级别注解不会沿类层次结构向上应用于祖先类;在此类场景中,继承的方法需要在子类中本地重新声明,才能参与子类级别的注解配置。
当像上面这样的POJO类被定义为Spring上下文中的bean时,
可以通过在@EnableTransactionManagement
注解的@Configuration类中使用@EnableTransactionManagement
注解使bean实例具有事务性。请参阅
javadoc
以获取详细信息。
在XML配置中,<tx:annotation-driven/>标签提供了类似的便利:
<!-- from the file 'context.xml' -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- this is the service object that we want to make transactional -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- enable the configuration of transactional behavior based on annotations -->
<!-- a TransactionManager is still required -->
<tx:annotation-driven transaction-manager="txManager"/> (1)
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- (this dependency is defined somewhere else) -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- other <bean/> definitions here -->
</beans>
| 1 | 使bean实例具有事务性的那一行。 |
您可以省略 transaction-manager 属性在 <tx:annotation-driven/>
标签中,如果要注入的 TransactionManager 的 bean 名称是
transactionManager。如果要注入的 TransactionManager bean 有其他名称,您必须使用 transaction-manager 属性,如前面的示例所示。 |
响应式事务方法使用响应式返回类型,与以下清单所示的命令式编程安排形成对比:
-
Java
-
Kotlin
// the reactive service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {
@Override
public Publisher<Foo> getFoo(String fooName) {
// ...
}
@Override
public Mono<Foo> getFoo(String fooName, String barName) {
// ...
}
@Override
public Mono<Void> insertFoo(Foo foo) {
// ...
}
@Override
public Mono<Void> updateFoo(Foo foo) {
// ...
}
}
// the reactive service class that we want to make transactional
@Transactional
class DefaultFooService : FooService {
override fun getFoo(fooName: String): Flow<Foo> {
// ...
}
override fun getFoo(fooName: String, barName: String): Mono<Foo> {
// ...
}
override fun insertFoo(foo: Foo): Mono<Void> {
// ...
}
override fun updateFoo(foo: Foo): Mono<Void> {
// ...
}
}
请注意,返回的 Publisher 在与 Reactive Streams 取消信号相关时有特殊注意事项。有关更多详细信息,请参阅“使用 TransactionalOperator”下的 取消信号 部分。
|
代理模式中的方法可见性和
@Transactional
如果您希望在不同类型的代理中对方法可见性进行一致处理(这是5.3版本之前的默认行为),考虑指定
默认情况下,Spring TestContext 框架 也支持非私有的 |
您可以将 @Transactional 注解应用于接口定义、接口上的方法、类定义或类上的方法。但是,@Transactional 注解的存在本身并不足以激活事务行为。@Transactional 注解只是元数据,可以被相应的运行时基础设施使用,该基础设施利用此元数据将适当的 Bean 配置为具有事务行为。在前面的示例中,<tx:annotation-driven/> 元素在运行时开启了实际的事务管理。
Spring团队建议您使用@Transactional注解标注具体类的方法,而不是依赖于接口中的注解方法,即使从5.0版本开始接口方法的注解在基于接口和目标类的代理中也能正常工作。由于Java注解不会从接口继承,所以在使用AspectJ模式时,接口中声明的注解仍然无法被织入基础设施识别,因此切面不会被应用。结果就是,您的事务注解可能会被静默地忽略:您的代码可能看起来“正常工作”,直到您测试回滚场景。 |
在代理模式(默认模式)下,只有通过代理传入的外部方法调用才会被拦截。这意味着,自调用(实际上,目标对象中的一个方法调用目标对象的另一个方法)在运行时不会导致实际的事务,即使被调用的方法标记有@Transactional。此外,代理必须完全初始化才能提供预期的行为,因此你不应该在初始化代码中依赖此功能 — 例如,在@PostConstruct方法中。 |
如果希望自调用方法也包含在事务中,请考虑使用 AspectJ 模式(参见下表中的 mode 属性)。在这种情况下,根本不会有代理。相反,目标类会被织入(即,其字节码会被修改),以支持任何类型方法的 @Transactional 运行时行为。
| XML 属性 | 注解属性 | 默认 | 描述 |
|---|---|---|---|
|
未提供(参见 |
|
要使用的事务管理器的名称。仅当事务管理器的名称不是 |
|
|
|
默认模式( |
|
|
|
仅适用于 |
|
|
|
定义了应用于带有 |
默认处理 @Transactional 注解的建议模式是 proxy,
这仅允许通过代理拦截调用。同一类中的本地调用无法以这种方式拦截。要使用更高级的拦截模式,
可以考虑结合编译时或加载时编织切换到 aspectj 模式。 |
proxy-target-class 属性控制使用 @Transactional 注解的类创建哪种类型的事务代理。如果 proxy-target-class 设置为 true,则会创建基于类的代理。如果 proxy-target-class 是 false 或者该属性被省略,则会创建标准的 JDK 接口代理。(有关不同代理类型的讨论,请参见 代理机制。) |
@EnableTransactionManagement 和 <tx:annotation-driven/> 会查找
@Transactional 仅在它们定义的同一应用上下文中的 bean 上。
这意味着,如果你在一个 WebApplicationContext
用于 DispatcherServlet 的配置中使用注解驱动的配置,它只会检查你的控制器中的 @Transactional bean,
而不会检查你的服务中的。有关更多信息,请参见 MVC。 |
在评估方法的事务设置时,最具体的定位具有优先权。在下面的例子中,DefaultFooService类在类级别上使用了只读事务的设置,但同一类中的@Transactional注解在updateFoo(Foo)方法上的设置优先于类级别上定义的事务设置。
-
Java
-
Kotlin
@Transactional(readOnly = true)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// ...
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
// ...
}
}
@Transactional(readOnly = true)
class DefaultFooService : FooService {
override fun getFoo(fooName: String): Foo {
// ...
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
override fun updateFoo(foo: Foo) {
// ...
}
}
@Transactional 设置
@Transactional 注解是元数据,用于指定接口、类或方法必须具有事务语义(例如,“当调用此方法时启动一个新的只读事务,暂停任何现有事务”)。
默认的@Transactional设置如下:
-
传播设置为
PROPAGATION_REQUIRED. -
隔离级别是
ISOLATION_DEFAULT. -
事务是读写型的。
-
事务超时默认使用底层事务系统的默认超时时间,或者如果系统不支持超时,则设置为无。
-
任何
RuntimeException或Error都会触发回滚,而任何已检查的Exception不会。
您可以更改这些默认设置。下表总结了<code>0</code>注释的各种属性:
| 属性 | 类型 | 描述 |
|---|---|---|
|
可选的限定符,用于指定要使用的事务管理器。 |
|
|
|
|
|
包含 |
标签可能由事务管理器进行评估,以将特定于实现的行为与实际事务相关联。 |
|
可选的传播设置。 |
|
|
|
可选的隔离级别。仅适用于传播值为 |
|
|
可选的事务超时。仅适用于传播值为 |
|
|
用 |
|
|
读写事务与只读事务。仅适用于值为 |
|
包含 |
可选的异常类型数组,这些异常必须导致回滚。 |
|
异常名称模式数组。 |
可选的异常名称模式数组,这些模式必须导致回滚。 |
|
包含 |
可选的异常类型数组,这些异常类型不会导致回滚。 |
|
异常名称模式数组。 |
可选的异常名称模式数组,这些模式不会导致回滚。 |
| 有关回滚规则语义、模式以及基于模式的回滚规则可能产生意外匹配的警告详情,请参见回滚规则。 |
目前,您无法显式控制事务的名称,其中“名称”指的是出现在事务监视器和日志输出中的事务名称。
对于声明式事务,事务名称始终是受事务通知类的完整类名 + . + 方法名。例如,如果
handlePayment(..)类的BusinessService方法启动了事务,则该事务的名称将为:com.example.BusinessService.handlePayment。
带有 @Transactional 的多个事务管理器
大多数Spring应用程序只需要一个事务管理器,但有时您可能希望在单个应用程序中使用多个独立的事务管理器。您可以使用value或transactionManager属性的@Transactional注解来可选地指定要使用的TransactionManager的标识。这可以是事务管理器bean的bean名称或限定符值。例如,使用限定符符号,您可以将以下Java代码与应用程序上下文中的以下事务管理器bean声明结合起来:
-
Java
-
Kotlin
public class TransactionalService {
@Transactional("order")
public void setSomething(String name) { ... }
@Transactional("account")
public void doSomething() { ... }
@Transactional("reactive-account")
public Mono<Void> doSomethingReactive() { ... }
}
class TransactionalService {
@Transactional("order")
fun setSomething(name: String) {
// ...
}
@Transactional("account")
fun doSomething() {
// ...
}
@Transactional("reactive-account")
fun doSomethingReactive(): Mono<Void> {
// ...
}
}
以下列表显示了 bean 声明:
<tx:annotation-driven/>
<bean id="transactionManager1" class="org.springframework.jdbc.support.JdbcTransactionManager">
...
<qualifier value="order"/>
</bean>
<bean id="transactionManager2" class="org.springframework.jdbc.support.JdbcTransactionManager">
...
<qualifier value="account"/>
</bean>
<bean id="transactionManager3" class="org.springframework.data.r2dbc.connection.R2dbcTransactionManager">
...
<qualifier value="reactive-account"/>
</bean>
在这种情况下,TransactionalService上的各个方法在不同的事务管理器下运行,这些事务管理器通过order、account和reactive-account限定符进行区分。如果未找到特定的TransactionManager bean,则仍使用默认的<tx:annotation-driven>目标bean名称,transactionManager。
自定义组合注解
如果您发现您在许多不同的方法上反复使用相同的属性 @Transactional,Spring 的元注解支持 允许您为您的特定用例定义自定义组合注解。例如,考虑以下注解定义:
-
Java
-
Kotlin
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(transactionManager = "order", label = "causal-consistency")
public @interface OrderTx {
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(transactionManager = "account", label = "retryable")
public @interface AccountTx {
}
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@Transactional(transactionManager = "order", label = ["causal-consistency"])
annotation class OrderTx
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@Transactional(transactionManager = "account", label = ["retryable"])
annotation class AccountTx
前面的注解让我们可以将上一节中的示例写成如下形式:
-
Java
-
Kotlin
public class TransactionalService {
@OrderTx
public void setSomething(String name) {
// ...
}
@AccountTx
public void doSomething() {
// ...
}
}
class TransactionalService {
@OrderTx
fun setSomething(name: String) {
// ...
}
@AccountTx
fun doSomething() {
// ...
}
}
在前面的示例中,我们使用了语法来定义事务管理器限定符和事务标签,但也可以包含传播行为、回滚规则、超时和其他功能。