事务的快速入门原理及spring的事务控制
1.事务概念
什么是事务: 一个包含多个步骤的业务逻辑操作,被事务控制,这些操作同时成功或失败。
在实际的开发过程中,一个业务操作如:转账,往往是要多次访问数据库才能完成的。转账是一个用户扣钱,另一个用户加钱。如果其中有一条SQL语句出现异常,这条SQL就可能执行失败。
事务执行是一个整体,所有的SQL语句都必须执行成功。如果其中有1条SQL语句出现异常,则所有的SQL语句都要回滚,整个业务执行失败。
在mysql数据库中DML操作时,事务默认是自动提交的,而在oracle数据库中,事务则是需要手动提交的。
手动提交事务的sql语句
功能 | SQL语句 |
---|---|
开启事务 | start transaction |
提交事务 | commit |
回滚事务 | rollback |
2.事务原理
事务开启之后, 所有的操作都会临时保存到事务日志中, 事务日志只有在得到commit命令才会同步到数据表中,其他任何情况都会清空事务日志(rollback,断开连接)。
事务的步骤:
1)客户端连接数据库服务器,创建连接时创建此用户临时日志文件
2)开启事务以后,所有的操作都会先写入到临时日志文件中
3)所有的查询操作从表中查询,但会经过日志文件加工后才返回
4)如果事务提交则将日志文件中的数据写到表中,否则清空日志文件。
回滚点(savepoint):在某些成功的操作完成之后,后续的操作有可能成功有可能失败,但是不管成功还是失败,前面操作都已经成功,可以在当前成功的位置设置一个回滚点。可以供后续失败操作返回到该位置,而不是返回所有操作,这个点称之为回滚点。
3.事务特性
事务的四大特性:
- 原子性 (atomicity):原子性是指事务是一个不可分割的最小操作单位,事务中的操作要么都发生,要么都不发生。
- 一致性 (consistency):事务的执行的前后数据的完整性保持一致. 事务必须使数据库从一个一致性状态变换到另外一个一致性状态,操作前后总量不变。
- 隔离性 (isolation):事务与事务之间不应该相互影响,执行时保持隔离的状态。
- 持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的。
4.事务隔离级别
事务在操作时的理想状态: 所有的事务之间保持隔离,互不影响。因为并发操作,多个用户同时访问同一个数据。可能引发并发访问的问题:
- 脏读:一个事务读取到了另一个事务中尚未提交的数据
- 不可重复读:一个事务中两次读取的数据内容不一致,要求的是一个事务中多次读取时数据是一致的,这是事务update时引发的问题
- 幻读:一个事务中两次读取的数据的数量不一致,要求在一个事务多次读取的数据的数量是一致的,这是insert或delete时引发的问题
MySQL数据库有四种隔离级别:
级别 | 名字 | 隔离级别 | 脏读 | 不可重复读 | 幻读 | 数据库默认级别 |
---|---|---|---|---|---|---|
1 | 读未提交 | read uncommitted | 是 | 是 | 是 | |
2 | 读已提交 | read committed | 否 | 是 | 是 | Oracle和SQL Server |
3 | 可重复读 | repeatable read | 否 | 否 | 是 | MySQL |
4 | 串行化 | serializable | 否 | 否 | 否 |
- read uncommited:是最低的事务隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。
- read commited:保证一个事物提交后才能被另外一个事务读取。另外一个事务不能读取该事物未提交的数据。
- repeatable read:这种事务隔离级别可以防止脏读,不可重复读。但是可能会出现幻象读。它除了保证一个事务不能被另外一个事务读取未提交的数据之外还避免了以下情况产生(不可重复读)。
- serializable:这是花费最高代价但最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读之外,还避免了幻象读(避免三种)。
隔离级别越高,性能越差,安全性越高。
查询隔离级别 select @@tx_isolation;
设置隔离级别 set global transaction isolation level 级别字符串;
5.事务的传播行为(7种)
PROPAGION_XXX :事务的传播行为
- 保证同一个事务中
1、PROPAGATION_REQUIRED:
如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
2、PROPAGATION_SUPPORTS:
支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
3、PROPAGATION_MANDATORY:
支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。 - 保证没有在同一个事务中
4、PROPAGATION_REQUIRES_NEW:
创建新事务,无论当前存不存在事务,都创建新事务。
5、PROPAGATION_NOT_SUPPORTED:
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6、PROPAGATION_NEVER:
以非事务方式执行,如果当前存在事务,则抛出异常。
7、PROPAGATION_NESTED:
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
6.spring的声明式事务控制
基于xml的声明式事务控制:
- 配置事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
- 配置事务通知
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"></tx:method>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
</tx:attributes>
</tx:advice>
- 配置事务通知与切入点的关联
<aop:config>
<aop:pointcut id="pt" expression="execution(* xx.xx.service.impl.*.*(..))"></aop:pointcut>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
</aop:config>
基于注解的事务控制:
在service类前加上@Transactional,声明这个service所有方法需要事务管理。每一个业务方法开始时都会打开一个事务。