Spring的事务处理
1.什么是事务?
保证业务操作完整性的一种数据库机制
事务4个特点:A C I D
1.A 原子性
2.C 一致性
3.I 隔离性
4.D 持久性
2.如何控制事务
JDBC:
Connection.setAutoCommit(false)
Connection.commit()
Connection.rollback()
Mybatis:
Mybatis自动开启事务
sqlSession(底层也封装了Connection).commit()
sqlSession.rollback()
结论:控制事务的底层 都是Connection对象完成
3.Spring控制事务的开发
Spring通过AOP的方式进行事务开发
1.原始对象
public class XXXUserServiceImpl{
1.原始对象-->原始方法--->核心功能(业务处理+DAO调用)
2.DAO最为Service的成员变量,依赖注入的方式进行赋值
}
2.额外功能org.springframework.jdbc.datasource.DataSourceTransactionManage
注入DataSource
MethodInterceptor
public Object invoke(MethodInvocation invocation){
try{
Connection.setAutoCommit(false)
Object ret=invocation.proceed();
Connection.commit()
}catch(Exception e){
Connection.rollback();
}
return ret;
}
@Aspect
@Around
3.切入点
@Transactional
事务的额外功能加入哪些业务方法
1.类上:类中所有方法都会加入事务
2.方法上:这个方法会加入事务
4.组装切面
1.切入点
2.额外功能
<tx:annotation-driven transaction-manager=""/>
4.Spring事务的编码
- 搭建开发环境(jar)
XML
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.1.14.RELEASE</version>
</dependency>
- applicationContext.xml
java
@Transactional
public class EmpServiceImpl implements EmpService{
public EmpDao getEmpDao() {
return empDao;
}
XML
<!-- MapperScannerConfigure自动创建Dao接口的实现类 并且命名就是按照DAO接口首单词首字母小写创建-->
<bean id="empService" class="mybatis.service.EmpServiceImpl">
<property name="empDao" ref="empDao"/>
</bean>
<!-- DataSourceTransactionManager-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- @Transactional-->
<!-- -->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
- 细节
<tx:annotation-driven transaction-manager="dataSourceTransactionManager" proxy-target-class="true"/>
进行动态代理底层实现切换
默认 false JDK
true Cglib
Spring事务属性
1.什么是事务属性?
属性:描述物体特征的一系列值
事务属性:描述事务特征的一系列值
1.隔离属性
2.传播属性
3.只读属性
4.超时属性
5.异常属性
2.如何添加事务属性
@Transactional(isloation=.propagation=,readOnly,timeout=,rollbackFor=,noRollbackFor)
1.隔离属性(Isolation)
- 隔离属性概念
概念:它描述了事务解决并发问题的特征
1.什么是并发?
多个事务(用户)在同一时间,访问操作了相同的数据
同一时间 0.000几秒 微小前后
2.并发会产生哪些问题
1.脏读
2.不可重复读
3.幻影读
3.并发问题如何解决
通过隔离属性解决,隔离属性设置不同的值,解决并发处理过程中的问题
事务并发产生的问题
脏读
一个事务,读取了另一个事务中没有提交的数据,会在本事务中产生数据不一致的问题
举例:
客户A向B转账100元,事务步骤如下:
读取A余额(500元)
扣减A账户(500-100=400)
未提交时系统崩溃
B账户未收到款项(仍为200元)
此时数据库出现脏数据:
A账户实际余额为400元(未提交的中间状态)
但事务已回滚,系统仍显示A余额为500元
若其他事务读取A余额,可能获取到400元的"脏数据"
解决方案:
@Transactional(isolation = Isolation.READ_COMMITTED)
不可重复读
一个事务中,多次读取相同的数据,但是读取结果不一样,会在本事务中产生数据不一致的问题
事务A查询账户余额为500元,此时事务B修改余额为400元并提交。事务A再次查询时发现余额变为400元,同一事务内两次读取结果不同,即为不可重复读。
具体过程:
事务A读取余额(500元)
事务B更新余额(400元)并提交
事务A再次读取,得到400元(与第一次不一致)
解决方案:@Transactional(isolation=Isolation.REPEATABLE_READ)
本质相当于事务A读取的时候加了一把行锁
幻影读
一个事务中,多次对整个表进行查询统计,但是结果不一样,会在本事务中产生数据不一致的问题
如:
事务A查询账户列表(当前有A、B两个账户),此时事务C新增账户C并提交。事务A再次查询时发现多出一条记录(A、B、C),同一事务内两次查询结果集不同,即为幻影读。
具体过程:
事务A查询账户表(结果:A、B)
事务C插入账户C并提交
事务A再次查询,结果变为A、B、C
与不可重复读的区别在于:幻影读针对新增或删除的行,而非修改已有数据
解决方案:
@Transactional(isolation = Isolation.SERIALIZABLE)
本质:表锁
总结:
并发安全:**SERIALIZABL>REPEATABLE_READ>**READ_COMMITTED
运行效率:READ_COMMITTED> REPEATABLE_READ>SERIALIZABL
READ_COMMITTED
不读未提交的数据(No Dirty Read) :事务只能看到其他事务已提交的修改。
但允许已提交的数据被其他事务修改 :
为什么安全性最低?
不可重复读(Non-repeatable Read):事务A第一次读数据后,事务B可以修改并提交,事务A第二次读可能看到不同的值。幻影读(Phantom Read):事务A查询某范围数据时,事务B可以插入或删除符合条件的数据并提交,导致事务A两次查询结果集不一致。