1.什么是事务传播行为
当事务方法被另外一个事务方法调用时,必须指定事务如何传播的。例如:方法可能继承现有的事务,或者开启一个新的事务,并在自己的事务中运行。Sping定义了7种事务传播类型。
传播属性 | 描述 |
---|---|
REQUIRED | 如果有事务在运行,当前的方法就在这个事务中运行,否则,就启动一个新的事务,并在自己的事务中运行(默认值) |
REQUIRES_NEW | 当前方法必须启动新的事务,并在它自己的事务中运行,如果有事务正在运行,应该将他挂起 |
SUPPORTS | 如果有事务在运行,当前的方法就在这个事务中运行,否则它可以不运行在事务中 |
NOT_SUPPORE | 当前的方法不应该运行在事务中,如果有运行的事务,将它挂起 |
MANDATORY | 当前的发给发必须运行在事务内部,如果没有正在运行的事务,就抛出异常 |
NEVER | 当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常 |
NESTED | 如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则,就启动一个新的事务,并在它自己的事务内运行 |
例如:现在我去买书这个操作 数据库中有3张表 一个书的库存表,一个书的单价表 ,一个我的账号余额表。
下面是伪代码:
@Transactional
public void buy(int userId,String book){
//获取图书价格
//更新图书库存
//更新账号余额
}
@Transactional
public void checkout(int userId){
List<String> list = new ArrayList<String>();
list.add("50");
list.add("60");,
for(String book:list){
buy(userId,book)
}
}
我们现在要买两本书,一本50元,一本60元,而我目前的账号余额为100元,因为使用默认的REQUIRED事务,所以buy中用的事务是checkout中的事务,buy方法调用了两次,第一次成功购买,而第二此因为账号余额不足buy方法出现异常,checkout中事务回滚,所以都不会购买成功,而如果buy中使用的是@Transactional(propagation=Propagetion.REQUIRES_NEW),那么每次调用buy方法都会开启一个新的事务,那么至少会购买成功一本书。
2.什么是数据库的隔离级别
了解隔离级别之前,必须要先了解数据库事务并发问题
目前有两个事务t1和t2
(1).脏读:
t1将某条记录age从20改为了30
t2读取了age为30
t1出现异常事务回滚
t2读取到的就是一个无效的值
(2).不可重复度
t1读取age值20
t2将age修改为30
t1再次读取值为30
(3).幻读
t1读取表1中的部分数据
t2向表中插入了新的行
那什么是数据库的隔离级别呢:
数据库必须有隔离并发运行各个事务的能力,使他们不会相互影响,避免各种并发问题,一个事务与其他事务隔离程度称为隔离级别,隔离级别越高,数据一致性越好,但是并发性越弱
(1).读未提交
运行t1读取t2未提交的修改
(2).读已提交
要求t1只能读取t2已经提交的修改
(3).可重复读
确保t1可以多次从一个字段中读取相同的值,即t1执行期间禁止其它事务对这个字段进行更新
(4)串行化
确保t1可以多次从一个表中读取相同的行,在t1执行期间,禁止其他事务对这个表进行添加,删除,更新操作,可以避免任何并发问题,但是性能十分低下
例子:比如模拟查询图书价格
编号为 1的图书 价格为 50
下面是伪代码
@Transactional(propagation=Propagetion.REQUIRED,isolation=Isolation.Default)
public void query(){
double bookPrice =bookDao.query("1");
double bookPrice2 =bookDao.query("1");
}
我们在bookPrice那打一个断点,启动debug模式,在debug模式中bookPrice价格为50 没有错 ,现在我们手动进入数据中将价格修改为500并提交,因为我是用的是mysql数据库,mysql默认的隔离级别为可重复读,所以第二次查询的结果还是50。而如果将isolation属性改为Isolation.READ_COMMITTED(读已提交),依然重复上面的步骤,第一次读取的是50,第二次读取的就是修改过后并且提交了的值500. 读已提交这种隔离级别也是开发中常用的隔离级别。
最后总结一下 并不是隔离级别越高越好,READ_COMMITTED是开发常用的隔离级别。