一、事务的理解
事务是数据库管理系统执行过程中的一个逻辑单位,是一个原子操作单元,就是一组sql指令,要么全部成功要么全部撤销不执行
二、事务的特性-ACID
- 原子性(Atomicity):每次操作都是原子操作,要么全部成功,要么全部失败
- 一致性(Consistency):事务提交之前和提交之后的的数据应该都是一致的
- 隔离性(Isolation):每个事务都是独立的事务,多个并发事务要相互隔离
持久性(Durability):事务提交之后,数据就是永久性发生变化
三、spring事务的实现方式
声明式事务
基于xml配置或者注解方式(在类上添加 @Transaction 注解)
- 编程式事务
四、事务的传播特性-7个
支持当前事务
- int PROPAGATION_REQUIRED = 0; 有事务就加入,没事务,则新建一个事务;
- int PROPAGATION_SUPPORTS = 1; 有事务就加入,没事务,则以非事务方式运行;
- int PROPAGATION_MANDATORY = 2; 有事务就加入,没事务,则抛出异常;
不支持当前事务
- int PROPAGATION_REQUIRES_NEW = 3; 创建一个新事务,当前有事务就挂起;
- int PROPAGATION_NOT_SUPPORTED = 4; 以非事务方式运行,当前有事务就挂起;
- int PROPAGATION_NEVER = 5; 以非事务方式运行,当前有事务,则抛出异常;
- int PROPAGATION_NESTED = 6; 当前有事务就创建一个事务作为嵌套事务运行,如果当前不存在事务,则参照PROPAGATION_REQUIRED
对于NESTED事务,如果外部事务异常,则内部事务也必须回滚;如果内部事务异常回滚,则不影响外部事务;
五、事务的隔离级别-5个
spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致:
- ISOLATION_DEFAULT:用底层数据库的设置隔离级别,数据库设置的是什么我就用什么;
- ISOLATIONREADUNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读);
- ISOLATIONREADCOMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server 的默认级别;
- ISOLATIONREPEATABLEREAD:可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别;
- ISOLATION_SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。
脏读 :表示一个事务能够读取另一个事务中还未提交的数据。比如,某个事务尝试插入记录 A,此时该事务还未提交,然后另一个事务尝试读取到了记录 A。
不可重复读 :是指在一个事务内,多次读同一数据。
幻读 :指同一个事务内多次查询返回的结果集不一样。比如同一个事务 A 第一次查询时候有 n 条记录,但是第二次同等条件下查询却有 n+1 条记录,这就好像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一个事务结果集里面的数据,同一个记录的数据内容被修改了,所有数据行的记录就变多或者变少了。
六、注解事务的配置项
value 和 transactionManager 属性
它们两个是一样的意思。当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器。
propagation 属性
事务的传播行为,默认值为 Propagation.REQUIRED。
isolation 属性
事务的隔离级别,默认值为 Isolation.DEFAULT。
timeout 属性
事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
readOnly 属性
指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
rollbackFor 属性
noRollbackFor 属性
七、Spring内置事务管理器实现
1、DataSourceTransactionManager
位于org.springframework.jdbc.datasource包中,数据源事务管理器,提供对单个javax.sql.DataSource事务管理,用于Spring JDBC抽象框架、iBATIS或MyBatis框架的事务管理;
2、HibernateTransactionManager
位于org.springframework.orm.hibernate3包中,提供对单个org.hibernate.SessionFactory事务支持,用于集成Hibernate框架时的事务管理;该事务管理器只支持Hibernate3+版本,且Spring3.0+版本只支持Hibernate3.2+版本;
3、JpaTransactionManager
位于org.springframework.orm.jpa包中,提供对单个javax.persistence.EntityManagerFactory事务支持,用于集成JPA实现框架时的事务管理;
4、JtaTransactionManager
位于org.springframework.transaction.jta包中,提供对分布式事务管理的支持,并将事务管理委托给Java EE应用服务器事务管理器;
5、JdoTransactionManager
位于org.springframework.orm.jdo包中,提供对单个javax.jdo.PersistenceManagerFactory事务管理,用于集成JDO框架时的事务管理;
6、OC4JjtaTransactionManager
位于org.springframework.transaction.jta包中,Spring提供的对OC4J10.1.3+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持;
7、WebSphereUowTransactionManager
位于org.springframework.transaction.jta包中,Spring提供的对WebSphere 6.0+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持;
8、WebLogicJtaTransactionManager
位于org.springframework.transaction.jta包中,Spring提供的对WebLogic8.1+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持。
八、事务的回滚机制
1、回滚机制-原子性
事务回滚机制,也就是在说MySQL的事务原子性是如何实现的。
所谓原子性,就是指一个事务是一个不可分割的工作单位,其中的操作要么都做,要么都不做;如果事务中的一个sql语句执行失败,则已执行的语句必须回滚,数据库会退回到事务前的状态。
2、实现原理
在说明原理之前,需要首先介绍一下MySQL的事务日志。
MySQL的日志有很多种,如二进制日志、错误日志、查询日志、慢查询日志等,此外InnDB引擎还提供了两种事务日志:redo log(重做日志)和undo log(回滚日志)。
- 其中redo log用于保证事务持久性;
- undo log则是事务原子性和隔离性实现的基础。
我们这里,之所以能够保证原子性,则是靠undo log。
当事务对数据库进行修改时,InnDB会生成对应的undo log;如果事务失败或者调用了rollback,导致事务回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。
undo log属于逻辑日志,它记录的是sql执行相关的信息。当发生回滚时,InnoDB会根据undo log的内容做与之前相反的工作:对于每个insert,回滚时会执行delete;对于每个delete,回滚时会执行insert;对于每个update,回滚时会执行一个相反的update,把数据改回去。
Spring事务什么时候会失效?
spring事务的原理是AOP,进行了切面增强,那么失效的根本原因是这个AOP不起作用了!常见情况有如下几种
1、发生自调用,类里面使用this调用本类的方法(this通常省略),此时这个this对象不是代理类,而是UserService对象本身!解决方法很简单,让那个this变成UserService的代理类即可!
2、方法不是public的:@Transactional 只能用于 public 的方法上,否则事务不会失效,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。因为底层是使用cjlib是基于父子类实现的,子类是不能重载父类的private方法的,无法很好的利用代理,会导致失效
3、数据库不支持事务,mysql的mylsam引擎不支持事务,innodb支持事务
4、没有被spring管理
5、异常被吃掉,事务不会回滚(或者抛出的异常没有被定义,默认为RuntimeException)
Spring中的事务是如何实现的
- Spring事务底层是基于数据库事务和AOP机制的
- 首先对于使用了@Transactional注解的Bean,Spring会创建一个代理对象作为Bean
- 当调用代理对象的方法时,会先判断该方法上是否加了@Transactional注解
- 如果加了,那么则利用事务管理器创建一个数据库连接
- 并且修改数据库连接的autocommit属性为false,禁止此连接的自动提交,这是实现Spring事务非常重要的一步
- 然后执行当前方法,方法中会执行sql
- 执行完当前方法后,如果没有出现异常就直接提交事务
- 如果出现了异常,并且这个异常是需要回滚的就会回滚事务,否则仍然提交事务
- Spring事务的隔离级别对应的就是数据库的隔离级别
- Spring事务的传播机制是Spring事务自己实现的,也是Spring事务中最复杂的
- Spring事务的传播机制是基于数据库连接来做的,一个数据库连接一个事务,如果传播机制配置为需要新开一个事务,那么实际上就是先建立一个数据库连接,在此新数据库连接上执行sql