一、Spring事务管理
1.Spring事务概述
事务transaction定义:最小不可分割的逻辑工作单元,具有ACID特性;
事务ACID特性:
- 原子性atomicity:由事务定义可知原子性是指,事务中的多个操作要么都成功要么都失败;
- 银行场景下:A、B两账户,A向B进行转账操作,若此间发生异常,事务会回滚,A、B两账户要保持在转账前状态;
- 一致性consistency:一个事务可以封装状态改变;
- 银行场景下:ABCE五个账户余额均为100元,总余额500元,5个账户间同时发生转账且无论并发量多大最终五个账户余额保持500元;
- 隔离性isolation:事务与事务之间相互隔离,主要特征是保护性和不变性;
- 隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。
- 持久性durability:事务一旦完成提交,数据便持久保存到数据库,不会被回滚;
Spring提供的两种事务管理方式:
- 编程式事务:通过编码方式实现事务
- 声明式事务:基于AOP,将具体业务逻辑与事务处理解耦;
- 一种方式:在配置文件(xml)中做相关的事务规则声明
- 二种方式:基于@Transactional注解方式
声明式事务管理使业务代码逻辑不受污染,因此在实际使用中声明式事务用得比较多;
二、Spring声明式事务处理
A.注解方式实现
1.在IOC中注入数据源事务管理DataSourceTransactionManager
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--设置注解驱动的事务管理 -->
<tx:annotation-driven transaction-manager="txManager"/>
2.注解应用事务:@Transactional(value="...",...)
@Transactional()接收的属性:
- value 当IOC中注入了多个DataSource时,通过value属性指定选择哪个事务管理器
- propagation 事务的传播行为,默认REQUIRED
- isolation 事务的隔离度,默认DEFAULT
- timeout 事务的超时时间,默认-1,如果超过该时间限制事务还没有完成则自动回滚事务
- read-only 只读事务设置,默认false,当需要忽略一些不需要事务的方法(比如读取数据)时可以设置为true
- rollback-for 指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔
- no-rollback-for 当代码执行抛出 no-rollback-for 指定的异常类型,不回滚事务
注意:@Transactional()添加到类上即可修饰类中所有公开方法;添加到方法上可以覆盖添加到类上的注解;
B.基于xml方式实现
1.配置事务管理器
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
2.配置事务处理方式
<tx:advice id="txAdvice"
transaction-manager="txManager">
<tx:attributes>
<tx:method name="*"
propagation="REQUIRED"
isolation="READ_COMMITTED"
timeout="-1"
read-only="false"
rollback-for="java.lang.Throwable"
no-rollback-for="NoTransactionException"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="operation"
expression="execution(* beans.service..*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut="operation"/>
</aop:config>
3.实例
定义事务管理器
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
定义事务策略
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--定义查询方法都是只读的 -->
<tx:method name="query*" read-only="true" />
<tx:method name="find*" read-only="true" />
<tx:method name="get*" read-only="true" />
<!-- 主库执行操作,事务传播行为定义为默认行为 -->
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<!--其他方法使用默认事务策略 -->
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 定义切面,所有的service的所有方法 -->
<aop:pointcut id="txPointcut"
expression="execution(* com.jt.sys.service..*.*(..))" />
<!-- 应用事务策略到Service切面 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
三、Spring事务补充
1.Spring事务的传播特性
@Transactional(propagation=Propagation.REQUIRED) 如果没有事务创建新事务, 如果当前有事务参与当前事务;
@Transactional(propagation=Propagation.REQUIRES_NEW)必须是新事务, 如果有当前事务, 挂起当前事务并且开启新事务
@Transactional(propagation=Propagation.MANDATORY)必须有事务, 如果当前没有事务就抛异常
@Transactional(propagation=Propagation.NEVER)绝对不能有事务, 如果在事务中调用则抛出异常
@Transactional(propagation=Propagation.NESTED)必须被嵌套到其他事务中
@Transactional(propagation=Propagation.NOT_SUPPORTED)不支持事务
@Transactional(propagation=Propagation.SUPPORTS)支持事务, 如果没有事务也不会创建新事务
2.Spring事务的隔离级别
多个事务并发执行时可能会导致什么问题?(脏读,不可重复读,幻读)
当多个事务并发执行时,可通过设置事务的隔离级别保证事务的完整性,一致性。
事务的隔离级别从低到高有如下几种方式:
1)READ_UNCOMMITTED (此级别可能会出现脏读)
2)READ_COMMITTED(此级别可能会出现不可重复读)
3)REPEATABLE_READ(此级别可能会出现幻读)
4)SERIALIZABLE(多事务串行执行)
说明:spring中一般采用 @Transactional(isolation=Isolation.READ_COMMITTED) 方式声明级别, 这种方式是并发性能和安全性折中的选择. 是大多数软件项目采用的隔离级别.
3.MySql隔离级别
查看InnoDB存储引擎 系统级的隔离级别 和 会话级的隔离级别
更改会话级的隔离级别
更改系统级的隔离级别
思考:
1)MySQL 中如何查看当前系统默认隔离级别?
show variables like '%storage_engine%';
2)MySQL 中如何设置事务隔离级别?
set session transaction isolation level 'reapable read'