1.先介绍下事务的传播属性:
事务细节:
- isolation-Isolation:事务的隔离级别。
- propagation-Propagation:事务的传播行为。
- noRollbackFor-Class[]:哪些异常事务可以不回滚。
- noRollbackForClassName-String[]:不常用,写全类名。
- rollbackFor-Class[]:指定哪些异常事务回滚。
- rollbackForClassName-String[]:不常用,写全类名。
- readOnly-boolean:设置事务的属性为只读事务。
- timeout-int:超时,事务超出指定执行时常后,自动终止并回滚,秒为单位。
1.1、Isolation-调整隔离级别:
数据库中事务并发问题:
- 脏读:
tx1将某条记录的值从20修改为了30
tx2读取到了tx1修改后的值为30
tx1回滚,值恢复到了20
tx2读取到的就是一个无效的值
- 不可重复读:
tx1读取到某条记录的值为20
tx2将该条记录的值修改为30
tx1再次读取该记录时,出现了和第一次不一致的值
- 幻读:
tx1读取了表中的一部分数据
tx2向表中插入了新的行
tx1读取该表时,多出了一些行
——1、read uncommitted:读未提交
产生的问题:脏读、不可重复读、幻读。
——2、read commited:读已提交(oracle默认使用本级别)
产生的问题:不可重复读、幻读。
——3、repeatable read:可重复读(mysql默认使用本级别)
产生的问题:幻读。
——4、serializable:串行化
可以解决所有的问题。类似于锁表的一个操作,很少用,类似于单线程处理。
* 注意:隔离级别,从小到大,安全性越来越高但是效率越来越低。
![Spring-Transactions-事务传播属性和传播行为介绍_第1张图片](http://img.e-com-net.com/image/info8/ab8621b6d1b74cbc85df01399d56ff96.jpg)
@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-指定默认运行时异常-某些异常可以不回滚:
- 运行时异常(非检查异常):可以不用处理,事务会进行回滚。
- 编译时异常(检查异常):要么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");
}
//这属于编译时异常,数据库的修改会成功。默认不回滚。
- 事务的回滚:默认发生运行时异常都回滚,发生编译时异常不会回滚。
- noRollbackFor-Class[]:哪些异常事务可以不回滚。
- noRollbackForClassName-String[]:不常用,写全类名。
- rollbackFor-Class[]:指定哪些异常事务回滚。
- 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种类传播行为:
![Spring-Transactions-事务传播属性和传播行为介绍_第2张图片](http://img.e-com-net.com/image/info8/9adbf64e676b40adaa3b9e3d4be53bcb.jpg)
@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。
- 注意代理对象是谁的代理对象,如果没有加入到容器中,代理对象又是谁?
本类事务的代理对象调用本类的方法就只是一个事务。