使用 @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 时,可以通过 @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 实例成为事务性的行。 |
如果要连接的 TransactionManager 的 bean 名称是 transactionManager ,则可以在 <tx:annotation-driven/> 标签中省略 transaction-manager 属性。如果要依赖注入的 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 Framework 默认情况下也支持非私有 |
您可以将@Transactional
注解应用于接口定义、接口上的方法、类定义或类上的方法。但是,仅仅存在@Transactional
注解不足以激活事务行为。@Transactional
注解仅仅是元数据,可以被相应的运行时基础设施使用,该基础设施使用该元数据来配置具有事务行为的适当 bean。在前面的示例中,<tx:annotation-driven/>
元素在运行时开启了实际的事务管理。
Spring 团队建议您使用@Transactional 注解来注释具体类的方法,而不是依赖于接口中注释的方法,即使后者在 5.0 及更高版本中对基于接口的代理和目标类代理有效。由于 Java 注解不会从接口继承,因此当使用 AspectJ 模式时,接口声明的注解仍然不会被编织基础设施识别,因此不会应用方面。因此,您的事务注解可能会被静默忽略:您的代码可能看起来“正常工作”,直到您测试回滚场景。
|
在代理模式(默认模式)下,只有通过代理进入的外部方法调用会被拦截。这意味着自调用(实际上,目标对象中的一个方法调用目标对象的另一个方法)在运行时不会导致实际的事务,即使被调用的方法被标记为@Transactional 。此外,代理必须完全初始化才能提供预期的行为,因此您不应该在初始化代码中依赖此功能,例如在@PostConstruct 方法中。
|
如果您希望自调用也包含在事务中,请考虑使用 AspectJ 模式(请参见下表中的mode
属性)。在这种情况下,首先不存在代理。相反,目标类会被编织(即它的字节码会被修改)以支持@Transactional
在任何类型的方法上的运行时行为。
XML 属性 | 注解属性 | 默认值 | 描述 |
---|---|---|---|
|
N/A(请参见 |
|
要使用的交易管理器名称。仅当交易管理器名称不是 |
|
|
|
默认模式( |
|
|
|
仅适用于 |
|
|
|
定义应用于使用 |
处理@Transactional 注释的默认建议模式是proxy ,它允许仅拦截通过代理的调用。同一个类中的本地调用无法以这种方式被拦截。对于更高级的拦截模式,请考虑将aspectj 模式与编译时或加载时编织结合使用。
|
proxy-target-class 属性控制为使用@Transactional 注释的类创建的交易代理的类型。如果proxy-target-class 设置为true ,则创建基于类的代理。如果proxy-target-class 为false 或省略该属性,则创建标准的基于 JDK 接口的代理。(有关不同代理类型的讨论,请参阅代理机制。)
|
@EnableTransactionManagement 和 <tx:annotation-driven/> 仅在定义它们的同一个应用程序上下文中查找 @Transactional 。这意味着,如果您将基于注解的配置放在 DispatcherServlet 的 WebApplicationContext 中,它只检查您的控制器中的 @Transactional bean,而不是您的服务。有关更多信息,请参见 MVC。
|
在评估方法的事务设置时,最派生的位置优先。在以下示例中,DefaultFooService
类在类级别上使用只读事务的设置进行了注解,但同一个类中 updateFoo(Foo)
方法上的 @Transactional
注解优先于在类级别定义的事务设置。
-
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
则不会。
您可以更改这些默认设置。下表总结了 @Transactional
注解的各种属性
属性 | 类型 | 描述 |
---|---|---|
|
可选限定符,指定要使用的事务管理器。 |
|
|
|
|
|
|
标签可能会被事务管理器评估,以将特定于实现的行为与实际事务相关联。 |
|
可选传播设置。 |
|
|
|
可选隔离级别。仅适用于 |
|
|
可选事务超时。仅适用于 |
|
|
指定以秒为单位的 |
|
|
读写事务与只读事务。仅适用于值为 |
|
|
必须导致回滚的异常类型可选数组。 |
|
异常名称模式数组。 |
必须导致回滚的异常名称模式可选数组。 |
|
|
必须不导致回滚的异常类型可选数组。 |
|
异常名称模式数组。 |
必须不导致回滚的异常名称模式可选数组。 |
有关回滚规则语义、模式以及有关基于模式的回滚规则的潜在无意匹配的警告的更多详细信息,请参阅 回滚规则。 |
目前,您无法对事务的名称进行显式控制,其中“名称”是指在事务监视器和日志输出中显示的事务名称。对于声明式事务,事务名称始终是完全限定的类名 + .
+ 事务性建议类的函数名。例如,如果 BusinessService
类的 handlePayment(..)
函数启动了一个事务,则事务的名称将为:com.example.BusinessService.handlePayment
。
@Transactional
中的多个事务管理器
大多数 Spring 应用程序只需要一个事务管理器,但可能存在您希望在一个应用程序中使用多个独立事务管理器的场景。您可以使用 @Transactional
注释的 value
或 transactionManager
属性来可选地指定要使用的 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() {
// ...
}
}
在前面的示例中,我们使用了语法来定义事务管理器限定符和事务标签,但我们也可以包含传播行为、回滚规则、超时和其他功能。