其他分享
首页 > 其他分享> > Sring事务管理

Sring事务管理

作者:互联网

最近工作中遇到的需求,需要用到嵌套事务,然而在涉及到不同事务方法之间互相调用时的传播行为时却不是很确定,之前好像只是停留在定义的层面,对于具体各种情况事务的回滚情况并不是很确定。所以决定对各种情形进行实际的代码demo,验证一下结果。
以及在开发中遇到了Transaction rolled back because it has been marked as rollback-only异常分析下出现的问题。

1.事务是什么

事务是一系列的动作,它们综合在一起才是一个完整的工作单元。 主要具有ACID四个特性:


2.数据库事务隔离级别

数据库的事务隔离级别分为四个级别:

大多数数据库默认的事务隔离级别是Read committed,比如Sql Server , Oracle。
Mysql的默认隔离级别是Repeatable read。
Mysql InnoDB引擎支持事务 MyISAM不支持事务

数据库空中的开启事务、提交与回滚

START TRANSACTION;
INSERT INTO sys_user value (1,20,'男','ShinyRou');
commit ;--提交

START TRANSACTION;
INSERT INTO sys_user value (2,20,'男','ShinyRou1');
ROLLBACK ;--回滚

3.Spring事务管理

事务是数据库提供的特性,SPring中的事务只是进行了封装来方便操作事务的提交与回滚。
同一个事务,使用的是同一个数据库连接,同一个事务ID,在spring中的多个DB操作执行后不会立即执行到数据库,直到全部完成事务提交后才会执行到DB。

3.1Spring事务接口
3.2spring中事务的使用

3.2.1.编程式的事务 通过DataSourceTransactionMannager、TranssactionDefinition等API来编程式的控制事务的提交与回滚
3.2.2.声明式事务 使用@Transactional主键来实现 原理是通过AOP 基于代理模式实现
- 当使用了@Transactional注解的业务方法中 抛出了运行时异常 RuntimeException时,事务就会进行回滚
- @Transactional注解中rollbackFor 属性可以执行抛出什么异常时进行回滚
- 当@Transactional 注解使用在类上时,类中所有的方法都会开启事务
@Transactional注解的定义

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";

    Propagation propagation() default Propagation.REQUIRED;  //传播特性

    Isolation isolation() default Isolation.DEFAULT; //隔离级别

    int timeout() default -1; //超时时间

    boolean readOnly() default false;

    Class<? extends Throwable>[] rollbackFor() default {}; //回滚的异常

    String[] rollbackForClassName() default {};

    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};
}


4.事务的传播行为

事务的传播行为主要是用于在多个事务方法互相调用时事务应该如何传播
Propagation枚举的定义

public enum Propagation {
    REQUIRED(0),   //支持当前事务,如果不存在事务,就新建一个事务
    SUPPORTS(1),   //支持当前事务,如果不存在事务,就不使用事务
    MANDATORY(2),  //支持当前事务,如果不存在事务,就抛出异常
      //0,1,2 可以归为一类,如果当前有事务 就使用当前事务,只是在当前不存在事务时,处理方法不一致

    REQUIRES_NEW(3),  //如果当前事务存在,挂起当前事务,创建一个新事务 (不在同一个事务中)
    NOT_SUPPORTED(4), //以非事务方式运行,如果当前事务存在,挂起当前事务
    NEVER(5), //以非事务方式运行如果当前事务存在,就抛出异常
      //3,4,5  可以归为一类,都不支持当前事务(不在同一个事务中)

    NESTED(6); //如果不存在事务,则新建事务与REQUIRED相同,如果存在事务就创建子事务,执行嵌套事务
      //嵌套事务:外围事务回滚,所有子事务也回滚,子事务回滚不会影响其他子事务与外围食物


    //最为常用的是 REQUIRED  REQUIRES_NEW   NESTED
    private final int value;

    private Propagation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }
}

传播行为结果验证可以参考
传播行为代码验证 注意:原文在验证REQUIRES_NEW时地方,注解里面的传播方式写错REQUIRED


5.Transaction rolled back because it has been marked as rollback-only异常的原因分析

    //用户表事务操作
    @Transactional
    @Override
    public int addUserREQUIRED(SysUser user) {
        int userResult = sysUserDao.insert(user);
        return userResult;
    }

    //日志表事务 正确执行
    @Transactional
    @Override
    public int addLogREQUIRED(Log log) {
        return logDao.insert(log);
    }

    //日志表事务 抛出异常
    @Transactional
    @Override
    public int addLogREQUIREDWithException(Log log) {
       int result =  logDao.insert(log);
       throw new RuntimeException("DB ERROR");
    }

    @Transactional
    @Override
    public void fooFunction(SysUser user) {
        sysUserService.addUserREQUIRED(user);
        Log log = new Log();
        log.setUserId(user.getNo());
        log.setDescription("add user"+user.toString());
        //logService.addLogREQUIRED(log);//正确执行
        try{
            logService.addLogREQUIREDWithException(log);
        }catch(Exception e){
            e.getStackTrace();
        }
    }

假设fooFunction 中对addLogREQUIREDWithException 抛出的异常进行捕获就会出现上述异常
原因是: 默认传播行为 REQUIRED 此时整体使用的是同一个事务
addLogREQUIREDWithException中抛出异常 对log表的操作需要回滚,整个事务也标记为需要回滚,但是在fooFunction方法中异常被捕获不满足回滚条件,在事务整体commit时就会抛出上述异常

解决方法:

标签:Sring,事务管理,事务,log,default,Transactional,回滚,value
来源: https://www.cnblogs.com/shinyrou/p/13387318.html