Spring中的事务和事务的传播机制

事务是一组操作的集合,不可以被分割。事务会把所有的操作作为一个整体,这组操作要么全部成功,要么全部失败。

事务有三种操作:

  1. 开启事务;
  2. 提交事务;
  3. 回滚事务。

如果代码的执行逻辑是这样:

开启事务

业务A

回滚事务

此时A当中的所有操作都不会生效

开启事务

业务A

提交事务

开启事务后只有这种情况下A中的逻辑才生效

Spring中事务的实现有两种

编程式(手动操作事务)

Spring Boot对于事务操作内置了两个类,我们在使用时可以选择直接注入:

  • DataSourceTransactionManager:事务管理器,里面包含了事务的操作和获取;
  • TransactionDefinition:事务的属性。在获取事务时需要充当参数。

提交事务

@Slf4j
@RestController
@RequestMapping("/trans")
public class Test {
    //获取事务管理器
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;

    //获取事务属性
    @Autowired
    private TransactionDefinition definition;

    @Autowired
    private UserMapper userMapper;

    @RequestMapping("/fun1")
    public void fun1() {
        //获取并开启事务
        TransactionStatus transaction = dataSourceTransactionManager.getTransaction(definition);
        //业务操作
        //向数据库中插入一条数据
        userMapper.userInsert("zhangsan","man");
        //打印日志
        log.info("数据插入完成");
        //提交事务
        dataSourceTransactionManager.commit(transaction);
    }
}
@Mapper
public interface UserMapper {
    @Insert("insert into userinfo(username,gender) values (#{userName},#{gender});")
    void userInsert(String userName, String gender);
}

这是数据库的初始状态

代码执行后数据成功插入

回滚事务

@RequestMapping("/fun1")
public void fun1() {
    //获取并开启事务
    TransactionStatus transaction = dataSourceTransactionManager.getTransaction(definition);
    //业务操作
    userMapper.userInsert("zhangsan","man");
    log.info("数据插入完成");
    //提交事务
//        dataSourceTransactionManager.commit(transaction);
    //回滚事务
    dataSourceTransactionManager.rollback(transaction);
}

当代码执行成功后,数据库中的数据并没有变多。

注解式(利用注解自动实现事务)

使用注解实现事务是非常简单的只需要给需要添加事务的方法加上@Transactional注解。添加该注解后程序会自动的在方法开始前开启事务,在方法结束后提交事务;如果在方法执行中发生了没有处理的异常会自动进行回滚事务

@Transactional既可以修饰方法也可以修饰类:

  1. 修饰方法时该方法必须是被public修饰的方法,否则既不会生效也不会报错;
  2. 当修饰类时,会对该类中的所有被public修饰的方法生效。

当方法正常执行完毕后会自动提交事务 :

@Slf4j
@RestController
@RequestMapping("/trans")
public class Test {
    @Autowired
    private UserMapper userMapper;

    @Transactional
    @RequestMapping("/fun2")
    public void fun2() {
        userMapper.userInsert("lisi","man");
        log.info("数据插入完成");
    }
}

当方法执行过程中发生异常时自动回滚事务 (该注解默认只回滚运行时异常<RuntimeException
>和错误<error>):

发生运行时异常,事务回滚:

@Slf4j
@RestController
@RequestMapping("/trans")
public class Test {
    @Autowired
    private UserMapper userMapper;

    @Transactional
    @RequestMapping("/fun2")
    public void fun2() {
        userMapper.userInsert("lisi111","man");
        log.info("数据插入完成");
        //发生运行时异常,事务回滚
        throw new RuntimeException();
    }
}

编译时异常不会回滚:

@Slf4j
@RestController
@RequestMapping("/trans")
public class Test {
    @Autowired
    private UserMapper userMapper;

    @Transactional
    @RequestMapping("/fun2")
    public void fun2() throws IOException {
        userMapper.userInsert("lisi111","man");
        log.info("数据插入完成");
        //发生编译时异常,事务不会回滚
        throw new IOException();
    }
}

此时尽管程序已经报错了,可数据还是正常插入了。如何解决这个问题呢?

rollbackFor

可以通过配置 @Transactional 注解当中的 rollbackFor 属性,通过 rollbackFor 这个属性来指定出现何种异常类型时事务进行回滚。

这个属性的类型是数组需要注意

Class<? extends Throwable>[] rollbackFor() default {};
@Slf4j
@RestController
@RequestMapping("/trans")
public class Test {
    @Autowired
    private UserMapper userMapper;

    //此时会回滚所有的Exception类型的异常
    @Transactional(rollbackFor = {Exception.class})
    @RequestMapping("/fun2")
    public void fun2() throws IOException {
        userMapper.userInsert("lisi222","man");
        log.info("数据插入完成");
        //此时发生编译时异常,事务回滚
        throw new IOException();
    }
}

此时error类型的错误依然会进行回滚。

@Slf4j
@RestController
@RequestMapping("/trans")
public class Test {

    @Autowired
    private UserMapper userMapper;

    @Transactional(rollbackFor = {Exception.class})
    @RequestMapping("/fun2")
    public void fun2() {
        userMapper.userInsert("lisi222","man");
        log.info("数据插入完成");
        //会发生栈溢出错误,仍然会回滚
        while(true) {
            fun2();
        }
    }
}

手动回滚事务

使用 TransactionAspectSupport.currentTransactionStatus() 得到当前的事务,并使用setRollbackOnly使事务进行回滚

@Slf4j
@RestController
@RequestMapping("/trans")
public class Test {

    @Autowired
    private UserMapper userMapper;

    @Transactional(rollbackFor = {Exception.class})
    @RequestMapping("/fun2")
    public void fun2() {
        userMapper.userInsert("66666","man");
        log.info("数据插入完成");
        //手动设置回滚事务
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

事务隔离级别

SQL中的事务隔离级别:

  1. 读未提交(READ UNCOMMITTED):读未提交,也叫未提交读。该隔离级别的事务可以看到其他事务中未提交的数据(未提交的数据可能会发生回滚,但是该隔离级别却可以读到,这个问题称之为脏读)
  2. 读已提交(READ COMMITTED):也叫提交读,该隔离级别的事务能读取到已经提交事务的数据。(该隔离级别不会有脏读的问题。但由于在事务的执行中可以读取到其他事务提交的结果,所以在不同时间的相同SQL查询可能会得到不同的结果,这种现象叫做不可重复读)
  3. 可重复读(REPEATABLE READ):事务不会读到其他事务对已有数据的修改,即使其他事务已提交。可重复读,是MySQL的默认事务隔离级别。(虽然可以确保同一事务多次查询的结果一致,但是其他事务新插入的数据,是可以感知到A事务正在执行时,另⼀个事务成功的插入了某条数据,而此时A事务如果再查寻数据库就会导致两次查找到的“结果集”不同<数据变多了>,这个现象叫幻读。)
  4. 串行化(SERIALIZABLE):序列化,事务最高隔离级别。它会强制事务排序,使之不会发生冲突,从而解决了脏读,不可重复读和幻读问题,但因为执行效率低,所以真正使用的场景并不多。

Spring中的事务隔离级别:

  1. Isolation.DEFAULT :以连接的数据库的事务隔离级别为主;
  2. Isolation.READ_UNCOMMITTED :读未提交;
  3. Isolation.READ_COMMITTED :读已提交;
  4. Isolation.REPEATABLE_READ :可重复读;
  5. Isolation.SERIALIZABLE :串行化;

事务隔离级别可以通过 @Transactional 中的 isolation 属性进行设置

@Transactional(isolation = Isolation.DEFAULT)
@RequestMapping("/fun2")
public void fun2() {}

事务的传播机制

如果A方法中调用B方法那么B方法是使用A方法的事务还是自己的事务,事务的传播机制就是为了解决该问题。

@Transactional 注解支持事务传播机制的设置,通过 propagation 属性来设置。

Spring 事务传播机制有 7 种(在A方法中调用B(七种传播机制都设置在该方法上)方法):

  1. Propagation.REQUIRED:默认的事务传播级别。如果A存在事务,则B加入该事务。如果A没有事务,则B创建一个新的事务;
  2. Propagation.SUPPORTS:如果A存在事务,则B加入该事务。如果A没有事务,则B以非事务的方式继续运行;
  3. Propagation.MANDATORY:强制性。如果A存在事务,则B加入该事务。如果A没有事务,B抛出异常;
  4. Propagation.REQUIRES_NEW:创建一个新的事务。不管A是否开启事务,B都会重新创建一个事务,且创建的事务相互独立,互不干扰;
  5. Propagation.NOT_SUPPORTED:以非事务方式运行。无论A是否存在事务,B都以非事务运行;
  6. Propagation.NEVER:以非事务方式运行。如果A存在事务,则抛出异常;
  7. Propagation.NESTED :如果A存在事务,则B创建一个事务作为当前事务的嵌套事务来运行。如果A没有事务,B创建一个事务。
@Transactional(propagation = Propagation.MANDATORY)
@RequestMapping("/fun2")
public void fun2() {}

  • 61
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 46
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 46
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值