Using @Transactional
除了基于 XML 的事务配置声明式方法之外,你还可以使用基于注释的方法。在 Java 源代码中直接声明事务语义将声明与受影响的代码紧密地联系在一起。没有过度耦合的太大危险,因为打算以事务方式使用的代码几乎总是以这种方式部署的。
In addition to the XML-based declarative approach to transaction configuration, you can use an annotation-based approach. Declaring transaction semantics directly in the Java source code puts the declarations much closer to the affected code. There is not much danger of undue coupling, because code that is meant to be used transactionally is almost always deployed that way anyway.
标准 |
The standard |
通过使用 @Transactional
注释提供的易用性最好通过一个示例来说明,该示例在后面的文本中进行了说明。考虑以下类定义:
The ease-of-use afforded by the use of the @Transactional
annotation is best
illustrated with an example, which is explained in the text that follows.
Consider the following class definition:
-
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 认为是事务性方法的更多详细信息。请注意,类级注解不适用于类层次结构上方的祖先类;在这种情况下,需要在本地重新声明继承的方法,以便参与子类级别的注解。
Used at the class level as above, the annotation indicates a default for all methods of the declaring class (as well as its subclasses). Alternatively, each method can be annotated individually. See method visibility for further details on which methods Spring considers transactional. Note that a class-level annotation does not apply to ancestor classes up the class hierarchy; in such a scenario, inherited methods need to be locally redeclared in order to participate in a subclass-level annotation.
当像上面那样的 POJO 类在 Spring 上下文中被定义为 Bean 时,您可以通过 @Configuration
类中的 @EnableTransactionManagement
注解使 Bean 实例成为事务性的。请参阅https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/annotation/EnableTransactionManagement.html[javadoc] 以了解全部详细信息。
When a POJO class such as the one above is defined as a bean in a Spring context,
you can make the bean instance transactional through an @EnableTransactionManagement
annotation in a @Configuration
class. See the
javadoc
for full details.
在 XML 配置中,<tx:annotation-driven/>
标记提供了类似的便利:
In XML configuration, the <tx:annotation-driven/>
tag provides similar convenience:
<!-- 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 | The line that makes the bean instance transactional. |
如果要连接的 |
You can omit the |
反应式事务方法使用反应式返回值类型,这与命令式编程安排相反,如下所示:
Reactive transactional methods use reactive return types in contrast to imperative programming arrangements as the following listing shows:
-
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 一节。
Note that there are special considerations for the returned Publisher
with regards to
Reactive Streams cancellation signals. See the
Cancel Signals
section under "Using the TransactionalOperator" for more details.
Method visibility and
@Transactional in proxy mode
The 如果希望对不同种类的代理一致地处理方法可见性(这是 5.3 之前默认的做法),请考虑指定 If you prefer consistent treatment of method visibility across the different kinds of
proxies (which was the default up until 5.3), consider specifying
Spring TestContext Framework 默认情况下也支持非私有的 The Spring TestContext Framework supports non-private |
您可以将 @Transactional
注解应用于接口定义、接口上的方法、类定义或类上的方法。但是,只有 @Transactional
注解本身还不足以激活事务行为。@Transactional
注解只是一种元数据,相应的运行时基础结构可以使用这些元数据,并使用该元数据配置具有事务行为的适当 bean。在前面的示例中,<tx:annotation-driven/>
元素会在运行时开启实际的事务管理。
You can apply the @Transactional
annotation to an interface definition, a method
on an interface, a class definition, or a method on a class. However, the mere presence
of the @Transactional
annotation is not enough to activate the transactional behavior.
The @Transactional
annotation is merely metadata that can be consumed by corresponding
runtime infrastructure which uses that metadata to configure the appropriate beans with
transactional behavior. In the preceding example, the <tx:annotation-driven/>
element
switches on actual transaction management at runtime.
Spring 团队建议使用 |
The Spring team recommends that you annotate methods of concrete classes with the
|
在代理模式(它是默认模式)中,仅拦截通过代理传入的外部方法调用。这意味着自调用(实际上,目标对象中的方法调用目标对象中的另一个方法)不会导致实际事务在运行时,即使调用的方法用 |
In proxy mode (which is the default), only external method calls coming in through
the proxy are intercepted. This means that self-invocation (in effect, a method within
the target object calling another method of the target object) does not lead to an actual
transaction at runtime even if the invoked method is marked with |
如果您希望自行调用也用事务包装,请考虑使用 AspectJ 模式(请参阅下表中的 mode
属性)。在这种情况下,首先没有代理。相反,目标类被编织(即,它的字节码被修改),以支持在任何方法上进行 @Transactional
运行时行为。
Consider using AspectJ mode (see the mode
attribute in the following table) if you
expect self-invocations to be wrapped with transactions as well. In this case, there is
no proxy in the first place. Instead, the target class is woven (that is, its byte code
is modified) to support @Transactional
runtime behavior on any kind of method.
XML Attribute | Annotation Attribute | Default | Description |
---|---|---|---|
|
N/A (see |
|
Name of the transaction manager to use. Required only if the name of the transaction
manager is not |
|
|
|
The default mode ( |
|
|
|
Applies to |
|
|
|
Defines the order of the transaction advice that is applied to beans annotated with
|
处理 |
The default advice mode for processing |
|
The |
|
|
在评估方法的事务设置时,最派生的位置优先。在以下示例的情况下,DefaultFooService
类在类级别上使用只读事务的设置进行注释,但是同一类中的 updateFoo(Foo)
方法上的 @Transactional
注解优先于在类级别定义的事务设置。
The most derived location takes precedence when evaluating the transactional settings
for a method. In the case of the following example, the DefaultFooService
class is
annotated at the class level with the settings for a read-only transaction, but the
@Transactional
annotation on the updateFoo(Foo)
method in the same class takes
precedence over the transactional settings defined at the class level.
-
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
的默认设置如下:
The @Transactional
annotation is metadata that specifies that an interface, class,
or method must have transactional semantics (for example, "start a brand new read-only
transaction when this method is invoked, suspending any existing transaction").
The default @Transactional
settings are as follows:
-
The propagation setting is
PROPAGATION_REQUIRED.
-
The isolation level is
ISOLATION_DEFAULT.
-
The transaction is read-write.
-
The transaction timeout defaults to the default timeout of the underlying transaction system, or to none if timeouts are not supported.
-
Any
RuntimeException
orError
triggers rollback, and any checkedException
does not.
您可以更改这些默认设置。下表总结了 @Transactional
注解的各种属性:
You can change these default settings. The following table summarizes the various
properties of the @Transactional
annotation:
Property | Type | Description |
---|---|---|
|
Optional qualifier that specifies the transaction manager to be used. |
|
|
|
Alias for |
|
Array of |
Labels may be evaluated by transaction managers to associate implementation-specific behavior with the actual transaction. |
|
Optional propagation setting. |
|
|
|
Optional isolation level. Applies only to propagation values of |
|
|
Optional transaction timeout. Applies only to propagation values of |
|
|
Alternative for specifying the |
|
|
Read-write versus read-only transaction. Only applicable to values of |
|
Array of |
Optional array of exception types that must cause rollback. |
|
Array of exception name patterns. |
Optional array of exception name patterns that must cause rollback. |
|
Array of |
Optional array of exception types that must not cause rollback. |
|
Array of exception name patterns. |
Optional array of exception name patterns that must not cause rollback. |
请参阅 Rollback rules 以进一步了解回滚规则语义、模式以及有关基于模式的回滚规则可能无意间匹配的警告。 |
See Rollback rules for further details on rollback rule semantics, patterns, and warnings regarding possible unintentional matches for pattern-based rollback rules. |
从 6.2 开始,您可以全局更改默认回滚行为——例如,通过 As of 6.2, you can globally change the default rollback behavior – for example, through
请注意,特定于事务的回滚规则会覆盖默认行为,但会保留对未指定异常所选的默认行为。这适用于 Spring 的 Note that transaction-specific rollback rules override the default behavior but
retain the chosen default for unspecified exceptions. This is the case for
Spring’s 除非依赖具有提交行为的 EJB 风格业务异常,否则建议即使在(潜在意外)经检查异常的情况下,也切换到 Unless you rely on EJB-style business exceptions with commit behavior, it is
advisable to switch to |
目前,您无法显式控制事务的名称,其中“名称”表示出现在事务监视器和日志输出中的事务名称。对于声明式事务,事务名称始终是事务建议类的完全限定类名 + .
+ 方法名。例如,如果 BusinessService
类的 handlePayment(..)
方法启动事务,则事务的名称将为 com.example.BusinessService.handlePayment
。
Currently, you cannot have explicit control over the name of a transaction, where 'name'
means the transaction name that appears in a transaction monitor and in logging output.
For declarative transactions, the transaction name is always the fully-qualified class
name of the transactionally advised class + .
+ the method name. For example, if the
handlePayment(..)
method of the BusinessService
class started a transaction, the
name of the transaction would be com.example.BusinessService.handlePayment
.
Multiple Transaction Managers with @Transactional
大多数 Spring 应用程序只需一个事务管理器,但在某些情况下,您可能希望在一个应用程序中有多个独立的事务管理器。您可以使用 @Transactional
注解的 value
或 transactionManager
属性来选择性地指定要使用的 TransactionManager
的标识。这可以是交易管理器 bean 的 bean 名称或限定符值。例如,使用限定符表示法时,可以将以下 Java 代码与应用程序上下文中以下事务管理器 bean 声明结合使用:
Most Spring applications need only a single transaction manager, but there may be
situations where you want multiple independent transaction managers in a single
application. You can use the value
or transactionManager
attribute of the
@Transactional
annotation to optionally specify the identity of the
TransactionManager
to be used. This can either be the bean name or the qualifier value
of the transaction manager bean. For example, using the qualifier notation, you can
combine the following Java code with the following transaction manager bean declarations
in the application context:
-
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 声明:
The following listing shows the bean declarations:
<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 的情况。
In this case, the individual methods on TransactionalService
run under separate
transaction managers, differentiated by the order
, account
, and reactive-account
qualifiers. The default <tx:annotation-driven>
target bean name, transactionManager
,
is still used if no specifically qualified TransactionManager
bean is found.
如果同一类上的所有事务方法共享相同的限定符,请考虑声明一个类型级别的 If all transactional methods on the same class share the same qualifier, consider
declaring a type-level 此类类型级别限定符可以在具体类上声明,也可以应用于基类中的事务定义。这实际上覆盖了任何不合格基类方法的默认事务管理器选择。 Such a type-level qualifier can be declared on the concrete class, applying to transaction definitions from a base class as well. This effectively overrides the default transaction manager choice for any unqualified base class methods. 最后但并非最不重要的是,这样的类型级bean限定符可以起到多种用途,例如:当值为“order”时,可以用它进行自动注入(识别order存储库)以及事务管理器选择,只要用于自动注入的目标bean以及关联的事务管理器定义都声明了相同的限定符值即可。这样的限定符值只需在匹配类型的bean集合中才是唯一的,而不必用作ID。 Last but not least, such a type-level bean qualifier can serve multiple purposes, e.g. with a value of "order" it can be used for autowiring purposes (identifying the order repository) as well as transaction manager selection, as long as the target beans for autowiring as well as the associated transaction manager definitions declare the same qualifier value. Such a qualifier value only needs to be unique within a set of type-matching beans, not having to serve as an ID. |
Custom Composed Annotations
如果你发现自己在许多不同的方法上重复使用相同的属性, Spring’s meta-annotation support 让你可以定义自定义组合注解以满足你的特定用例。例如,考虑以下注解定义:
If you find you repeatedly use the same attributes with @Transactional
on many different methods,
Spring’s meta-annotation support
lets you define custom composed annotations for your specific use cases. For example, consider the
following annotation definitions:
-
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
前面的注释让我们可以将上一节中的示例编写成如下内容:
The preceding annotations let us write the example from the previous section as follows:
-
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() {
// ...
}
}
在前一个示例中,我们使用了用于定义事务管理器限定符和事务标签的语法,但我们也可以包括传播行为、回滚规则、超时和其他功能。
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.