Spring的事务处理【Spring学习笔记九】

150 阅读5分钟

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(Connection).rollback();
    
结论:控制事务的底层 都是Connection对象完成的。

3、Spring控制事务的开发

Spring是通过AOP的方式进行事务开发

3.1、原始对象

public class XXXUserServiceImpl{
    private xxxDAO xxxDAO
    
    set get
    
    1. 原始对象 ---》 原始⽅法 ---》核⼼功能 (业务处理+DAO调⽤)
    2. DAO作为Service的成员变量,依赖注⼊的⽅式进⾏赋值
}

3.2、额外功能

1. org.springframework.jdbc.datasource.DataSourceTransactionManager
2. 注⼊DataSource

1. MethodInterceptor
public Object invoke(MethodInvocation invocation){
    try{
        Connection.setAutoCommit(false);
        Object ret = invocation.proceed();
        Connection.commit();
    }catch(Exception e){
        Connection.rollback();
    }
    return ret;
}
2. @Aspect
    @Around

3.3、切入点

@Transactional
事务的额外功能加⼊给那些业务⽅法。

1. 类上:类中所有的⽅法都会加⼊事务
2. ⽅法上:这个⽅法会加⼊事务

3.4、组装切面

1. 切入点
2. 额外功能

<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>

4、Spring控制事务的编码

  • 搭建开发环境(jar)

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.1.14.RELEASE</version>
    </dependency>
    
  • 编码

    <bean id="userService" class="com.baizhiedu.service.UserServiceImpl">
    	<property name="userDAO" ref="userDAO"/>
    </bean>
    
    <!--DataSourceTransactionManager-->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    	<property name="dataSource" ref="dataSource"/>
    </bean>
    
    @Transactional
    public class UserServiceImpl implements UserService {
    	private UserDAO userDAO;
    
    <tx:annotation-driven transactionmanager="dataSourceTransactionManager"/>
    
  • 细节

    <tx:annotation-driven transaction-manager="dataSourceTransactionManager" proxy-target-class="true"/>
    
    进行动态代理底层实现的切换 proxy-target-class
    	默认 false JDK
    		true Cglib
    

5、Spring中的事务属性

5.1、什么是事务属性

属性: 描述物体特征的一系列值
	性别 身高 体重 ...
    
事务属性: 描述事务特征的一系列值
1. 隔离属性
2. 传播属性
3. 只读属性
4. 超时属性
5. 异常属性

5.2、如何添加事务属性

@Transactional(isloation=,propagation=,readOnly=,timeout=,rollbackFor=,noRollbackFor=,)

5.3、事务属性详解

1、隔离属性(ISOLATION)
  • 隔离属性的概念

    概念:他描述了事务解决并发问题的特征
    
    1. 什么是并发
    	多个事务(用户)在同一时间,访问操作了相同的数据
    	
    	同一时间:0.000几秒 微小前 微小后
    2. 并发会产生哪些问题
    	1. 脏读
    	2.不可重复读
    	3.幻影读
    	
    3. 并发问题如何解决
    	通过隔离属性解决,隔离属性中设置不同的值,解决并发处理过程中的问题。
    
  • 事务并发产生的问题

    • 脏读

      一个事务,读取了另一个事务中没有提交的数据。会在本事务中产生数据不一致的问题。
      
      解决方法:@Transactional(isolation=Isolation.READ_COMMITTED)
      
    • 不可重复读

      ⼀个事务中,多次读取相同的数据,但是读取结果不⼀样。会在本事务中产⽣数据不⼀致的问题
      注意:1 不是脏读 2 ⼀个事务中
      解决⽅案 @Transactional(isolation=Isolation.REPEATABLE_READ)
      本质: ⼀把⾏锁
      
    • 幻影读

      ⼀个事务中,多次对整表进⾏查询统计,但是结果不⼀样,会在本事务中产⽣数据不⼀致的问题
      解决⽅案 @Transactional(isolation=Isolation.SERIALIZABLE)
      本质:表锁
      
    • 总结

      并发安全: SERIALIZABLE>REPEATABLE_READ>READ_COMMITTED
      
      运⾏效率: READ_COMMITTED>REPEATABLE_READ>SERIALIZABLE
      
  • 数据库对于隔离属性的支持

    隔离属性的值MySQLOracle
    ISOLATION_READ_COMMITTEDYESYES
    ISOLATION_REPEATABLE_READYESNO
    ISOLATION_SERIALIZABLEYESYES
    Oracle不支持REPEATABLE_READ值 如何解决不可重复读
    采用的是多版本比对的方式 解决不可重复读的问题
    
  • 默认隔离属性

    ISOLATION_DEFAULT:会调用不同数据库所设置的默认隔离属性
    
    MySQL:REPEATABLE_READ
    Oracle:READ_COMMITTED
    
  • 隔离属性在实战中的建议

    推荐使用Spring指定的ISOLATION_DEFAULT
    1. MySQL repeatable_read
    2. Oracle read_committed
    
    未来实战中,并发访问的情况 很低
    
    如果真遇到并发问题,乐观锁
    	Hibernate(JPA) Version
    	MyBatis         通过拦截器自定义开发
    
2、传播属性(PROPAGATION)
  • 传播属性的概念

    概念:他描述了事务解决嵌套问题的特征
    
    什么叫做事务的嵌套:他指的是一个大的事务中,包含了若干个小的事务
    
    问题:大事务中融入了很多小的失去,他们彼此影响,最终会导致外部大的失去,丧失了事务的原子性。
    
  • 传播属性的值及其用法

    传播属性的值外部不存在事务外部存在事务用法备注
    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)及其不常用
  • 默认的传播属性

    REQUIRED是传播属性的默认值
    
  • 推荐传播属性的使用方式

    增删改方法:直接使用默认值
    查询方法:显示指定传播属性的值为SUPPORTS
    
3、只读属性(readOnly)
针对于只进行查询的业务方法,可以加入只读属性,提高运行效率。

默认值:false
4、超时属性(timeout)
指定了事务等待的最长时间

1. 为什么事务进行等待?
	当前事务访问数据时,有可能访问的数据被别的事务进行加锁处理,那么此时事务就必须进行等待。
	
2. 等待时间 秒
3. 如何应用 @Transactional(timeout = 2)
4. 超时属性的默认值 -1
	最终由相应的数据库来指定
5、异常属性
Spring事务处理过程中

默认 对于RuntimeException及其子类 采用的是回滚的策略
默认 对于Exception及其子类 采用的是提交的策略

rollbackFor = {java.lang.Exception,xxx,xxx}
noRollbackFor = {java.lang.RuntimeException,xxx,xxx}

@Transactional(rollbackFor = {java.lang.Exception.class},noRollbackFor = {java.lang.RuntimeException.class})

建议:实战中使用RuntimeException及其子类 使用事务异常属性的默认值

6、事务属性常见配置总结

1. 隔离属性 默认值
2. 传播属性 Required(默认值) 增删改 Supports 查询操作
3. 只读属性 readOnly false 增删改 true 查询操作
4. 超时属性 默认值 -1
5. 异常属性 默认值

增删改操作 @Transactional
查询操作   @Transactional(propagation=Propagation.SUPPORTS,readOnly=true)

7、基于标签的事务配置方式(事务开发的第二种形式)

基于注解 @Transaction的事务配置回顾
<bean id="userService" class="cn.edu.njtech.service.UserServiceImpl">
	<property name="userDAO" ref="userDAO"/>
</bean>

<!--DataSourceTransactionManager-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

@Transactional(isolation=,propagation=,...)
public class UserServiceImpl implements UserService {
	private UserDAO userDAO;

<tx:annotation-driven transactionmanager="dataSourceTransactionManager"/>

基于标签的事务配置
<bean id="userService" class="cn.njtech.edu.service.UserServiceImpl">
    <property name="userDAO" ref="userDAO"/>
</bean>
<!--DataSourceTransactionManager-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"/>
</bean>

事务属性
<tx:advice id="txAdvice" transacationmanager="dataSourceTransactionManager">
    <tx:attributes>
	    <tx:method name="register" isoloation="",propagation=""></tx:method>
		<tx:method name="login" .....></tx:method>
        等效于
        @Transactional(isolation=,propagation=,)
        public void register(){
        }
    </tx:attributes>
</tx:advice>

<aop:config>
	<aop:pointcut id="pc" expression="execution(* cn.edu.njtech.service.UserServiceImpl.register(..))"></aop:pointcut>
	<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"></aop:advisor>
</aop:config>
  • 基于标签的事务配置在实战中的应用方式
<bean id="userService"
    class="com.baizhiedu.service.UserServiceImpl">
    <property name="userDAO" ref="userDAO"/>
</bean>
<!--DataSourceTransactionManager-->
<bean id="dataSourceTransactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"/>
</bean>

编程时候 service中负责进⾏增删改操作的⽅法 都以modify开头
查询操作 命名⽆所谓
<tx:advice id="txAdvice" transacationmanager="dataSourceTransactionManager">
	<tx:attributes>
		<tx:method name="register"></tx:method>
		<tx:method name="modify*"></tx:method>
		<tx:method name="*" propagation="SUPPORTS" readonly="true"></tx:method>
	</tx:attributes>
</tx:advice>

应⽤的过程中,service放置到service包中
<aop:config>
	<aop:pointcut id="pc" expression="execution(* cn.edu.njtech.service..*.*(..))"></aop:pointcut>
	<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"></aop:advisor>
</aop:config>