Spring-Transactions-事务传播属性和传播行为介绍

1.先介绍下事务的传播属性:

事务细节:

  • isolation-Isolation:事务的隔离级别。
  • propagation-Propagation:事务的传播行为。
  • noRollbackFor-Class[]:哪些异常事务可以不回滚。
  • noRollbackForClassName-String[]:不常用,写全类名。
  • rollbackFor-Class[]:指定哪些异常事务回滚。
  • rollbackForClassName-String[]:不常用,写全类名。
  • readOnly-boolean:设置事务的属性为只读事务。
  • timeout-int:超时,事务超出指定执行时常后,自动终止并回滚,秒为单位。

1.1、Isolation-调整隔离级别:

数据库中事务并发问题:

  • 当有两条事务并发执行:tx1和tx2:
  1. 脏读:
tx1将某条记录的值从20修改为了30
tx2读取到了tx1修改后的值为30
tx1回滚,值恢复到了20
tx2读取到的就是一个无效的值
  1. 不可重复读:
tx1读取到某条记录的值为20
tx2将该条记录的值修改为30
tx1再次读取该记录时,出现了和第一次不一致的值
  1. 幻读:
tx1读取了表中的一部分数据
tx2向表中插入了新的行
tx1读取该表时,多出了一些行
  • 隔离级别:
——1、read uncommitted:读未提交
产生的问题:脏读、不可重复读、幻读。
——2、read commited:读已提交(oracle默认使用本级别)
产生的问题:不可重复读、幻读。
——3、repeatable read:可重复读(mysql默认使用本级别)
产生的问题:幻读。
——4、serializable:串行化
可以解决所有的问题。类似于锁表的一个操作,很少用,类似于单线程处理。
* 注意:隔离级别,从小到大,安全性越来越高但是效率越来越低。

在这里插入图片描述

@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void updateStock(String isbn){
    String sql = "UPDATE book_stock SET stock = stock-1 WHERE isbn = ?";
    jdbcTemplate.update(sql, isbn);
}

注意以下两点:

  • 并发修改同一个数据,会自动排队。
  • 有事务的业务逻辑,容器中保存的是这个业务逻辑的代理对象。

1.2、rollbackFor-原本不回滚的异常,需要让其回滚:

* 默认是编译时的异常是不回滚的。

@Transactional(rollbackFor = {FileNotFoundException.class})
    public void checkout(String username, String isbn){
        bookDao.updateStock(isbn);
        Double price = bookDao.getPrice(isbn);
        bookDao.updateBalance(username, price);
        new FileInputStream("D://不存在文件.java");
    }
//会进行回滚,不修改数据库中数据。

1.3、noRollbackFor-指定默认运行时异常-某些异常可以不回滚:

  • 异常不同,处理效果不同:
  1. 运行时异常(非检查异常):可以不用处理,事务会进行回滚。
  2. 编译时异常(检查异常):要么try-catch,要么在方法上声明throws。
  • 例:
  @Transactional(timeout = 3)
  public void checkout(String username, String isbn){
       bookDao.updateStock(isbn);
       Double price = bookDao.getPrice(isbn);
       bookDao.updateBalance(username, price);
       int i = 10 / 0;
   }
//该段代码在测试时,不会修改数据库成功。
  @Transactional(timeout = 3)
  public void checkout(String username, String isbn){
       bookDao.updateStock(isbn);
       Double price = bookDao.getPrice(isbn);
       bookDao.updateBalance(username, price);
       new FileInputStream("D://不存在文件.java");
   }
//这属于编译时异常,数据库的修改会成功。默认不回滚。
  • 事务的回滚:默认发生运行时异常都回滚,发生编译时异常不会回滚。
  1. noRollbackFor-Class[]:哪些异常事务可以不回滚。
  2. noRollbackForClassName-String[]:不常用,写全类名。
  3. rollbackFor-Class[]:指定哪些异常事务回滚。
  4. rollbackForClassName-String[]:不常用,写全类名。
  • 例:
	@Transactional(noRollbackFor = {ArithmeticException.class})
	public void checkout(String username, String isbn){
		   bookDao.updateStock(isbn);
		   Double price = bookDao.getPrice(isbn);
		   bookDao.updateBalance(username, price);
		   int i = 10 / 0;
	}
//该段代码在测试时,不会发生回滚

1.4、readOnly-只读事务:

readOnly-boolean:设置事务的属性为只读事务。可以进行事务优化
readOnly=true:加快查询速度。
默认是false,不开启的,只有当sql语句中只有只读标准时,可以启用。

@Transactional(readOnly = true)

1.5、timeout-超时设置:

    @Transactional(timeout = 3)
    public void checkout(String username, String isbn){
//        第一步:减库存
        bookDao.updateStock(isbn);
//        第二步:减余额,先查询图书价格
        Double price = bookDao.getPrice(isbn);
        bookDao.updateBalance(username, price);
    }

2.事务的传播行为:

  • propagation-Propagation:事务的传播行为;
  • 传播行为(事务的传播+事务的行为):如果有多个事务进行嵌套运行,那么子事务是否要和大事务共用一个事务。
  • 那么事务的传播行为就是方法开启的事务嵌套使用时,可以通过规定事务的行为,改变运行方法:

* Spring定义了7种类传播行为:

在这里插入图片描述

@Transactional(propagation=Propagation.REQUIRED)
public void mulTx(){
    //@Transactional(propagation=Propagation.REQUIRED)
    bookService.checkout("Tom", "ISBN-001");
    
    //@Transactional(propagation=Propagation.REQUIRED)
    bookService.updatePrice("ISBN-002", 998);
}

//大事务里面还有两个小事务。

注意:为了理解子事务和大事务的关系,可以将事务之间的行为看成一种继承关系。就像他们之间是不是同开一辆车,司机又是谁,那么谁就决定了车速(timeout),谁可以上车(rollbackFor/noRollbackFor),谁可以决定开的车子级别(isolation),谁又可以拿这个车子干嘛(readOnly)。

* 接下来我们看一个例子,帮助我们更好的理解:

//REQUIRED
A(){
	//REQUIRED
	B(){
		//REQUIRED_NEW
		C(){}
		//REQUIRED
		D(){}
	
	//REQUIRED_NEW
	E(){
		//REQUIRED
		F(){}
}
  • 分析:A是1车司机车上有B,D,那么所有的事都应该听他的,A若没规定,那么就可以听自己的。C自己单独开一辆车。E是2车司机,车上有F。

* REQUIRED事务属性来源于大事务:

  • 就像子事务设置timeout属性,但是其大事务没有timeout属性或者不一致,那么子事务的属性是跟随大事务的。子事务的属性是继承自大事务的。

* 本类事务方法之间的调用就只是 一个事务 :

  • REQUIRED:将之前事务的connection传递给这个方法使用。而REQUIRED_NEW直接使用新的connection。
  • 注意代理对象是谁的代理对象,如果没有加入到容器中,代理对象又是谁?
    本类事务的代理对象调用本类的方法就只是一个事务。
发布了52 篇原创文章 · 获赞 1 · 访问量 2237

猜你喜欢

转载自blog.csdn.net/Shen_R/article/details/105217711
今日推荐