Spring 事务管理
Spring 事务管理相关接口的关系如下:

事务管理概述
Spring 为 JDBC、JPA、Hibernate、JTA 等不同数据访问技术提供统一的事务编程模型。业务代码主要面向 Spring 的事务抽象,而具体的提交、回滚和资源管理由相应事务管理器完成。
核心接口是 PlatformTransactionManager:
public interface PlatformTransactionManager extends TransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
getTransaction() 根据事务定义获取当前事务状态;commit() 和 rollback() 分别负责提交与回滚。
常见事务管理器
JDBC 事务
使用 JDBC 或 JdbcTemplate 时,通常使用 DataSourceTransactionManager。
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
它通过同一数据源获取的 JDBC Connection 管理事务,并最终调用连接的 commit() 或 rollback()。
JPA 事务
使用 JPA 时,通常使用 JpaTransactionManager。
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
JpaTransactionManager 与 JPA 的 EntityManagerFactory、EntityManager 协作管理事务。
Hibernate 事务
直接使用原生 Hibernate SessionFactory 时,可以使用 HibernateTransactionManager。如果项目通过 JPA 使用 Hibernate 实现,则应优先使用 JpaTransactionManager。
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
JTA 事务
当一个业务操作需要协调多个事务资源时,可以使用 JTA 和 JtaTransactionManager。例如,一个事务同时涉及多个数据库或数据库与消息系统。
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager"/>
JTA 的具体配置依赖应用服务器或所使用的事务协调器,不能仅靠一个固定 Bean 配置适配所有环境。
事务属性
TransactionDefinition 用于描述事务策略,主要包含传播行为、隔离级别、超时时间、只读标记和事务名称。

public interface TransactionDefinition {
int getPropagationBehavior();
int getIsolationLevel();
int getTimeout();
boolean isReadOnly();
String getName();
}事务传播行为
传播行为决定一个带事务的方法被其他事务方法调用时,应加入现有事务、创建新事务,还是禁止事务。
| 传播行为 | 含义 |
|---|---|
REQUIRED | 默认值。有事务则加入,没有事务则新建事务。 |
SUPPORTS | 有事务则加入,没有事务则以非事务方式运行。 |
MANDATORY | 必须存在事务,否则抛出异常。 |
REQUIRES_NEW | 总是创建新事务;若已有事务,则暂停外层事务。 |
NOT_SUPPORTED | 以非事务方式运行;若已有事务,则暂停该事务。 |
NEVER | 必须以非事务方式运行;若存在事务,则抛出异常。 |
NESTED | 有事务时使用保存点形成嵌套范围;没有事务时行为类似 REQUIRED。 |
REQUIRED
多个方法加入同一个物理事务,其中任意参与者把事务标记为仅回滚,最终提交时整个事务都会回滚。
@Transactional(propagation = Propagation.REQUIRED)
public void saveOrder() {
// 业务逻辑
}
REQUIRES_NEW
内层方法使用独立事务,外层事务在内层事务执行期间被挂起。内层事务提交后,即使外层事务随后回滚,内层已提交内容通常不会随之回滚。
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveAuditLog() {
// 独立保存审计日志
}
NESTED
NESTED 通常基于同一个物理事务中的 JDBC 保存点实现。内层范围可以回滚到保存点,但它不能像独立事务一样脱离外层事务单独最终提交;外层事务回滚时,嵌套范围也会回滚。
该传播行为依赖事务管理器和底层资源对保存点的支持,常见于 JDBC 资源事务。
事务隔离级别
隔离级别用于控制并发事务之间的数据可见性。
并发事务常见问题
- 脏读:读取到其他事务尚未提交的数据。
- 不可重复读:同一事务内两次读取同一行,得到不同值,通常由其他事务更新并提交引起。
- 幻读:同一事务内按相同条件查询两次,结果集中的行数发生变化,通常由其他事务插入或删除并提交引起。
Spring 隔离级别
| 隔离级别 | 含义 |
|---|---|
DEFAULT | 使用数据库默认隔离级别。 |
READ_UNCOMMITTED | 允许读取未提交数据,可能出现脏读、不可重复读和幻读。 |
READ_COMMITTED | 只能读取已提交数据,可避免脏读。 |
REPEATABLE_READ | 同一事务内重复读取已读取行时保持一致,可避免脏读和不可重复读。 |
SERIALIZABLE | 事务近似串行执行,隔离程度最高,但并发性能通常最低。 |
不同数据库的锁机制和多版本并发控制实现不同,因此同名隔离级别在具体细节上可能存在差异。
@Transactional(isolation = Isolation.READ_COMMITTED)
public void updateAccount() {
// 业务逻辑
}
只读事务
只读标记表示该事务主要执行查询操作,事务管理器和数据库可以据此进行优化。
@Transactional(readOnly = true)
public User queryById(Long id) {
return userMapper.selectById(id);
}
readOnly = true 通常是一种优化提示,不应把它当作绝对的数据库写入安全限制。是否真正禁止写操作取决于事务管理器和数据库实现。
事务超时
事务超时用于限制事务允许执行的最长时间。超时后,事务通常会被标记为回滚。
@Transactional(timeout = 10)
public void importData() {
// 业务逻辑
}
应避免在事务中执行长时间网络调用、文件处理或人为等待,否则会长期占用数据库连接和锁资源。
回滚规则
Spring 声明式事务默认在抛出 RuntimeException 或 Error 时回滚;对于普通受检异常,默认不回滚。
@Transactional(rollbackFor = Exception.class)
public void transfer() throws Exception {
// 发生受检异常时也回滚
}
也可以通过 noRollbackFor 指定某些异常不触发回滚。
@Transactional(noRollbackFor = BusinessWarningException.class)
public void execute() {
// 业务逻辑
}
回滚规则应根据业务语义设置,不建议无差别捕获异常后静默处理。若异常被方法内部捕获且不再抛出,事务拦截器通常无法感知该异常,也就不会按异常自动回滚。
事务状态
TransactionStatus 表示当前事务的运行状态,并提供保存点和仅回滚标记等能力。
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
boolean hasSavepoint();
void flush();
}
常见状态方法包括:
isNewTransaction():是否为当前调用新建了事务。hasSavepoint():是否持有保存点。setRollbackOnly():把事务标记为只能回滚。isRollbackOnly():事务是否已被标记为回滚。isCompleted():事务是否已经完成。
接口的具体方法会随 Spring 版本演进,使用时应以当前项目依赖中的 API 为准。
编程式事务
编程式事务由业务代码明确控制事务边界,适合事务范围需要动态决定或精细控制的场景。它的缺点是事务代码会进入业务逻辑,侵入性较强。
使用 TransactionTemplate
TransactionTemplate 是推荐的编程式事务方式之一。
@Service
public class UserService {
private final TransactionTemplate transactionTemplate;
private final UserRepository userRepository;
public UserService(
TransactionTemplate transactionTemplate,
UserRepository userRepository) {
this.transactionTemplate = transactionTemplate;
this.userRepository = userRepository;
}
public Boolean save(User user) {
return transactionTemplate.execute(status -> {
try {
userRepository.insert(user);
return true;
} catch (RuntimeException e) {
status.setRollbackOnly();
throw e;
}
});
}
}如果回调抛出运行时异常,事务模板会按规则回滚。只有在需要把异常转换为正常返回值时,才需要显式调用 setRollbackOnly()。
直接使用 PlatformTransactionManager
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = transactionManager.getTransaction(definition);
try {
userRepository.insert(user);
transactionManager.commit(status);
} catch (RuntimeException e) {
transactionManager.rollback(status);
throw e;
}直接操作事务管理器更灵活,但需要自己保证提交、回滚和异常传播逻辑正确。
声明式事务
声明式事务基于 Spring AOP,通过配置或注解把事务逻辑应用到目标方法,业务代码不需要手动提交和回滚。
XML 配置方式
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="query*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="servicePointcut"
expression="execution(* com.example.service..*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="servicePointcut"/>
</aop:config>需要在根元素中声明 tx 和 aop 命名空间。
注解方式
先启用事务注解:
<tx:annotation-driven transaction-manager="transactionManager"/>
也可以使用 Java 配置:
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
在业务类或方法上使用 @Transactional:
@Service
public class UserService {
private final UserDao userDao;
public UserService(UserDao userDao) {
this.userDao = userDao;
}
@Transactional(rollbackFor = Exception.class)
public void save(User user) throws Exception {
userDao.insert(user);
}
}类上的注解会作为该类方法的默认事务配置;方法上的注解可以覆盖类级配置。
@Transactional 常见失效场景
同类内部调用
默认代理模式下,一个方法通过 this 调用同类中的另一个 @Transactional 方法时,调用没有经过 Spring 代理,内层方法的独立事务配置通常不会生效。
@Service
public class OrderService {
public void createOrder() {
// 这是同类内部调用,不会经过代理对象。
saveOrder();
}
@Transactional
public void saveOrder() {
// 数据库操作
}
}通常应把需要独立事务边界的方法拆分到另一个 Spring Bean 中。
方法不可代理
在常见代理模式下,事务通常应应用于可被代理调用的公开实例方法。private 方法、静态方法和对象自行创建的方法不会按普通 Spring Bean 代理方式生效。
对象不是 Spring Bean
通过 new 手动创建的对象不受 Spring 容器管理,其 @Transactional 注解不会自动生效。
异常被捕获
@Transactional
public void save() {
try {
userDao.insert();
} catch (Exception e) {
// 异常被吞掉后,事务拦截器无法按该异常自动回滚。
}
}
应重新抛出异常,或根据业务需要调用 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()。
异常类型不匹配
受检异常默认不会触发回滚。需要回滚时,应通过 rollbackFor 明确配置。
使用建议
- 事务通常放在 Service 层,而不是 Controller 层。
- 一个事务只覆盖必须保持一致性的数据库操作。
- 避免在事务中进行长时间远程调用。
- 查询方法可设置为只读事务。
- 根据业务语义选择传播行为,不要把所有方法都配置成
REQUIRES_NEW。 - 对异常进行转换时,应保留原始异常信息,并确保回滚规则仍然生效。
喜欢的话,留下你的评论吧~