Spring事务管理
1. 事务的定义
事务是一组不可分割的逻辑操作单元,这些操作要么全部成功执行,要么在遇到错误时全部回滚,以保持数据的一致性和完整性。
2. 四大特性(ACID)
原子性(Atomicity):事务是最小的执行单位,不可再分,事务中的所有操作要么全部完成,要么全部不执行。
一致性(Consistency):事务执行前后,数据库的状态必须保持一致。即从一个一致性状态转换到另一个一致性状态。
隔离性(Isolation):多个事务并发执行时,一个事务的执行不应被其他事务所干扰,事务之间应该相互隔离。
持久性(Durability):一旦事务被提交,它对数据库的修改就是永久性的,即使系统发生故障也不会丢失。
3. 事务引发的问题
事务在并发执行时,可能会遇到以下读问题:
脏读(Dirty Read):一个事务读取了另一个事务未提交的数据。
不可重复读(Non-repeatable Read):在同一事务内,多次读取同一数据集合时,由于其他事务的更新操作,导致每次读取的数据不一致。
幻读(Phantom Read):在同一事务内,多次读取同一范围的数据时,由于其他事务的插入操作,导致每次读取的数据行数不一致。
4. 事务的隔离级别(Isolation Levels)
数据库系统提供了几种不同的事务隔离级别,以解决上述读问题:
READ UNCOMMITTED(未提交读):最低级别,允许读取尚未提交的数据变更,可能导致脏读、不可重复读和幻读。
READ COMMITTED(已提交读):允许读取已经提交的数据,避免了脏读,但可能出现不可重复读和幻读。
REPEATABLE READ(可重复读):保证在同一个事务中多次读取同样记录的结果是一致的,避免了脏读和不可重复读,但可能出现幻读(MySQL的默认隔离级别)。
SERIALIZABLE(串行化):最高的隔离级别,通过强制事务串行执行,避免了脏读、不可重复读和幻读,但会大大降低数据库系统的并发性能。
5. Spring事务管理的原理
Spring通过PlatformTransactionManager接口提供平台无关的事务管理功能,该接口定义了事务管理的基本方法。不同的持久化技术(如JDBC、Hibernate)有不同的实现类,如JdbcTransactionManager和HibernateTransactionManager。
TransactionDefinition:定义了事务的属性,如传播行为、隔离级别、超时时间等。
TransactionStatus:表示事务的状态,如是否有保存点、是否为新事务、是否已完成等。
Spring事务管理的核心在于,当执行事务性方法时,Spring会利用PlatformTransactionManager创建事务,并根据TransactionDefinition中的定义来配置事务的属性。事务执行过程中,TransactionStatus会记录事务的状态变化,以便在需要时进行回滚或提交。
6. 事务的传播行为(Propagation Behaviors)
Spring定义了七种事务传播行为,用于控制事务的边界和嵌套关系:
PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是Spring的默认传播行为。
PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。
PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
PROPAGATION_REQUIRES_NEW:创建一个新的事务,并挂起当前事务(如果存在)。
PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,则把当前事务挂起。
PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则行为等同于PROPAGATION_REQUIRED。嵌套事务允许你有一个事务内嵌另一个事务,内层事务拥有多次的提交/回滚操作,而外层事务不受影响,只有在外层事务提交时,内层事务所做的更改才会被永久保存。
这些传播行为为开发者提供了灵活的事务控制手段,以适应不同的业务场景需求。
7. 编程式事务管理
- 平台事务管理器
根据dao层的底层技术来选择对应的平台事务管理器
<!--
平台事务管理器
-->
<bean id="transactionManager" class="org.springframework.jdbc.support.JdbcTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
- TrasactionTemplate
事务管理模板
<!--
提供了事务管理的相关操作
采用了数据库默认的隔离级别
采用了spring默认的传播行为 Propagtion_required
-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
</bean>
@Autowired
private AccountDao dao;
@Autowired
private TransactionTemplate transactionTemplate; // 事务管理模板
@Override
public boolean transfer(Integer fromId, Integer toId, Integer account) {
Boolean status = transactionTemplate.execute(new TransactionCallback<Boolean>(){
/**
* 在该方法中执行的所有操作使用了同一个事务
*/
@Override
public Boolean doInTransaction(TransactionStatus status) {
int flag =0;
flag+=dao.saveAccount(fromId, account*-1, getNow());
sendMessage(fromId);
flag+=dao.saveAccount(toId, account, getNow());
sendMessage(toId);
return flag==2?true:false;
}
});
return status;
}
缺点
改变了业务逻辑的结构
8. 声明式事务管理
在spring中提供了事务管理,采用AOP的形式,对切入点进行事务的增强,并且spring提供了事务增强的标签,只需要通过配置就能够实现事务管理。
- 基于xml
1.平台事务管理器
2.增加tx标签,spring提供的增强
3.aop配置
<!--
平台事务管理器
-->
<bean id="transactionManager" class="org.springframework.jdbc.support.JdbcTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--
事务的增强
transaction-manager:指定增强采用的平台事务管理器
-->
<tx:advice id="tx" transaction-manager="transactionManager">
<!--
事务的属性的配置
-->
<tx:attributes>
<!--
在切入点的基础上对不同的方法进行不同的事务配置
-->
<tx:method name="insert*" isolation="DEFAULT" propagation="REQUIRED"/>
<tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED"/>
<tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED"/>
<tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED"/>
<tx:method name="remove*" isolation="DEFAULT" propagation="REQUIRED"/>
<!--
read-only:true可以提高查询效率
-->
<tx:method name="query*" read-only="true" isolation="DEFAULT" propagation="REQUIRED"/>
<tx:method name="select*" read-only="true" isolation="DEFAULT" propagation="REQUIRED"/>
<tx:method name="transfer" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(* com.xxx.service.impl.*.*(..))" id="p1"/>
<aop:advisor advice-ref="tx" pointcut-ref="p1"/>
</aop:config>
注意
因为spring声明式事务采用的是AOP,
1.所以同类中的方法调用会导致事务失效
class Service1{
@autoWird
Service1 service;
a(){
}
b(){
}
c(){
service.a()
int i=1/0;
service.b()
}
}
Service1Proxy对象调用
c() 调用的是目标对象的方法
- public修饰符
- final修饰符
- 基于注解
- 定义平台事务管理器
- 定义事务的注解驱动
<!-- 开启事务管理的注解驱动 transaction-manager:平台事务管理器 proxy-target-class: 是否强制使用cglib --> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
@Transactional
类级别:该类中所有方法使用了定义的事务
方法级别:该方法使用了定义的事务