Using @Transactional
除了基于 XML 的事务配置声明式方法之外,你还可以使用基于注释的方法。在 Java 源代码中直接声明事务语义将声明与受影响的代码紧密地联系在一起。没有过度耦合的太大危险,因为打算以事务方式使用的代码几乎总是以这种方式部署的。
标准 |
通过使用 @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) {
// ...
}
}
在类级别如上使用时,注解表示声明类(以及其子类)的所有方法的默认值。或者,可以单独对每个方法进行注解。请参阅 method visibility 以了解 Spring 认为是事务性方法的更多详细信息。请注意,类级注解不适用于类层次结构上方的祖先类;在这种情况下,需要在本地重新声明继承的方法,以便参与子类级别的注解。
当像上面那样的 POJO 类在 Spring 上下文中被定义为 Bean 时,您可以通过 @Configuration
类中的 @EnableTransactionManagement
注解使 Bean 实例成为事务性的。请参阅https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/annotation/EnableTransactionManagement.html[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 实例具有事务性质。 |
如果要连接的 |
反应式事务方法使用反应式返回值类型,这与命令式编程安排相反,如下所示:
-
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
有特殊考虑。有关更多详细信息,请参阅“使用事务操作符”下的 Cancel Signals 一节。
Method visibility and
@Transactional in proxy mode
Spring TestContext Framework 默认情况下也支持非私有的 |
您可以将 @Transactional
注解应用于接口定义、接口上的方法、类定义或类上的方法。但是,只有 @Transactional
注解本身还不足以激活事务行为。@Transactional
注解只是一种元数据,相应的运行时基础结构可以使用这些元数据,并使用该元数据配置具有事务行为的适当 bean。在前面的示例中,<tx:annotation-driven/>
元素会在运行时开启实际的事务管理。
Spring 团队建议使用 |
在代理模式(它是默认模式)中,仅拦截通过代理传入的外部方法调用。这意味着自调用(实际上,目标对象中的方法调用目标对象中的另一个方法)不会导致实际事务在运行时,即使调用的方法用 |
如果您希望自行调用也用事务包装,请考虑使用 AspectJ 模式(请参阅下表中的 mode
属性)。在这种情况下,首先没有代理。相反,目标类被编织(即,它的字节码被修改),以支持在任何方法上进行 @Transactional
运行时行为。
XML Attribute | Annotation Attribute | Default | Description |
---|---|---|---|
|
N/A (see |
|
要使用的事务管理器的名称。仅当事务管理器的名称不是 |
|
|
|
默认模式 ( |
|
|
|
仅适用于 |
|
|
|
定义应用于用 |
处理 |
|
|
在评估方法的事务设置时,最派生的位置优先。在以下示例的情况下,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
Settings
@Transactional
注解是一种元数据,它指定接口、类或方法必须具有事务语义(例如,“调用此方法时,启动一个全新的只读事务,暂停任何现有事务”)。@Transactional
的默认设置如下:
-
传播设置是
PROPAGATION_REQUIRED.
-
隔离级别是
ISOLATION_DEFAULT.
-
The transaction is read-write.
-
事务超时默认设置为底层事务系统默认超时,如果不支持超时,则设置为无。
-
任何
RuntimeException
或Error
都会触发回滚,而任何已检查的Exception
则不会。
您可以更改这些默认设置。下表总结了 @Transactional
注解的各种属性:
Property | Type | Description |
---|---|---|
|
指定要使用的事务管理器的可选限定符。 |
|
|
|
Alias for |
|
|
标签可通过事务管理器评估以用特定于实现的行为与实际事务关联。 |
|
Optional propagation setting. |
|
|
|
可选隔离级别。仅适用于 |
|
|
(以秒为单位)可选的事务超时时间。仅适用于传播值 |
|
|
作为 |
|
|
读写事务与只读事务。仅适用于 |
|
必须从 |
可能导致回滚的异常类型的可选数组。 |
|
异常名称模式的数组。 |
可能导致回滚的异常名称模式的可选数组。 |
|
必须从 |
不会导致回滚的异常类型的可选数组。 |
|
异常名称模式的数组。 |
不会导致回滚的异常名称模式的可选数组。 |
请参阅 Rollback rules 以进一步了解回滚规则语义、模式以及有关基于模式的回滚规则可能无意间匹配的警告。 |
从 6.2 开始,您可以全局更改默认回滚行为——例如,通过 |
目前,您无法显式控制事务的名称,其中“名称”表示出现在事务监视器和日志输出中的事务名称。对于声明式事务,事务名称始终是事务建议类的完全限定类名 + .
+ 方法名。例如,如果 BusinessService
类的 handlePayment(..)
方法启动事务,则事务的名称将为 com.example.BusinessService.handlePayment
。
Multiple Transaction Managers with @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
限定符进行区分。默认 <tx:annotation-driven>
目标 bean 名称 transactionManager
仍用于未找到特定限定符的 TransactionManager
bean 的情况。
如果同一类上的所有事务方法共享相同的限定符,请考虑声明一个类型级别的 |
Custom Composed Annotations
如果你发现自己在许多不同的方法上重复使用相同的属性, Spring’s meta-annotation support 让你可以定义自定义组合注解以满足你的特定用例。例如,考虑以下注解定义:
-
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() {
// ...
}
}
在前一个示例中,我们使用了用于定义事务管理器限定符和事务标签的语法,但我们也可以包括传播行为、回滚规则、超时和其他功能。