Spring事务

138 阅读6分钟

Spring事务

参考:

blog.csdn.net/yuanlaijike…

小姐姐非要问我:spring编程式事务是啥? (qq.com)

Spring系列第44篇:详解spring声明式事务(@Transactional)路人甲Java的博客-CSDN博客spring 声明式事务

1.事务分类

jdbcTemplate:

一个模块,对jdbc操作进行了封装,使其更简单,就是本文要讲的JdbcTemplate,JdbcTemplate是Spring对JDBC的封装,目的是使JDBC更加易于使用。

  • 编程式事务:程序员自己控制事务

    • 方式1:通过PlatformTransactionManager控制事务
    • 方式2:通过TransactionTemplate控制事务
  • 声明式事务:使用注解或配置文件声明事务,不关心事务具体流程:获取连接,关闭连接,事务提交,回滚,异常处理

    声明式事务的缺点:最小粒度为方法

编程式事务模型:

    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    //1.定义事务管理器,给其指定一个数据源(可以把事务管理器想象为一个人,这个人来负责事务的控制操作)
    PlatformTransactionManager platformTransactionManager = new DataSourceTransactionManager(dataSource);
    //2.定义事务属性:TransactionDefinition,TransactionDefinition可以用来配置事务的属性信息,比如事务隔离级别、事务超时时间、事务传播方式、是否是只读事务等等。
    TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
    //3.开启事务:调用platformTransactionManager.getTransaction开启事务操作,得到事务状态(TransactionStatus)对象
    TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
    //4.执行业务操作,下面就执行2个插入操作
    try {
        System.out.println("before:" + jdbcTemplate.queryForList("SELECT * from t_user"));
        jdbcTemplate.update("insert into t_user (name) values (?)", "test1-1");
        jdbcTemplate.update("insert into t_user (name) values (?)", "test1-2");
        //5.提交事务:platformTransactionManager.commit
        platformTransactionManager.commit(transactionStatus);
    } catch (Exception e) {
        //6.回滚事务:platformTransactionManager.rollback
        platformTransactionManager.rollback(transactionStatus);
    }

2.Spring事务的5个要素

隔离级别,传播行为,回滚规则,事务超时,只读

隔离级别

与数据的级别完全一致,如果spring配置的隔离级别和数据库冲突的时候,以spring为准

级别含义
ISOLATION_DEFAULT使用数据库默认的隔离级别(Mysql:Repeatable_read)
ISOLATION_READ_UNCOMMITTED读未提交,脏读、幻读或不可重复读
ISOLATION_READ_COMMITTED读已提交,幻读或不可重复读
ISOLATION_REPEATABLE_READ可重复读,幻读
ISOLATION_SERIALIZABLE可穿化

传播行为

事务中调用了另一个事务,那么到底是新开一个事务,还是继续在这个事务内部处理,亦或是其他等等,这都是由传播行为所决定的

传播行为含义
PROPAGATION_REQUIRED当前方法必须运行在事务中。 如果当前存在事务,加入已存在的事务中,否则启动一个新事务。
PROPAGATION_SUPPORTS当前方法支持运行在事务中。 如果当前存在事务,加入已存在的事务中,否则非事务运行。
PROPAGATION_MANDATORY当前方法必须运行在事务中。 如果当前存在事务,加入已存在的事务中,否则抛出异常。
PROPAGATION_REQUIRED_NEW当前方法必须运行在自己的事务中。 如果当前存在事务,挂起已存在的事务中,启动一个新事务执行。
PROPAGATION_NOT_SUPPORTED当前方法不支持运行在事务中。 如果当前存在事务,挂起已存在的事务中,非事务运行。
PROPAGATION_NEVER当前方法不能运行在事务中。 如果当前存在事务,抛出异常。
PROPAGATION_NESTED类似于 PROPAGATION_REQUIRED_NEW,但前者是两个独立的事务,而它是嵌套事务。

PROPAGATION_REQUIRES_NEW 启动一个新的,不依赖于环境的 “内部” 事务。这个事务将被完全 commited 或 rolled back 而不依赖于外部事务,它拥有自己的隔离范围, 自己的锁等等。当内部事务开始执行时,外部事务将被挂起, 内务事务结束时,外部事务将继续执行。 PROPAGATION_NESTED 开始一个 “嵌套的” 事务,它是已经存在事务的一个真正的子事务。 嵌套事务开始执行时,它将取得一个 savepoint

回滚规则

指定哪些异常需要做事务回滚

事务超时

事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。

@Transactional(timeout = 1) 单位s -1代表不设置超时时间

只读

如果事务只对数据库进行读操作,数据库可以利用事务的只读特性来进行一些特定的优化,例如优化掉不必要的锁操作等等。

3.声明式事务使用的5个步骤

  • 1.开启Spring事务管理,在配置类上增加 @EnableTransactionManagerment注解

当spring容器启动的时候,发现有@EnableTransactionManagement注解,此时会拦截所有bean的创建,扫描看一下bean上是否有@Transaction注解(类、或者父类、或者接口、或者方法中有这个注解都可以),如果有这个注解,spring会通过aop的方式给bean生成代理对象,代理对象中会增加一个拦截器,拦截器会拦截bean中public方法执行,会在方法执行之前启动事务,方法执行完毕之后提交或者回滚事务。

  • 2.定义事务管理器

Spring不会直接对事务进行管理,而是通过事务管理器接口PlatformTransactionManager完成

TransactionManagerDescription
DataSourceTransactionManager适用 JDBC 或 iBatis(MyBatis)
HibernateTransactionManager适用 Hibernate 3 及以上版本
JpaTransactionManager适用 JPA
JtaTransactionManager使用一个 JTA 实现来管理事务,在一个事务跨越多个资源时使用
  • 3.在事务上增加@Transaction注解

@Transaction放在接口上,那么接口的实现类中所有public都被spring自动加上事务

@Transaction放在类上,那么当前类以及其下无限级子类中所有pubilc方法将被spring自动加上事务

@Transaction放在public方法上,那么该方法将被spring自动加上事务

注意:@Transaction只对public方法有效

@Transaction事务的主要参数

参数描述
value指定的事务管理器的bean名称,如有多个PlatformTransactionManager的实现,需要指定唯一的
transactionManager同value,二者选配一个
propagation事务传播机制
isolation事务的隔离级别,默认是default
timeout事务执行的超时时间
readOnly是否为只读事务,例如查询操作,可以指定为只读,提升查询速度
rollbackfor定义异常,抛出该异常的时候,spring会让事务回滚,默认为RuntimeException和error
rollbackForClassName同rollbackfor指定方式为类名
norollbackfor定义不回滚的异常
norollbackForClassName定义不回滚的异常类名
  • 4.定义事务方法

在@Transaction注解中添加业务操作

public class UserService {
  @Autowired
  private JdbcTemplate jdbcTemplate;
  //先清空表中数据,然后批量插入数据,要么都成功要么都失败
  @Transactional
  public void insertBatch(String... names) {
      jdbcTemplate.update("truncate table t_user");
      for (String name : names) {
          jdbcTemplate.update("INSERT INTO t_user(name) VALUES (?)", name);
      }
   }
}
  • 5.调用事务方法

4.声明式事务注意点

4.1 仅对 public 方法有效

只有 @Transactional 注解应用到 public 方法上才能进行事务管理。这是因为 Spring 在 AOP 事务注解时,在读取注解上的属性方法中,会优先判断方法是否是 public,如果不是 public,就不会读取事务配置信息

4.2 AOP 的自调用问题

在 Spring 的 AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理。也就是说,在同一个类中的一个 @Transactional 方法中,去掉用另一个 @Transactional 方法,会导致第二个方法的事务无效,被 Spring AOP 所忽略。