说明:本文参考以下文章
https://blog.csdn.net/whbing1471/article/details/54097574
https://www.cnblogs.com/huanongying/p/7021555.html
一、事务的基本要素
1、原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。
2、一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。
3、隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
4、持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。
二、事务并发问题
1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
三、mysql数据库隔离级别
大多数的数据库系统的默认事务隔离级别都是:Read committed
而MySQL的默认事务隔离级别是:Repeatable Read
Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分
一、隔离性
TransactionDefinition接口中定义了五个不同的事务隔离级别、与数据库中的隔离级别相对应
(a) ISOLATION_DEFAULT:(PlatfromTransactionManager的)默认的隔离级别。使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应 。
(b)ISOLATION_READ_UNCOMMITTED:这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
例如:
Mary的原工资为1000,财务人员将Mary的工资改为了8000,但未提交事务。与此同时,Mary正在读取自己的工资。Mary发现自己的工资变为了8000,欢天喜地!而财务发现操作有误,而回滚了事务,Mary的工资又变为了1000 。像这样,Mary记取的工资数8000是一个脏数据。
(c)ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。
(d)ISOLATION_REPEATABLE_READ : 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
例如:
在事务1中,Mary 读取了自己的工资为1000,操作并没有完成。在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务。在事务1中,Mary 再次读取自己的工资时,工资变为了2000在一个事务中前后两次读取的结果并不致,导致了不可重复读。使用ISOLATION_REPEATABLE_READ可以避免这种情况发生。
(e)ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。
目前工资为1000的员工有10人。事务1,读取所有工资为1000的员工。共读取10条记录。这时另一个事务向employee表插入了一条员工记录,工资也为1000。事务1再次读取所有工资为1000的员工。共读取到了11条记录,这就产生了幻像读。
ISOLATION_SERIALIZABLE能避免这样的情况发生。但是这样也耗费了最大的资源。
二、传播性
在TransactionDefinition接口中定义了七个事务传播行为。
(a)PROPAGATION_REQUIRED:如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
(b)PROPAGATION_SUPPORTS :如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。
(c)PROPAGATION_MANDATORY :如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
(d)PROPAGATION_REQUIRES_NEW :总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
ts1为外层事务,ts2为内层事务。ts2与ts1是两个独立的事务,互不相干。Ts2是否成功并不依赖于ts1
使用PROPAGATION_REQUIRES_NEW,需要使用JtaTransactionManager作为事务管理器。
(e)PROPAGATION_NOT_SUPPORTED: 总是非事务地执行,并挂起任何存在的事务。
使用PROPAGATION_NOT_SUPPORTED,也需要使用JtaTransactionManager作为事务管理器。
(f)PROPAGATION_NEVER :总是非事务地执行,如果存在一个活动事务,则抛出异常:
(g)PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
这是一个嵌套事务,使用JDBC 3.0驱动时,仅仅支持DataSourceTransactionManager作为事务管理器。需要JDBC 驱动的java.sql.Savepoint类。有一些JTA的事务管理器实现可能也提供了同样的功能。
使用PROPAGATION_NESTED,还需要把PlatformTransactionManager的nestedTransactionAllowed属性设为true;
而nestedTransactionAllowed属性值默认为false;
嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。
PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别:它们非常类似,都像一个嵌套事务,如果不存在一个活动的事务,都会开启一个新的事务。使用PROPAGATION_REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。两个事务不是一个真正的嵌套事务。同时它需要JTA事务管理器的支持。
使用PROPAGATION_NESTED时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。DataSourceTransactionManager使用savepoint支持PROPAGATION_NESTED时,需要JDBC 3.0以上驱动及1.4以上的JDK版本支持。其它的JTA TrasactionManager实现可能有不同的支持方式。
PROPAGATION_REQUIRED应该是我们首先的事务传播行为。它能够满足我们大多数的事务需求。
三、超时
@Transactional(timeout=30) //默认是30秒
四、回滚
指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。
五、只读
@Transactional(readOnly=true)
该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。