对于最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

使用 @Transactional

除了基于 XML 的声明式事务配置方法外,您还可以使用基于注解的方法。直接在 Java 源代码中声明事务语义,可以使声明更贴近受影响的代码。这种方式几乎不会造成不必要的耦合,因为那些旨在以事务方式使用的代码,无论如何几乎总是以这种方式部署的。spring-doc.cadn.net.cn

标准的 jakarta.transaction.Transactional 注解也受到支持,可作为 Spring 自身注解的直接替代品。更多详细信息,请参阅 JTA 文档。

通过使用 @Transactional 注解所带来的易用性,最好通过以下示例来说明,该示例将在随后的文本中进行解释。 请考虑以下类定义:spring-doc.cadn.net.cn

// 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 将哪些方法视为事务性方法的更多详细信息,请参阅方法可见性。请注意,类级别的注解不会应用于类层次结构中的祖先类;在这种情况下,若要使继承的方法参与子类级别的注解,需要在子类中本地重新声明这些方法。spring-doc.cadn.net.cn

当像上面这样的 POJO 类在 Spring 上下文中被定义为一个 bean 时,你可以在 @EnableTransactionManagement 类中使用 @Configuration 注解,使该 bean 实例具备事务性。完整详情请参见 javadocspring-doc.cadn.net.cn

在XML配置中,<tx:annotation-driven/> 标签提供了类似的便利:spring-doc.cadn.net.cn

<!-- 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 bean 的名称为 <tx:annotation-driven/>,则可以在 TransactionManager 标签中省略 transactionManager 属性。如果要依赖注入的 TransactionManager bean 具有其他名称,则必须使用 transaction-manager 属性,如前面的示例所示。

Reactive事务方法使用响应式返回类型,与命令式编程安排不同,如下例所示:spring-doc.cadn.net.cn

// 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”一节中的 取消信号 部分。spring-doc.cadn.net.cn

方法可见性与代理模式下的 @Transactional

@Transactional 注解通常用于具有 public 可见性的方法上。 从 6.0 版本开始,默认情况下,基于类的代理也可以使 protected 或包可见(package-visible)的方法具备事务性。 请注意,在基于接口的代理中,事务性方法必须始终是 public 的,并且必须在被代理的接口中定义。 对于这两种代理,只有通过代理传入的外部方法调用才会被拦截。spring-doc.cadn.net.cn

如果你希望在不同类型的代理中对方法可见性进行一致的处理(这是 5.3 版本之前默认的行为),请考虑指定 publicMethodsOnlyspring-doc.cadn.net.cn

/**
 * Register a custom AnnotationTransactionAttributeSource with the
 * publicMethodsOnly flag set to true to consistently ignore non-public methods.
 * @see ProxyTransactionManagementConfiguration#transactionAttributeSource()
 */
@Bean
TransactionAttributeSource transactionAttributeSource() {
	return new AnnotationTransactionAttributeSource(true);
}

Spring TestContext 框架默认也支持非私有的 @Transactional 测试方法。 有关示例,请参见测试章节中的事务管理spring-doc.cadn.net.cn

您可以将 @Transactional 注解应用于接口定义、接口中的方法、类定义或类中的方法。然而,仅添加 @Transactional 注解本身并不足以激活事务行为。@Transactional 注解只是一种元数据,可由相应的运行时基础设施读取,并利用该元数据来配置具有事务行为的适当 Bean。在前面的示例中,<tx:annotation-driven/> 元素在运行时启用了实际的事务管理。spring-doc.cadn.net.cn

Spring 团队建议您将 @Transactional 注解标注在具体类的方法上,而不是依赖于接口中声明的注解方法,即使从 Spring 5.0 开始,后者在基于接口和基于目标类的代理中确实可以正常工作。由于 Java 注解不会从接口继承,因此在使用 AspectJ 模式时,织入(weaving)基础设施仍然无法识别在接口上声明的注解,导致切面无法被应用。因此,您的事务注解可能会被静默忽略:您的代码在常规情况下看似“正常工作”,直到您测试回滚场景时才会发现问题。
在代理模式下(这是默认模式),只有通过代理传入的外部方法调用才会被拦截。这意味着,即使被调用的方法标记了 @Transactional,自调用(即目标对象内部的一个方法调用该目标对象的另一个方法)在运行时也不会真正开启事务。此外,代理必须完全初始化后才能提供预期的行为,因此你不应在初始化代码中依赖此特性——例如,在 @PostConstruct 方法中。

如果你希望自调用(self-invocations)也被事务包装,可以考虑使用 AspectJ 模式(参见下表中的 mode 属性)。在这种情况下,根本不会创建代理。取而代之的是,目标类会被织入(即其字节码被修改),以支持在任意类型的方法上实现 @Transactional 的运行时行为。spring-doc.cadn.net.cn

表1. 注解驱动的事务设置
XML 属性 注解属性 默认 <description> </description>

transaction-managerspring-doc.cadn.net.cn

不适用(请参阅 TransactionManagementConfigurer Javadoc)spring-doc.cadn.net.cn

transactionManagerspring-doc.cadn.net.cn

使用事务管理器的名称。仅在事务管理器的名称不是transactionManager,如上述示例所示时才需要指定。spring-doc.cadn.net.cn

modespring-doc.cadn.net.cn

modespring-doc.cadn.net.cn

proxyspring-doc.cadn.net.cn

默认模式(proxy)使用 Spring 的 AOP 框架对带有注解的 bean 进行处理,使其被代理(遵循前面讨论过的代理语义,仅适用于通过代理传入的方法调用)。替代模式(aspectj)则使用 Spring 的 AspectJ 事务切面来织入受影响的类,修改目标类的字节码,从而适用于任何类型的方法调用。AspectJ 织入要求 classpath 中包含 spring-aspects.jar,并且启用了加载时织入(或编译时织入)。(有关如何设置加载时织入的详细信息,请参见Spring 配置。)spring-doc.cadn.net.cn

proxy-target-classspring-doc.cadn.net.cn

proxyTargetClassspring-doc.cadn.net.cn

falsespring-doc.cadn.net.cn

仅适用于proxy模式。控制为使用@Transactional注解标注的类创建何种类型的事务代理。 如果proxy-target-class属性设置为true,则创建基于类的代理。 如果proxy-target-classfalse或省略该属性,则创建标准的基于JDK接口的代理。 (有关不同代理类型的详细说明,请参见代理机制。)spring-doc.cadn.net.cn

orderspring-doc.cadn.net.cn

orderspring-doc.cadn.net.cn

Ordered.LOWEST_PRECEDENCEspring-doc.cadn.net.cn

定义应用于使用 @Transactional 注解的 bean 的事务通知的顺序。(有关 AOP 通知排序规则的更多信息,请参阅通知排序。) 未指定排序意味着由 AOP 子系统决定通知的顺序。spring-doc.cadn.net.cn

处理 @Transactional 注解的默认通知模式是 proxy, 该模式仅允许通过代理拦截方法调用。同一类内部的本地方法调用无法通过这种方式被拦截。 如需更高级的拦截模式,请考虑切换到 aspectj 模式,并结合编译时或加载时织入(weaving)使用。
proxy-target-class 属性控制为使用 @Transactional 注解标注的类创建何种类型的事务代理。如果将 proxy-target-class 设置为 true,则会创建基于类的代理;如果 proxy-target-classfalse 或省略该属性,则会创建标准的基于 JDK 接口的代理。(有关不同代理类型的讨论,请参见代理机制。)
@EnableTransactionManagement<tx:annotation-driven/> 仅在定义它们的同一应用上下文中的 bean 上查找 @Transactional 注解。 这意味着,如果你将基于注解的配置放在 WebApplicationContextDispatcherServlet 中, 它只会检查控制器(controller)中的 @Transactional bean,而不会检查服务(service)层中的 bean。 更多信息请参见MVC

在评估某个方法的事务设置时,最具体的(即最派生的)位置具有优先权。在以下示例中,DefaultFooService 类在类级别上使用了只读事务的设置进行注解,但该类中 @Transactional 方法上的 updateFoo(Foo) 注解会覆盖类级别定义的事务设置。spring-doc.cadn.net.cn

@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 设置如下:spring-doc.cadn.net.cn

您可以更改这些默认设置。以下表格概述了@Transactional 注解的各种属性:spring-doc.cadn.net.cn

表2. @Transactional 设置
<property> </property> 类型 <description> </description>

spring-doc.cadn.net.cn

Stringspring-doc.cadn.net.cn

指定要使用的事务管理器的可选注解。spring-doc.cadn.net.cn

transactionManagerspring-doc.cadn.net.cn

Stringspring-doc.cadn.net.cn

alias for value.spring-doc.cadn.net.cn

labelspring-doc.cadn.net.cn

数组形式的 String 标签,用于为交易添加表达性的描述。spring-doc.cadn.net.cn

标签可能由事务管理器评估,以便将实现特定的行为与实际的事务关联起来。spring-doc.cadn.net.cn

传播spring-doc.cadn.net.cn

enum: Propagationspring-doc.cadn.net.cn

可选传播设置。spring-doc.cadn.net.cn

isolationspring-doc.cadn.net.cn

enum: Isolationspring-doc.cadn.net.cn

可选的隔离级别。仅当传播值为REQUIREDREQUIRES_NEW 时适用。spring-doc.cadn.net.cn

timeoutspring-doc.cadn.net.cn

int(以秒为粒度)spring-doc.cadn.net.cn

可选的事务超时。仅适用于传播值为REQUIREDREQUIRES_NEW的情况。spring-doc.cadn.net.cn

timeoutStringspring-doc.cadn.net.cn

String(以秒为粒度)spring-doc.cadn.net.cn

替代指定超时(timeout)秒数的字符串值——例如,作为占位符。spring-doc.cadn.net.cn

readOnlyspring-doc.cadn.net.cn

booleanspring-doc.cadn.net.cn

读写与只读事务。仅适用于REQUIREDREQUIRES_NEW的值。spring-doc.cadn.net.cn

rollbackForspring-doc.cadn.net.cn

数组形式的Class对象,这些对象必须继承自Throwable.spring-doc.cadn.net.cn

必须导致回滚的异常类型的可选数组。spring-doc.cadn.net.cn

rollbackForClassNamespring-doc.cadn.net.cn

异常名称模式数组。spring-doc.cadn.net.cn

必须导致回滚的异常名称模式的可选数组。spring-doc.cadn.net.cn

noRollbackForspring-doc.cadn.net.cn

数组形式的Class对象,这些对象必须继承自Throwable.spring-doc.cadn.net.cn

不导致回滚的异常类型的可选数组。spring-doc.cadn.net.cn

noRollbackForClassNamespring-doc.cadn.net.cn

异常名称模式数组。spring-doc.cadn.net.cn

不导致回滚的异常名称模式的可选数组。spring-doc.cadn.net.cn

有关回滚规则的语义、模式以及基于模式的回滚规则可能意外匹配的警告等更多详细信息,请参见回滚规则

目前,您无法显式控制事务的名称,这里的“名称”指的是在事务监视器和日志输出中显示的事务名称。 对于声明式事务,事务名称始终是被事务增强类的全限定类名 + . + 方法名。例如,如果 handlePayment(..) 类的 BusinessService 方法启动了一个事务,那么该事务的名称将是:com.example.BusinessService.handlePaymentspring-doc.cadn.net.cn

多个事务管理器,适用于@Transactional

大多数 Spring 应用程序只需要一个事务管理器,但在某些情况下,你可能希望在单个应用程序中使用多个独立的事务管理器。你可以选择性地使用 value 注解的 transactionManager@Transactional 属性来指定要使用的 TransactionManager 的标识。该标识可以是事务管理器 bean 的名称,也可以是其限定符(qualifier)值。例如,通过使用限定符注解,你可以将以下 Java 代码与应用程序上下文中声明的以下事务管理器 bean 结合使用:spring-doc.cadn.net.cn

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> {
		// ...
	}
}

<p>以下列表显示了 Bean 声明:</p>spring-doc.cadn.net.cn

<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 上的各个方法将分别在不同的事务管理器下运行,这些事务管理器通过 orderaccountreactive-account 限定符加以区分。如果未找到带有特定限定符的 <tx:annotation-driven> bean,则仍会使用默认的 transactionManager 目标 bean 名称 TransactionManagerspring-doc.cadn.net.cn

自定义组合注解

如果你发现自己在许多不同的方法上反复使用相同的属性与 @Transactional 注解,Spring 的元注解支持 允许你为特定的使用场景定义自定义的组合注解。例如,请考虑以下注解定义:spring-doc.cadn.net.cn

@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

前面的注解让我们可以将上一节的例子编写如下:spring-doc.cadn.net.cn

public class TransactionalService {

	@OrderTx
	public void setSomething(String name) {
		// ...
	}

	@AccountTx
	public void doSomething() {
		// ...
	}
}
class TransactionalService {

	@OrderTx
	fun setSomething(name: String) {
		// ...
	}

	@AccountTx
	fun doSomething() {
		// ...
	}
}

在前面的示例中,我们使用语法定义了事务管理器限定符和事务注解标签,但我们也可以包括传播行为、回滚规则、超时和其他特性。spring-doc.cadn.net.cn