事务逻辑上的一组操作,组成这组操作的各个逻辑单元,要么一起成功,要么一起失败。
原子性 (atomicity):强调事务的不可分割,组成事务操作的各个最小逻辑单元缺一不可,强调了 操作的完整性
。
一致性 (consistency):事务的执行前后,数据的完整性保持一致,强调 数据的完整性
。
隔离性 (isolation):一个事务执行的过程中,不应该受到其他事务的干扰,强调 事务的独立性
。
持久性(durability):事务一旦结束,数据应持久到数据库中,强调了 事务的可持久性
。
脏读:一个事务读取到了另一个事务的 未提交
的数据。这些数据可能会被提交,也可能不会被提交。提交了还好,如果未提交,就读取到了脏数据。
不可重复读:一个事务读取到了另一个事务 已经提交了的 update 数据
,导致多次查询结果不一致.。侧重点是更新,重复读取数据时,若有其它事务对这些数据进行了更新操作,会导致查询到数据不一致。
幻读:一个事务读取到了另一个事务 已经提交了的 insert 数据
,导致多次查询结果不一致。侧重点是插入,重复读取数据时,若有其它事务进行了插入操作,会导致查询到数据条数不一致。
读取未提交(read uncommited):脏读,不可重复读,幻读都有可能发生。
读取已提交(read commited):避免了脏读,但是不可重复读和幻读都有可能发生。
可重复读(repeatable read):避免了脏读和不可重复读,但是幻读有可能发生。
串行化(serializable):避免了以上问题,但效率较低。
Mysql 默认的事务隔离级别:可重复读 Oracle 默认的事务隔离级别:读取已提交
PROPAGATION_REQUIRED:支持当前事务,如果不存在,就新建一个事务(默认属性)
PROPAGATION_SUPPORTS:支持当前事务,如果不存在,就不使用事务
PROPAGATION_MANDATORY:支持当前事务,如果不存在,抛出异常
PROPAGATION_REQUIRES_NEW:如果有事务存在,挂起当前事务,创建一个新的事务
PROPAGATION_NOT_SUPPORTEDd:以非事务方式运行,如果有事务存在,挂起当前事务
PROPAGATION_NEVER:以非事务方式运行,如果有事务存在,抛出异常
PROPAGATION_REQUIRED_NESTED: 如果当前事务存在,则嵌套事务执行
ServiceA { static void methodA() { ServiceB.methodB(); } } ServiceB { static void methodB() { } }
PROPAGATION_REQUIRED(加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务)
比如说:
ServiceB.methodB()的事务级别定义为PROPAGATION_REQUIRED,那么由于执行ServiceA.methodA的时候,ServiceA.methodA()已经起了事务,这时调用ServiceB.methodB(),ServiceB.methodB()看到自己已经运行在ServiceA.methodA()的事务内部,就不会再起新的事务。
而假如ServiceA.methodA()运行的时候发现自己没有在事务中,他就会为自己分配一个事务。这样,在ServiceA.methodA()或者在ServiceB.methodB()内的任何地方出现异常,事务都会被回滚。即ServiceB.methodB()的事务已经被提交,但是ServiceA.methodA()在接下来fail要回滚,ServiceB.methodB()也要回滚。
PROPAGATION_SUPPORTS(如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行)
和平常不加事务的代码区别很小,只是多了一个在事务中就以事务的形式运行的条件。
PROPAGATION_MANDATORY(必须在一个事务中运行)
也就是说:
他只能被一个父事务调用。否则,抛出异常。
PROPAGATION_REQUIRES_NEW(如果有事务存在,挂起当前事务,创建一个新的事务)
比如说:
我们设计ServiceA.methodA()的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB()的事务级别为PROPAGATION_REQUIRES_NEW,那么当执行到ServiceB.methodB()的时候,ServiceA.methodA()所在的事务就会挂起,ServiceB.methodB()会起一个新的事务,等待ServiceB.methodB()的事务完成以后,他才继续执行。
他与PROPAGATION_REQUIRED的事务区别在于事务的回滚程度了。因为ServiceB.methodB()是新起一个事务,那么就是存在两个不同的事务。如果ServiceB.methodB()已经提交,那么ServiceA.methodA()失败回滚,ServiceB.methodB()是不会回滚的。如果ServiceB.methodB()失败回滚,如果他抛出的异常被ServiceA.methodA()捕获,ServiceA.methodA()事务仍然可能提交。
PROPAGATION_NOT_SUPPORTED(以非事务方式运行,如果有事务存在,挂起当前事务)
比如说:
ServiceA.methodA()的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB()的事务级别是PROPAGATION_NOT_SUPPORTED ,那么当执行到ServiceB.methodB()时,ServiceA.methodA()的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA()的事务。
PROPAGATION_NEVER(以非事务方式运行,如果有事务存在,抛出异常)
假设ServiceA.methodA()的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB()的事务级别是PROPAGATION_NEVER ,那么ServiceB.methodB()就要抛出异常了。
PROPAGATION_NESTED(如果当前事务存在,则嵌套事务执行)
ServiceA { void methodA() { try { //savepoint ServiceB.methodB(); //PROPAGATION_NESTED 级别 } catch (SomeException) { // 执行其他业务, 如 ServiceC.methodC(); } } }
理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。
也就是说,如果父事务最后回滚,他也要回滚的。而Nested事务的好处是他有一个savepoint。也就是说ServiceB.methodB()失败回滚,那么ServiceA.methodA()也会回滚到savepoint点上,ServiceA.methodA()可以选择另外一个分支,比如ServiceC.methodC(),继续执行,来尝试完成自己的事务。