事务处理
01 概念
1、保证业务操作完整性的一种数据库机制
2、事务的四个特点:A C I D
A
:原子性C
:一致性I
:隔离性D
: 持久性
02 如何来控制事务
普通的事务控制底层,都是使用Connection对象来完成的
2.1 使用 jdbc 如何处理事务
Connection.setAutoCommit(false)
;Connection.commit()
;Connection.rollback()
;
2.2 使用mybatis处理事务
Mybatis 会自动提交事务
SqlSession(Connection).commit()
;SqlSession(Connection).rollback()
;
03 Spring控制事务的开发
:::success
Spring是通过AOP的方式进行事务的开发
:::
3.1 分析
3.1.1 原始对象
public XXXUserServiceImpl implements UserService{
private XXXUserMapper usermapper;
}
3.1.2 额外功能
方式一:实现MetnodIntercepetor 接口
public Object invoke(MethodInvocation invocation){
try{
Connection.setAutoCommit(false);
Object ret = invocation.proceed();
Connection.commit();
return ret;
}catch(Exception e){
Connection.rollback();
}
}
方式二:@Aspect
Spring提供:org.springframework.jdbc.datasource.DataSourceTransactionManager
注入连接,注入连接池对象
3.1.3 切入点
:::success
@Transactional:事务的额外功能加给那些业务方法
:::
- 类上:类上所有的方法都会加入事务
- 方法上:这个方法会加上事务
3.1.4 组装切面
- 切入点
- 额外功能
<tx:annotation-driven transaction-manager=""/>
3.2 编码
3.2.1 搭建开发环境
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.9</version>
</dependency>
3.2.2 原始对象
@Data
public class UserServiceImpl implements UserService {
private UserMapper userMapper;
@Override
public void register(User user) {
userMapper.register(user);
}
}
3.2.3 额外功能
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
3.2.4 切入点
加入注解
@Data
@Transactional
public class UserServiceImpl implements UserService {
private UserMapper userMapper;
@Override
public void register(User user) {
userMapper.register(user);
}
}
3.2.5 组装切面
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"></tx:annotation-driven>
3.3 细节分析
- 进行动态代理底层实现的切换
- 默认值为false,底层是JDK
- 如果说指定位true,底层就是Cglib
<tx:annotation-driven transaction-manager="dataSourceTransactionManager" proxy-target-class="false" />
04 Spring中的事务属性
4.1 什么是事务属性
:::info
属性:描述物体特征的一系列值
性别,身高,体重。。。。。
事务属性:描述事务特征的一系列值
:::
- 隔离属性
- 传播属性
- 只读属性
- 超时属性
- 异常属性
4.2 如何添加事务属性
@Transactional(isloation=对应的值)
4.3 隔离属性
4.3.1 隔离属性的概念
- 描述了事务解决并发问题的特征
- 什么是并发:多个事务(用户)在同一时间,访问操作了相同的数据
- 并发会产生什么问题
- 脏读
- 不可重复读
- 幻影读
- 并发问题如何解决:通过隔离属性解决,隔离属性中设置不同的值,解决并发处理过程中的问题
4.3.2 脏读
- 一个事务,读取了另一个事务中没有提交的数据,会在本事务中产生数据不一致的问题
@Transactional(isolation = Isolation._READ_COMMITTED_)
4.2.3 不可重复读
- 一个事务中,多次读取相同的数据,但是读取的结果不一致,会在本事务中产生数据不一致的问题
- 本质是一把行锁
@Transactional(isolation = Isolation._REPEATABLE_READ_)
4.2.4 幻影读
- 一个事务中,多次对整表进行查询统计,但是结果不一致,会在本事务中产生数据不一致的问题
@Transactional(isolation = Isolation._SERIALIZABLE_)
- 本质是表锁
4.2.5 总结
- 并发安全:
SERIALIZABLE > REPEATABLE_READ > READ_COMMITTED
- 运行效率:
SERIALIZABLE < REPEATABLE_READ < READ_COMMITTED
ISOLATION_DEFAULT
:会调用不同数据库所设置的默认隔离属性MYSQL
:REPEATABLE_READ
Oracle
:READ_COMMITTED
4.2.6 数据库对隔离属性的支持
隔离属性的值 | MySQL | Oracle |
---|---|---|
Isolation._READ_COMMITTED_ | 支持 | 支持 |
Isolation._REPEATABLE_READ_ | 支持 | 不支持(采取多版本对比的方式) |
Isolation._SERIALIZABLE_ | 支持 | 支持 |
4.2.7 实战建议
- 推荐使用Spring指定的
ISOLATION_DEFAULT
- 未来中的实战中,并发访问情况很低
- 如果说真遇见并发问题,使用乐观锁
4.4 传播属性
4.4.1 概念
- 描述了事务解决嵌套问题的特征
- 事务嵌套:指的是一个大的事务中,包含一个小的事务
- 问题:大事务中融入了很多小的事务,他们彼此影响,最终就会导致外部大的事务,丧失了事务的原子性
4.4.2 传播属性的值及其用法
REQUIRED
- 传播属性的默认值
- 外部不存在事务:开启新的事务
- 外部存在事务:融合到外部事务中
- 用法:
@Transactional(propagation = Propagation._REQUIRED_)
- 主要用于增删改方法之中,可以不进行书写,本身就是默认值
SUPPORTS
- 外部不存在事务:不开启新的事务
- 外部存在事务:融合到外部事务中
- 用法:
@Transactional(propagation = Propagation._SUPPORTS_)
- 主要运行在查询方法之中
REQUIRES_NEW
- 外部不存在事务:开启新的事务
- 外部存在事务:挂起外部事务,创建新的事务
- 用法:
@Transactional(propagation = Propagation._REQUIRES_NEW_)
- 主要用于日志记录的方法之中
NOT_SUPPORTED
- 外部不存在事务:不开启新的事务
- 外部存在事务:挂起外部事务
- 用法:
@Transactional(propagation = Propagation._NOT_SUPPORTED_)
- 不常用
NEVER
- 外部不存在事务:不开启事务
- 外部存在事务:抛出异常
- 用法:
@Transactional(propagation = Propagation._NEVER_)
- 不常用
MANDATORY
- 外部不存在事务:抛出异常
- 外部存在事务:融合到外部事务中
- 用法:
@Transactional(propagation = Propagation._MANDATORY_)
- 不常用
4.5 只读属性
针对于只进行查询操作的业务方法,可以加入只读属性,提高运行效率,默认值为 false
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
4.6 超时属性
- 概念:制定了事务等待的最长时间
- 为什么事务要进行等待?
- 因为当前事务访问数据的时候,有可能访问的数据被别的事务进行加锁操作,所以本事务就必须进行等待
- 默认值是 -1,最终是由对应的数据库来进行指定;一般情况之下用默认值就行
@Transactional(timeout = 2)
4.7 异常属性
- Spring的事务过程中,默认对于
RuntimeException 及其子类
,采用的是回滚策略;@Transactional(noRollbackFor = {java.lang.RuntimeException.class}
- Spring的事务过程中,默认对于
Exception及其子类
,采用的提交策略@Transactional(rollbackFor = {java.lang.Exception.class})
4.8 事务属性常见配置总结
- 隔离属性: 默认值
- 传播属性: Required(默认值)增删改、Supports 查询操作
- 只读属性: readOnly=false 增删改,true 查询操作
- 超时属性: 默认值 -1
- 异常属性: 默认值
1. 增删改操作:@Transactional
2. 查询操作:@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
05 基于标签的事务配置方式
5.1 基本使用
一般情况之下,切入点写成包切入点
<!--事务属性-->
<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
<tx:attributes>
<tx:method name="register" isolation="READ_COMMITTED" propagation="MANDATORY"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pc" expression="execution(* com.spring3.service.UserService.register(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
</aop:config>
5.2 实战应用方式
- 方式一:所有以Service结尾的方法名
<tx:attributes>
<tx:method name="*Service"/>
</tx:attributes>
- 方法二:除了以Service方法结尾的其余方法
<tx:attributes>
<tx:method name="*Service"/>
<tx:method name="*"/>
</tx:attributes>