Spring声明式事务
1. Spring事务管理
1.1 编程式事务
使用原生的JDBC API进行事务管理
- 获取数据库连接Connection对象
- 取消事务的自动提交
- 执行操作
- 正常完成操作时手动提交事务
- 执行失败时回滚事务
- 关闭相关资源
1.2 声明式事务
- 大多数情况下声明式事务比编程式事务管理更好:它将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
- 事务管理代码的
固定模式作为一种横切关注点
,可以通过AOP方法模块化,进而借助Spring AOP
框架实现声明式事务管理。- Spring在不同的事务管理API之上定义了一个抽象层,通过配置的方式使其生效,从而让应用程序开发人员不必了解事务管理API的底层实现细节,就可以使用Spring的事务管理机制。
- Spring既支持编程式事务管理,也支持声明式的事务管理。
1.3 Spring 声明式事务的实现
- 配置事务管理器
- 开启基于注解的声明式事务
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--1. 开启包扫描-->
<context:component-scan base-package="com.lz.tx"/>
<!--2. 引入外部配置文件-->
<context:property-placeholder location="jdbc.properties"></context:property-placeholder>
<!--3. 配置数据源-->
<bean id="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="jdbcUrl" value="${jdbc.jdbcURL}"/>
<property name="driverClass" value="${jdbc.driverClass}"/>
</bean>
<!--4. 配置jdbcTemplate操作数据库-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="pooledDataSource"/>
</bean>
<!--5. 配置声明式事务-->
<!--5.1 配置事务管理器-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="pooledDataSource"/>
</bean>
<!--5.2 开启基于注解的声明式事务, 依赖-->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--5.3 给事务方法加注解-->
</beans>
- 给事务方法加注解
2. 细节问题
2.1 事务超时—timeout
@Transaction(timeout=3)
事务超出指定执行时长后自动终止并回滚
2.2 只读事务—readOnly
@Transaction(readOnly=true)
设置事务为只读事务, 进行事务优化, 加快查询速度
2.3 触发事务回滚的异常— rollbackFor, noRollbackFor
- 默认情况
捕获到RuntimeException或Error时回滚,而捕获到编译时异常不回滚。 - 设置途径
- rollbackFor属性:指定遇到时必须进行回滚的异常类型,可以为多个
- noRollbackFor属性:指定遇到时不回滚的异常类型,可以为多个
2.4 隔离级别—isolation
2.4.1 事务并发问题
- 脏读
[1]Transaction01将某条记录的AGE值从20修改为30。
[2]Transaction02读取了Transaction01更新后的值:30。
[3]Transaction01回滚,AGE值恢复到了20。
[4]Transaction02读取到的30就是一个无效的值。 - 不可重复读
[1]Transaction01读取了AGE值为20。
[2]Transaction02将AGE值修改为30。
[3]Transaction01再次读取AGE值为30,和第一次读取不一致。 - 幻读
[1]Transaction01读取了STUDENT表中的一部分数据。
[2]Transaction02向STUDENT表中插入了新的行。
[3]Transaction01读取了STUDENT表时,多出了一些行。
2.4.2 隔离级别
数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。
一个事务与其他事务隔离的程度称为隔离级别
。SQL标准中规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。
- 读未提交:READ UNCOMMITTED
允许Transaction01读取Transaction02未提交的修改。 - 读已提交:READ COMMITTED
要求Transaction01只能读取Transaction02已提交的修改。 - 可重复读:REPEATABLE READ
确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。 - 串行化:SERIALIZABLE
确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。
各个隔离级别解决并发问题的能力见下表
2.4.3 Spring 中指定事务隔离级别—isolation
2.5 传播行为—propagation
如果有多个事务进行嵌套运行, 子事务是否要和大事务共用一个事务.
Spring定义了7种类传播行为。常用的 REQUIRED 传播行为和 REQUIRES_NEW 传播行为
注意: 小事务设置为REQUIRED, 则小事务的其他属性来源于大事务
3. 基于XML的声明式事务配置
<aop:config>
<aop:pointcut id="txPoint" expression="execution(* com.lz.tx.service.*.*(..))"/>
<aop:advisor advice-ref="myAdvice" pointcut-ref="txPoint"/>
</aop:config>
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--指明哪些方法是事务方法-->
<tx:method name="checkout" propagation="REQUIRES_NEW" timeout="-1"/>
<tx:method name="get*" read-only="true"/>
</tx:attributes>
</tx:advice>