事务的基本概念(理论)
一、事务是什么
百度百科:Java的事务处理,如果对数据库进行多次操作,每一次的执行或步骤都是一个事务。如果数据库操作在某一步没有执行或出现异常而导致事务失败,这样有的事务被执行有的就没有被执行,从而就有了事务的回滚,取消先前的操作。
二、事务的ACIP特点
(1)Atomic:原子性,操作要么一起成功,要么一起失败。
(2)Consistency:一致性,数据必须是准确的,执行前数据必须是准确的,执行后数据也必须是准确的。
(3)Isolation:隔离性,多个事务在跑的时候互不干扰
(4)Durability:持久性,事务成功了,就必须对数据的修改是永久有效的。
三、事务的隔离级别
(1)读未提交,read uncommitted:某个事务还未提交时,修改的数据让另一个事务读取捕获,也叫脏读。
(2)读已提交,read committed:事务A在跑的时候,查询一个数据为1,过段时间,事务B把数据修改成了2,并且提交成功了;此时事务A再次查询该数据变为了2,这是读了别人事务提交的数据,所以是读已提交。也叫不可重复读,就是所谓一个事务内对一个数据查询2次,可能会读到不同的结果。
(3)可重复读read repeatable:事务A在执行过程中,不管读几次某个数据C,读出来的数据C的值都是不变的,哪怕这个过程中事务B修改了该数据C并且提交了,事务A读到的还是自己事务开始时的这个数据的值。
(4)串行幻:事务A查询表里只有1行数据,事务B想插入数据B,但是事务A没执行完毕时,事务B被禁止执行,只有事务A执行提交后,事务B才能执行。解决幻读
(5)幻读(插入数据)不是隔离级别:在事务A读到一条数据C的时候,事务B插入并提交一条数据D到数据库。事务A查询第二次,查询到了2条数据。
四、事务的传播行为
事务的传播行为,默认值为 Propagation.REQUIRED。可以手动指定其他的事务传播行为。
(1)Propagation.REQUIRED:如果方法A当前存在事务,则方法B加入到该事务中,只要2个方法中,存在一个方法失败了,那么2个方法的事务全部都会回滚。如果方法A不存在事务,方法B会新起一个事务,方法B抛异常,方法B会回滚,方法A不会回滚。
(2)Propagation.SUPPORTS:如果方法A当前存在事务,则方法B加入该事务;如果方法A当前不存在事务,则方法B以非事务的方式继续运行。
(3)Propagation.MANDATORY:如果方法A当前存在事务,则方法B加入该事务;如果方法A当前不存在事务,则抛出异常。
(4)Propagation.REQUIRES_NEW:方法B强制创建一个新的事务,方法A的事务会卡住,等方法B事务执行完方法A才继续。如果方法A报错,方法B不受影响;如果方法B报错,往外抛异常,方法A捕获异常后可以选择回滚或者提交,如果方法A没有捕获异常会导致方法A回滚。
(5)Propagation.NOT_SUPPORTED:方法A暂停当前事务,方法B以非事务的方式运行结束,方法A继续。方法B(非事务)报错不会导致方法A回滚。
(6)Propagation.NEVER:方法B以非事务的方式运行,如果方法A存在事务进行调用,则方法B抛出异常。
(7)Propagation.NESTED:开启嵌套事务,方法B开启一个子事务,如果回滚,那么方法B就回滚到开启子事务的保存点(save point)。
五、项目里基于Spring的事务是如何设置
1、spring支持2种事务,编程事务和声明式事务。
(1)编程事务是使用TransationTemplate来管理事务
(2)声明式事务是在xml里配置个AOP来声明切面加个事务或者@Transaction注解。
2、现在一般是使用@Transactional来注解,根据阿里的规范,一般建议加在方法级。另外一般配合rollbackFor使用,指定哪些异常类才回滚事务。
(1)isolation属性可以设置事务的隔离级别,一般不建议调整
(2)propagation属性,事务的传播行为。
六、一个小例子
问题:前面50次成功,第51次失败。要求a,全部回滚;b前面50次提交,第51次回滚?
1、a要求:方法A跟方法B全部使用默认的@Transational
2、b要求:方法C使用默认的@Transational,方法B使用@Transatioanal(propagation=PROPAGATION.REQUIRES_NEW)
public void ServiceA{
@autowrited
private ServiceB b;
//要求A
@Transational
public void functionA(){
for(i=0;i<50;i++){
b.functionB();
}
}
//要求B
@Transatioanal
public void functionC(){
for(i=0;i<50;i++){
try{
b.functionB();
}catch(Exception e){
//打印错误日志
}
}
}
}
public void ServiceB{
//A要求,方法A调用加:@Transational
//B要求,方法C调用加:@Transatioanal(propagation=PROPAGATION.REQUIRES_NEW)
public void funtionB(){
//方法
}
}