Contents

使用JPA配置 Spring事务

1.概述

本教程将讨论配置 Spring Transactions 的正确方法、如何使用*@Transactional*注解以及常见的陷阱。 有关核心持久性配置的更深入讨论,请查看Spring with JPA 教程

基本上,有两种不同的方式来配置事务、注解和 AOP,每一种方式都有自己的优势。我们将在这里讨论更常见的注释配置。

2. 配置事务

Spring 3.1 引入了*@EnableTransactionManagement*注解,我们可以在*@Configuration*类中使用它来启用事务支持:

@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig{
   @Bean
   public LocalContainerEntityManagerFactoryBean
     entityManagerFactoryBean(){
      //...
   }
   @Bean
   public PlatformTransactionManager transactionManager(){
      JpaTransactionManager transactionManager
        = new JpaTransactionManager();
      transactionManager.setEntityManagerFactory(
        entityManagerFactoryBean().getObject() );
      return transactionManager;
   }
}

但是,如果我们使用的是 Spring Boot 项目并且类路径上有 spring-data-* 或 spring-tx 依赖项,则默认情况下将启用事务管理

3. 使用 XML 配置事务

对于 3.1 之前的版本,或者如果 Java 不是一个选项,这里是使用annotation-driven和命名空间支持的 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*注解 bean:

@Service
@Transactional
public class FooService {
    //...
}

注释还支持进一步的配置

  • 交易的传播类型
  • 事务的隔离级别
  • 事务操作的超时
  • 一个readOnly标志——提示持久性提供者事务应该是只读的
  • 事务的回滚规则

请注意,默认情况下,回滚仅发生在运行时,未经检查的异常。检查的异常不会触发事务的回滚。当然,我们可以使用rollbackFornoRollbackFor注释参数来配置这种行为。

5. 潜在的陷阱

5.1. 交易和代理

在高层次上, Spring 为所有使用@Transactional*注解的类创建代理*,无论是在类上还是在任何方法上。代理允许框架在运行方法之前和之后注入事务逻辑,主要用于启动和提交事务。

需要记住的重要一点是,如果事务性 bean 正在实现一个接口,默认情况下代理将是一个 Java 动态代理。这意味着只有通过代理传入的外部方法调用才会被拦截。**任何自调用都不会启动任何事务,*即使该方法具有@Transactional*注解。

使用代理的另一个注意事项是**只有公共方法才应该使用*@Transactional*进行注解。**任何其他可见性的方法将简单地忽略注解,因为它们没有被代理。

5.2. 更改隔离级别

courseDao.createWithRuntimeException(course);

我们还可以更改事务隔离级别:

@Transactional(isolation = Isolation.SERIALIZABLE)

请注意,这实际上已在 Spring 4.1中引入 ;如果我们在 Spring 4.1 之前运行上面的示例,它将导致:

org.springframework.transaction.InvalidIsolationLevelException:标准 JPA 不支持自定义隔离级别 -为您的 JPA 实现使用特殊的JpaDialect

5.3. 只读事务

readOnly标志通常会产生混淆,尤其是在使用 JPA 时。来自 Javadoc:

这只是作为实际事务子系统的提示;它不一定会导致写访问尝试失败。无法解释只读提示的事务管理器在请求只读事务时不会抛出异常。

事实是,**当设置了readOnly标志时,我们不能确定不会发生插入或更新。**此行为取决于供应商,而 JPA 与供应商无关。

**了解readOnly标志仅在事务内部相关也很重要。**如果操作发生在事务上下文之外,则简单地忽略该标志。一个简单的示例将调用一个带有以下注释的方法:

@Transactional( propagation = Propagation.SUPPORTS,readOnly = true )

在非事务性上下文中,不会创建事务并且readOnly标志将被忽略。

5.4. 事务记录

了解事务相关问题的一种有用方法是微调事务包中的日志记录。Spring中相关的包是“ org.springframework.transaction”,需要配置一个TRACE的日志级别。

5.5. 事务回滚

@Transactional注释是指定方法上事务的语义的元数据。我们有两种回滚事务的方法:声明式和编程式。

声明式方法中,我们使用*@Transactional*注解对方法进行注释@Transactional注释使用属性rollbackForrollbackForClassName来回滚事务,并使用属性noRollbackFornoRollbackForClassName来避免对列出的异常进行回滚。

声明式方法中的默认回滚行为将在运行时异常时回滚。

让我们看一个使用声明性方法回滚事务以处理运行时异常或错误的简单示例:

@Transactional
public void createCourseDeclarativeWithRuntimeException(Course course) {
    courseDao.create(course);
    throw new DataIntegrityViolationException("Throwing exception for demoing Rollback!!!");
}

接下来,我们将使用声明性方法为列出的检查异常回滚事务。我们示例中的回滚是在SQLException上:

@Transactional(rollbackFor = { SQLException.class })
public void createCourseDeclarativeWithCheckedException(Course course) throws SQLException {
    courseDao.create(course);
    throw new SQLException("Throwing exception for demoing rollback");
}

让我们看一下在声明性方法中对属性noRollbackFor的简单使用,以防止所列异常的事务回滚:

@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();
    }
}

声明性回滚策略应该优于程序化回滚策略