使用@Transactional管理实务遇到的坑

景:

一个spring老项目,遇到使用@Transactional事务不起作用问题,这里记录一下。

最开始在类上加@Transactional注解

@Transactional
public class Test {
    ...
}

把@Transactional注解放在类级别时,表示所有该类的公共方法都配置相同的事务属性信息。 使用@Transactional 不指定 rollback-for对应异常类,默认抛出 RuntimeException异常时才会回滚,try catch捕获异常不会回滚。

尝试一把抛RuntimeException异常数据依然创建成功,事务没有起作用。

经过一番排查,发现spring-content.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="load*" read-only="true" />
    <tx:method name="get*" read-only="true" />
    <tx:method name="select*" read-only="true" />
    <tx:method name="create*" propagation="NOT_SUPPORTED"
      rollback-for="java.lang.Exception" />  
  </tx:attributes>
</tx:advice> 

配置文件不好更改,不过@Transactional颗粒度支持类或者方法配置。

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public class Test {
    ...
}

这样事务就生效了,目标方法中抛出的异常是 rollbackFor 指定的异常的子类,事务同样会回滚。

有时我们不能直接暴露异常,就需要手动回滚。代码:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();。

比如

try {
    ...
} catch (Exception e) {
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    ...
}

Spring 支持两种类型的事务管理,分别是编程式和声明式。上面提到使用注解 @Transactional 为声明式事务。

使用声明式事务,对于公用方法生效,事务管理范围为整个方法。如果需要颗粒度更小的管理事务,需要编程式介入,编程式需要手动维护事务,但也更灵活。

示例代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    @Autowired
    PlatformTransactionManager transactionManager;


    public void create(String name, Integer age, Integer marks, Integer year){
      TransactionDefinition def = new DefaultTransactionDefinition();
      TransactionStatus status = transactionManager.getTransaction(def);
      try {
         String SQL1 = "insert into Student (name, age) values (?, ?)";
         jdbcTemplateObject.update( SQL1, name, age);
         // Get the latest student id to be used in Marks table
         String SQL2 = "select max(id) from Student";
         int sid = jdbcTemplateObject.queryForInt( SQL2 );
         String SQL3 = "insert into Marks(sid, marks, year) " +
                       "values (?, ?, ?)";
         jdbcTemplateObject.update( SQL3, sid, marks, year);
         System.out.println("Created Name = " + name + ", Age = " + age);
         transactionManager.commit(status);
      } catch (DataAccessException e) {
         System.out.println("Error in creating record, rolling back");
         transactionManager.rollback(status);
         throw e;
      }
      return;
    }

参考文档

updatedupdated2020-12-242020-12-24
Load Comments?