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)不会触发回滚。当然,你可以通过 rollbackFornoRollbackFor 来自定义回滚规则。


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. 事务回滚方式

事务回滚有两种方式:声明式和编程式。

✅ 声明式回滚

通过 @TransactionalrollbackFornoRollbackFor 属性来控制:

@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 项目地址



原始标题:Transactions with Spring and JPA

» 下一篇: Spring中的REST分页