【Spring】关于Spring事务管理的一些杂记

【Spring】 Spring中的事务处理

1、Spring的事务管理

  • 事务原本是数据库中的概念,在实际项目的开发中,进行事务的处理一般是在业务逻辑层,即Service层。这样做是为了能够使用事务的特性来管理关联操作的业务。在Spring中通常可以通过以下两种方式来实现对事务的管理:
    • 1)编码式事务允许用户在代码中精确定义事务的边界
    • 2)声明式事务(基于AOP)有助于用户将操作与事物规则进行解耦

2、事务的ACID特性

  • 原子性(Atomic):一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。
  • 一致性(Consistent):事务必须是使数据库从一个一致性状态变到另一个一致状态。一致性与原子性是关系密切的。
  • 隔离性(Isolated):一个事务的执行不能被其他事务干扰。即一个事物内部的操作及使用的数据对并发的其他事物是隔离的,并发执行的各个事务之间不能互相干扰。
  • 持久性(Durable):持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

3、Spring中事务的隔离级别

  • 未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他回话中未提交事务修改的数据。

  • 提交读(Read Committed):智能读取到已经提交的数据。Oracle等多数数据库默认都是该级别(不重复度)。

  • 可重复读(Repeated Read):在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该个梨界别消除了不可重复读,但还是存在幻读,但是InnoDB解决了幻读。(MySQL的默认级别

  • 串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞。

  • Spring中事务的隔离级别:

    隔离级别 含义
    ISOLATION_DEFAULT 使用后端数据库默认的隔离级别
    ISOLATION_READ_UNCOMMITTED 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
    ISOLATION_READ_COMMITTED 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
    ISOLATION_REPEATABLE_READ 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
    ISOLATION_SERIALIZABLE 最高的隔离级别,完全服从 ACID 的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的

4、事务的传播特性

  • 传播行为(Propagation Behavior):事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并咋自己的事物中运行。Spring定义了七种传播行为(加粗为常用):

    传播行为 含义
    PROPAGATION_REQUIRED 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务
    PROPAGATION_SUPPORTS 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
    PROPAGATION_MANDATORY 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
    PROPAGATION_REQUIRED_NEW 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用 JTATransactionManager 的话,则需要访问 TransactionManager
    PROPAGATION_NOT_SUPPORTED 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用 JTATransactionManager 的话,则需要访问 TransactionManager
    PROPAGATION_NEVER 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常
    PROPAGATION_NESTED 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与 PROPAGATION_REQUIRED 一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务

5、@Transaction (注解式声明)

在这里插入图片描述

  • 案例:

    @Transactional(propagation = Propagation.REQUIRED, // 事务的传播特性
            noRollbackForClassName = "ArithmeticException", // 指定发生什么异常不回滚,使用的是异常的名称
            noRollbackFor = ArithmeticException.class, // 指定发生什么异常不回滚,使用的是异常的类型
            rollbackForClassName = "", // 指定发生什么异常必须回滚
            rollbackFor = ArithmeticException.class, // 指定发生什么异常必须回滚
            timeout = -1, //连接超时设置,默认值是-1,表示永不超时
            readOnly = false, // 默认是false,如果是查询操作,必须设置为true
            isolation = Isolation.DEFAULT // 使用数据库自己的隔离级别
    )
    
  • 注意事项:

    • 1)只能声明在public的method。原因是spring是通过JDK代理或者CGLIB代理的,生成的代理类,只能处理public方法,注解放在类名称上面,这样你配置的这个@Transactional 对这个类中的所有public方法都起作用,@Transactional 在方法名上,只对这个方法有作用,同样必须是public的方法。

    • 2)不能被类内部方法调用。还是因为代理的原因,类内部自调用,不会经过代理类,所以@Transactional不会生效

      扫描二维码关注公众号,回复: 15375873 查看本文章
  • Spring + MyBatis的事务管理器配置(声明式事务处理)

        <!-- 添加事务管理器 -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <!-- 添加注解事务, 设置注解事务的优先级 -->
    	<tx:annotation-driven transaction-manager="transactionManager" />
    

6、声明式事务的配置(通过AspectJ实现)

<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 导入applicationContext_mapper.xml -->
    <import resource="applicationContext_mapper.xml"></import>
    <!-- 添加包扫描 -->
    <context:component-scan base-package="com.Etui.service"></context:component-scan>

    <!-- 添加事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 以下为声明式事务 -->
    <!-- 配置事务切面 -->
    <tx:advice id="myadvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*select*" read-only="true"/>
            <tx:method name="*query*" read-only="true"/>
            <tx:method name="*find*" read-only="true"/>
            <tx:method name="*get*" read-only="true"/>
            <tx:method name="*search*" read-only="true"/>

            <tx:method name="*update*" propagation="REQUIRED" />
            <tx:method name="*change*" propagation="REQUIRED" />
            <tx:method name="*modify*" propagation="REQUIRED" />

            <tx:method name="*insert*" propagation="REQUIRED" />
            <tx:method name="*add*" propagation="REQUIRED" />

            <tx:method name="*drop*" propagation="REQUIRED" />
            <tx:method name="*delete*" propagation="REQUIRED" />
        </tx:attributes>
    </tx:advice>

    <!-- 绑定切面和切入点 -->
    <aop:config>
        <aop:pointcut id="mycut" expression="execution(* com.Etui.service.impl.*.*(..))"></aop:pointcut>
        <aop:advisor advice-ref="myadvice" pointcut-ref="mycut"></aop:advisor>
    </aop:config>
</beans>

猜你喜欢

转载自blog.csdn.net/m0_47015897/article/details/124476773