此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.2.0! |
用@Transactional
除了基于 XML 的声明式事务配置方法外,您还可以 使用基于注释的方法。直接在 Java 中声明事务语义 源代码使声明更接近受影响的代码。没有太多 过度耦合的危险,因为本应以事务方式使用的代码是 无论如何,几乎总是以这种方式部署。
标准jakarta.transaction.Transactional annotation 也支持作为
Spring 自己的 Comments 的直接替代品。请参阅 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) {
// ...
}
}
如上所述,在类级别使用,注释表示所有方法的默认值 声明类(及其子类)的 Statementing Class。或者,每种方法都可以是 单独注释。有关 Spring 认为哪些方法为事务的更多详细信息,请参见方法可见性。请注意,类级别 annotation 不适用于 class 层次结构中的祖先类;在这种情况下, 继承的方法需要在本地重新声明才能参与 subclass-level 注解。
当 POJO 类(如上面的类)在 Spring 上下文中被定义为 bean 时,
您可以通过@EnableTransactionManagement
注解@Configuration
类。有关完整详细信息,请参阅 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/> tag 的TransactionManager 的名称为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”下的 Cancel Signals 部分。
方法 visibility 和
@Transactional 在代理模式下这 如果您希望对不同类型方法的可见性进行一致的处理
proxies(在 5.3 之前是默认值),请考虑指定
Spring TestContext 框架支持非私有 |
您可以应用@Transactional
对接口定义的注解、方法
在接口、类定义或类上的方法上。然而,仅仅存在
的@Transactional
annotation 不足以激活事务行为。
这@Transactional
annotation 只是元数据,可以由相应的
运行时基础结构,该基础结构使用该元数据来配置适当的 bean
交易行为。在前面的示例中,<tx:annotation-driven/>
元素
在运行时打开实际事务管理。
Spring 团队建议您使用@Transactional 注解,而不是依赖接口中带注解的方法,
即使后者在 5.0 中确实适用于基于接口的代理和 Target 类代理。
由于 Java 注解不是从接口继承的,因此接口声明的注解
在使用 AspectJ 模式时仍然无法被 weaveing 基础设施识别,因此
aspect 不会被应用。因此,您的交易注释可能是
静默忽略:在您测试回滚方案之前,您的代码可能看起来“有效”。 |
在代理模式(这是默认模式)下,只有外部方法调用通过
代理被拦截。这意味着自调用(实际上是
target 对象调用 target 对象的另一个方法)不会导致实际的
transaction 在运行时,即使调用的方法标记为@Transactional .也
代理必须完全初始化才能提供预期行为,因此您不应
在初始化代码中依赖此功能 — 例如,在@PostConstruct 方法。 |
考虑使用 AspectJ 模式(参见mode
属性),如果您
期望 self-invocations 也将与 transactions 一起包装。在这种情况下,有
首先没有代理。相反,目标类是 woven 的(即它的字节码
已修改)以支持@Transactional
运行时行为。
XML 属性 | 注释属性 | 违约 | 描述 |
---|---|---|---|
|
N/A(请参阅 |
|
要使用的事务管理器的名称。仅当交易名称
manager 不是 |
|
|
|
默认模式 ( |
|
|
|
适用于 |
|
|
|
定义应用于 Comments 为 |
用于处理的默认通知模式@Transactional annotations 为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
class 为
在类级别使用只读事务的设置进行注释,但@Transactional
注解updateFoo(Foo)
method 采用
优先于在 Class 级别定义的事务设置。
-
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
annotation 是指定接口、类、
或方法必须具有事务语义(例如,“启动全新的只读
transaction 来暂停任何现有事务”)。
默认的@Transactional
设置如下:
-
传播设置为
PROPAGATION_REQUIRED.
-
隔离级别为
ISOLATION_DEFAULT.
-
事务是读写的。
-
事务超时默认为基础事务的默认超时 system,如果不支持超时,则设置为 none。
-
任何
RuntimeException
或Error
触发器回滚,并且任何选中的Exception
does 不。
您可以更改这些默认设置。下表总结了各种
的属性@Transactional
注解:
财产 | 类型 | 描述 |
---|---|---|
|
指定要使用的事务管理器的可选限定符。 |
|
|
|
别名 |
|
数组 |
事务管理器可以评估标签,以将特定于实现的行为与实际事务相关联。 |
|
可选的传播设置。 |
|
|
|
可选隔离级别。仅适用于 |
|
|
可选的事务超时。仅适用于 |
|
|
用于指定 |
|
|
读写与只读事务。仅适用于 |
|
数组 |
必须导致回滚的异常类型的可选数组。 |
|
异常名称模式的数组。 |
必须导致回滚的异常名称模式的可选数组。 |
|
数组 |
不得导致回滚的异常类型的可选数组。 |
|
异常名称模式的数组。 |
不得导致回滚的异常名称模式的可选数组。 |
有关回滚规则语义、模式和警告的更多详细信息,请参阅回滚规则 关于基于模式的回滚规则的可能意外匹配。 |
从 6.2 开始,您可以全局更改默认回滚行为 – 例如,通过 请注意,特定于事务的回滚规则会覆盖默认行为,但
为未指定的异常保留所选的默认值。这种情况
Spring的 除非您依赖于具有提交行为的 EJB 样式业务异常,否则它是
建议切换到 |
目前,您无法对事务的名称进行显式控制,其中 'name'
表示出现在事务监视器和日志记录输出中的事务名称。
对于声明性事务,事务名称始终是完全限定类
事务性建议类的名称 +.
+ 方法名称。例如,如果handlePayment(..)
方法BusinessService
类启动了事务,则
交易的 name 将为com.example.BusinessService.handlePayment
.
多个事务管理器@Transactional
大多数 Spring 应用程序只需要一个事务 Management 器,但可能有
您希望在单个事务管理器中有多个独立的事务管理器的情况
应用。您可以使用value
或transactionManager
属性的@Transactional
注解来选择性地指定TransactionManager
以供使用。这可以是 bean 名称或 qualifier 值
事务管理器 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
在 separate 下运行
事务管理器,通过order
,account
和reactive-account
限定 符。默认的<tx:annotation-driven>
目标 Bean 名称、transactionManager
,
如果没有特别限定的,则仍使用TransactionManager
Bean 的 Bean 中找到。
如果同一类上的所有事务方法共享相同的限定符,则考虑
声明类型级 这样的类型级限定符可以在具体类上声明,应用于 transaction 定义。这实际上会覆盖 任何非限定基类方法的默认事务管理器选项。 最后但并非最不重要的一点是,这样的类型级 bean 限定符可以用于多种用途, 例如,如果值为 “order”,则它可以用于自动装配目的(标识 Order 存储库)以及事务管理器选择,只要 用于自动装配的目标 bean 以及关联的事务管理器 definitions 声明相同的限定符值。这样的限定符值只需要 在一组类型匹配的 bean 中是唯一的,而不必用作 ID。 |
自定义组合注释
如果您发现您重复使用相同的属性@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() {
// ...
}
}
在前面的示例中,我们使用了语法来定义事务管理器限定符 和事务标签,但我们也可以包括传播行为 回滚规则、超时和其他功能。