1. 概述
这篇文章将深入讲解 如何正确配置 Spring 事务管理,以及如何使用 @Transactional
注解。同时,我们也会指出一些常见的“坑”,帮助大家少走弯路。
如果你对 Spring + JPA 的持久层配置还不太熟悉,建议先看看这篇基础教程:Spring with JPA tutorial。
✅ Spring 提供了两种方式来配置事务:基于注解(Annotation-based)和基于 AOP(Aspect-oriented Programming)。本文主要讨论更常见的注解方式。
2. 配置事务支持
从 Spring 3.1 开始,可以通过在 @Configuration
类上添加 @EnableTransactionManagement
来启用事务支持:
@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig {
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
//...
}
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
}
⚠️ 如果你使用的是 Spring Boot,并且项目中引入了 spring-data-*
或 spring-tx
依赖,那么事务管理默认是开启的,无需额外配置。
3. 使用 XML 配置事务
对于 Spring 3.1 之前的版本,或者不能使用 Java 配置的场景,可以使用如下 XML 配置:
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="myEmf" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
4. 使用 @Transactional 注解
配置好事务管理器后,就可以通过 @Transactional
注解在类或方法级别上启用事务:
@Service
@Transactional
public class FooService {
//...
}
该注解还支持多种配置选项:
- 事务的传播行为(Propagation Type)
- 事务的隔离级别(Isolation Level)
- 超时时间(Timeout)
- 是否只读(readOnly flag)
- 回滚规则(Rollback rules)
⚠️ 默认情况下,事务只会在 运行时异常(RuntimeException) 或 错误(Error) 时回滚,而检查型异常(Checked Exception)不会触发回滚。当然,你可以通过 rollbackFor
和 noRollbackFor
来自定义回滚规则。
5. 常见陷阱
5.1. 事务与代理机制
Spring 会为所有标注了 @Transactional
的类或方法创建代理,以便在方法执行前后注入事务逻辑。
⚠️ 但要注意:如果该类实现了接口,默认使用的是 Java 动态代理。这意味着:
- ❌ 只有通过代理调用的方法才会被拦截
- ❌ 类内部的自调用(self-invocation)不会触发事务
✅ 同时,只有 public
方法上的 @Transactional
注解才会生效。非 public
方法上的注解会被忽略。
5.2. 修改事务隔离级别
你可以通过如下方式指定隔离级别:
@Transactional(isolation = Isolation.SERIALIZABLE)
⚠️ 这个特性在 Spring 4.1 中才被正式支持。如果在 Spring 4.1 之前的版本中使用,可能会抛出如下异常:
org.springframework.transaction.InvalidIsolationLevelException: Standard JPA does not support custom isolation levels – use a special JpaDialect for your JPA implementation
5.3. 只读事务
readOnly
标志经常引起误解,尤其是在使用 JPA 时:
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
⚠️ 它只是一个“提示”,JPA 提供商不一定会遵守。即使设置了 readOnly
,也不能保证不会执行写操作。
✅ 此外,readOnly
只在事务上下文中生效。如果方法不在事务中运行,这个标志会被忽略。
5.4. 事务日志调试
调试事务相关问题时,推荐打开 Spring 事务包的日志:
logging.level.org.springframework.transaction=TRACE
5.5. 事务回滚方式
事务回滚有两种方式:声明式和编程式。
✅ 声明式回滚
通过 @Transactional
的 rollbackFor
和 noRollbackFor
属性来控制:
@Transactional
public void createCourseDeclarativeWithRuntimeException(Course course) {
courseDao.create(course);
throw new DataIntegrityViolationException("Throwing exception for demoing Rollback!!!");
}
@Transactional(rollbackFor = { SQLException.class })
public void createCourseDeclarativeWithCheckedException(Course course) throws SQLException {
courseDao.create(course);
throw new SQLException("Throwing exception for demoing rollback");
}
@Transactional(noRollbackFor = { SQLException.class })
public void createCourseDeclarativeWithNoRollBack(Course course) throws SQLException {
courseDao.create(course);
throw new SQLException("Throwing exception for demoing rollback");
}
✅ 编程式回滚
通过 TransactionAspectSupport
手动标记回滚:
public void createCourseDefaultRatingProgramatic(Course course) {
try {
courseDao.create(course);
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
6. 总结
本文介绍了如何在 Spring + JPA 项目中配置事务,包括基于 Java 和 XML 的方式。我们还深入讲解了 @Transactional
注解的使用及其常见陷阱。
📌 最佳实践建议:
- 优先使用注解方式配置事务
- 注意代理机制导致的事务失效问题
- 合理设置事务隔离级别和回滚规则
- 调试事务问题时,记得开启事务日志
如需获取本文中涉及的完整代码示例,请访问:GitHub 项目地址