spring总结-事务管理

简介

Spring 框架中事务管理有两种方式:一种是传统的编程式事务管理,即通过编写代码实现的事务管理;另一种是基于 AOP 技术实现的声明式事务管理(一般都使用声明式)。

编程式事务管理

通过 TransactionTemplate或者TransactionManager手动管理事务

使用TransactionTemplate:

@Autowired
private TransactionTemplate transactionTemplate;
public void testTransaction() {

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {

                try {
                    // ....  业务代码
                } catch (Exception e){
                    //回滚
                    transactionStatus.setRollbackOnly();
                }
            }
        });
}

使用 TransactionManager

@Autowired
private PlatformTransactionManager transactionManager;

public void testTransaction() {
  TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
          try {
               // ....  业务代码
              transactionManager.commit(status);
          } catch (Exception e) {
              transactionManager.rollback(status);
          }
}

 

声明式事务管理

xml文件方式

注解方式

使用 @Transactional注解进行事务管理的示例

查看代码
 @Transactional(propagation=propagation.PROPAGATION_REQUIRED)
public void aMethod {
  //do something
  B b = new B();
  C c = new C();
  b.bMethod();
  c.cMethod();
}

 

事务相关核心接口

PlatformTransactionManager: (平台)事务管理器,Spring 事务策略的核心。
TransactionDefinition: 事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)。
TransactionStatus:事务运行状态

PlatformTransactionManager(事务管理器)

Spring并不直接管理事务,而是提供了多种事务管理器。Spring 事务管理器的接口是:PlatformTransactionManager。
通过这个接口,Spring 为各个平台如JDBC(DataSourceTransactionManager)、Hibernate(HibernateTransactionManager)、JPA(JpaTransactionManager)等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。

PlatformTransactionManager接口中定义了三个方法:

package org.springframework.transaction;

import org.springframework.lang.Nullable;

public interface PlatformTransactionManager {
    //获得事务
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
    //提交事务
    void commit(TransactionStatus var1) throws TransactionException;
    //回滚事务
    void rollback(TransactionStatus var1) throws TransactionException;
}

TransactionDefinition(事务属性)

事务管理器接口 PlatformTransactionManager 通过 getTransaction(TransactionDefinition definition) 方法来得到一个事务,这个方法里面的参数是 TransactionDefinition 类 ,这个类就定义了一些基本的事务属性。
 
什么是事务属性?
事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。
 
事务属性包含了5个方面:
  • 隔离级别
  • 传播行为
  • 回滚规则
  • 是否只读
  • 事务超时
TransactionDefinition 接口中定义了 5 个方法以及一些表示事务属性的常量比如隔离级别、传播行为等等。
package org.springframework.transaction;

import org.springframework.lang.Nullable;

public interface TransactionDefinition {
    ///事务传播行为 对应枚举类Propagation
    //如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
    int PROPAGATION_REQUIRED = 0;
    //如果当前存在事务,则加入该事务,就以非事务方式执行。
    int PROPAGATION_SUPPORTS = 1;
    //如果当前存在事务,则加入该事务,如果当前没有事务,则抛出异常(mandatory:强制性)。
    int PROPAGATION_MANDATORY = 2;
    //创建一个新的事务,如果当前存在事务,则把当前事务挂起。内层事务执行完就立即提交。开启的事务相互独立,互不干扰。
    int PROPAGATION_REQUIRES_NEW = 3;
    //以非事务方式执行操作,如果当前存在事务,就把当前事务挂起(相互独立,互不干扰)。
    int PROPAGATION_NOT_SUPPORTED = 4;
    //以非事务方式执行,如果当前存在事务,则抛出异常。
    int PROPAGATION_NEVER = 5;
    //如果当前存在事务,就在嵌套事务内执行;如果当前没有事务,则创建一个新的事务
    //子事务不会独立提交,而是取决于父事务,当父事务提交,那么子事务才会随之提交;如果父事务回滚,那么子事务也回滚。
    //子事务又有自己的特性,那就是可以独立进行回滚,不会引发父事务整体的回滚(当然需要try catch子事务,避免异常传递至父层事务,如果没有,则也会引发父事务整体回滚)。这个特性比较有意思,虽然不能独立提交,但是可以独立回滚。
    int PROPAGATION_NESTED = 6;
    
    ///事务隔离级别,对应枚举类:Isolation
    int ISOLATION_DEFAULT = -1;//使用数据库默认的隔离级别,MySQL 默认采用的 REPEATABLE_READ(可重复读) 隔离级别
    //(读未提交)最低的隔离级别,使用这个隔离级别很少,因为它允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
    int ISOLATION_READ_UNCOMMITTED = 1;
    //(读提交)允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
    int ISOLATION_READ_COMMITTED = 2;
    //(可重复读)对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
    int ISOLATION_REPEATABLE_READ = 4;
    // (串行化)最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
    int ISOLATION_SERIALIZABLE = 8;
    int TIMEOUT_DEFAULT = -1;
    // 返回事务的传播行为,默认值为 REQUIRED。
    int getPropagationBehavior();
    //返回事务的隔离级别,默认值是 DEFAULT
    int getIsolationLevel();
    // 返回事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
    int getTimeout();//单位:秒
    // 返回是否为只读事务,默认值为 false;只读事务不涉及数据的修改,数据库会提供一些优化手段
    boolean isReadOnly();

    @Nullable
    String getName();
}

 说明:

传播行为说明:

事务传播行为是为了解决业务层方法之间互相调用的事务问题。

当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事物,并在自己的事务中运行。

 

读取要不要加事务:

1)如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持sql执行期间的读一致性。
2)如果你一次执行多条查询语句,例如统计查询,报表查询,这这种场景下,多查询sql必须保证整个的读一致性,否则,在前条sql查询之后,后条sql查询之前,数据被其他用户改变,则该此整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持。

 

TransactionStatus(事务状态)

TransactionStatus接口用来记录事务的状态 该接口定义了一组方法,用来获取或判断事务的相应状态信息。
PlatformTransactionManager.getTransaction(…)方法返回一个 TransactionStatus 对象。
 
TransactionStatus接口内容:
public interface TransactionStatus{
    boolean isNewTransaction(); // 是否是新的事务
    boolean hasSavepoint(); // 是否有恢复点
    void setRollbackOnly();  // 设置为只回滚
    boolean isRollbackOnly(); // 是否为只回滚
    boolean isCompleted; // 是否已完成
}

 

事务回滚规则

默认情况下事务只有遇到运行期异常(RuntimeException 的子类)时才会回滚,Error 也会导致事务回滚,但是,在遇到检查型(Checked)异常时不会回滚。

检查型异常和非检查型异常

如果你想要回滚你定义的特定的异常类型的话,可以这样:

@Transactional(rollbackFor= MyException.class)

@Transactional 注解

说明

1、作用范围
1)方法:推荐将注解使用于方法上,不过需要注意的是:该注解只能应用到 public 方法上,否则不生效。
2)类:如果这个注解使用在类上的话,表明该注解对该类中所有的 public 方法都生效。
3)接口 :不推荐在接口上使用。

 

@Transactional 的常用配置参数:

 原理

@Transactional 的工作机制是基于 AOP 实现的,AOP 又是使用动态代理实现的。如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理,如果目标对象没有实现了接口,会使用 CGLIB 动态代理。

如果一个类或者一个类中的 public 方法上被标注@Transactional 注解的话,Spring 容器就会在启动的时候为其创建一个代理类,在调用被@Transactional 注解的 public 方法的时候,实际调用的是,TransactionInterceptor 类中的 invoke()方法。这个方法的作用就是在目标方法之前开启事务,方法执行过程中如果遇到异常的时候回滚事务,方法调用完成之后提交事务。

事务失效情况

如果同一个类中的其他没有@Transactional注解的方法内部调用有@Transactional 注解的方法,有@Transactional 注解的方法的事务会失效。
这是由于Spring AOP代理的原因造成的,因为只有当 @Transactional 注解的方法在类以外被调用的时候,Spring 事务管理才生效。
解决办法就是避免同一类中自调用或者使用 AspectJ (一个面向切面的框架)取代 Spring AOP 代理。
 

@Transactional 的使用注意事项总结


1)@Transactional 注解只有作用到 public 方法上事务才生效,不推荐在接口上使用;
2)避免同一个类中调用 @Transactional 注解的方法,这样会导致事务失效;
3)正确的设置 @Transactional 的 rollbackFor 和 propagation 属性,否则事务可能会回滚失败;
4)被 @Transactional 注解的方法所在的类必须被 Spring 管理,否则不生效;
5)底层使用的数据库必须支持事务机制,否则不生效。
 

事务日志说明

//创建sqlsesion

Creating a new SqlSession

//代表注册指定sqlSession的事务

Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@83bed72]

-------------执行sql

//正在释放sqlSession事务,事务并没有提交

Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4b7b9a1a]

 

//指定的sqlSession的事务提交了

Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@83bed72]

Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@83bed72]

Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@83bed72]

事务报错

UnexpectedRollbackException

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

当A类a方法上面标记了@Transactional,a方法调用B类的b方法,b方法上也标记了@Transactional,如果b方法抛异常,导致回滚,a方法调用b方法时进行了try catch;这样就导致b方法去回滚,而a方法cache了异常去正常提交事务,提交的时候发现不能提交只能回滚,就抛出了这个错误提示。

处理方法1:

去掉b方法上的@Transactional,和a方法中调用b方法的try catch(或catch中再抛异常)让a方法可以抛出异常让a的事务可以出发整体事务的回滚。

处理方法2:

在b方法的@Transactional添加属性propagation= Propagation.NESTED,外层事务的提交和回滚能够控制嵌套的内层事务回滚;而内层事务报错时,只回滚内层事务,外层事务可以继续提交。与 Hibernate JPA 一起使用将导致 Spring 异常,这是因为 Hibernate JPA 不支持嵌套事务,可以考虑用 Propagation.REQUIRES_NEW 代替一下.

posted @ 2022-10-14 10:55  星光闪闪  阅读(213)  评论(0)    收藏  举报