事务的传播属性、隔离级别等以及在Spring中的设置

1. 事务的传播属性

# 事务传播属性

 事务传播:就是在多个业务层之间相互调用时传递事务的过程称之为事务传播
 
 		 将事务对象在业务层之间进行传递的过程
 		 
 		 (本来不同的业务层是不同的事务对象,那么我们在一个业务层就不能调用其他业务层的方法了,但是spring框架提供了
 		 事务传播属性,可以在一个事务层调用其他事务层的方法时将事务对象也传递过去,这样即使调用了别的事务层,但是由于
 		 事务对象是一样的,那么这就成为了一个原子操作,这样这两个(多个)事务要么一起成功,要么有一个失败就全部回滚),
 		 这就称之为事务的传播属性。
 		 
 		 本来不同的业务层时不能相互调用的,但是sprong框架引入了事务的传播属性,事物的传播属性可以使不同的业务层之间
 		 相互调用,进而达到了不同的业务层可以相互调用的结果。
 		 
- propagation:事务传播属性
         REQUIRED:需要事务             如果外层没有事务    则开启新的事务    如果外层存在事务,则融入当前事务
         SUPPORTS:支持事务             如果外层没有事务    不会开启新的事务  如果外层存在事务,则融入当前事务
         REQUIRES_NEW:每次开启新的事务  如果外层存在事务,外层事务挂起,自己开启新的事务执行,执行完成,恢复外部事务继续执行
         NOT_SUPPORTED:不支持事务      如果外层存在事务,外层事务挂起,自己以非事务方式执行,执行完成,恢复外部事务执行
         
         NEVER:不能有事务         	  存在事务报错
         MANDATORY:强制事务            没有事务报错
         NESTED:嵌套事务               事务之间可以嵌套运行 数据库 oracle mysql 不支持 		 
         
        上面四个属性用的比较多,下面三个属性用的比较少。
         
         
         
- 一般在给只需要给查询方法的propagation属性设置为SUPPORTS即可,增、删、改方法一般都不用额外加

<!--增、删、改一般设置为REQUIRED(默认,所以可以不写propagation这个属性),查询一般设置为SUPPORTS-->
<tx:method name="save*"/>
<tx:method name="update*"/>
<tx:method name="delete*"/>
<tx:method name="find*" propagation="SUPPORTS"/>
 

在这里插入图片描述

外层方法把事务对象传给内层业务调用的过程(其实本质传的是connection连接对象),称之为事务的传播

propagation:事务传播属性

  • REQUIRED
    • REQUIRED:需要事务 如果外层没有事务 则开启新的事务 如果外层存在事务,则融入当前事务
    • spring中事务属性的默认值
    • 要么一起成功,要么只要有一个失败就一起回滚
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>

在这里插入图片描述

  • SUPPORTS
    • SUPPORTS:支持事务 如果外层没有事务 不会开启新的事务 如果外层存在事务,则融入当前事务
    • 一般不用于增删改,因为没有谁主动开启事务,一直没有事务管理,那数据不就乱套了嘛
    • 一般用于查询操作(查询操作一般没有事务配置,事务属性一般设置为SUPPORTS),给find方法配一个SUPPORTS,不是说要给find方法加事务,而是让它去融入事务支持事务传播
<tx:method name="save*" propagation="SUPPORTS"/>
<tx:method name="update*" propagation="SUPPORTS"/>

在这里插入图片描述

注意:SUPPORTS一般用于查询操作(查询操作一般没有事务配置,事务属性一般设置为SUPPORTS),给find方法配一个SUPPORTS,不是说要给find方法加事务,而是让它去融入事务支持事务传播

<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*"/>
<tx:method name="find*" propagation="SUPPORTS"/>

在这里插入图片描述

  • REQUIRES_NEW
    • 每次开启新的事务 如果外层存在事务,外层事务挂起,自己开启新的事务执行,执行完成,恢复外部事务继续执行
    • 不关心外层有没有事务,有也好,没有也好,我每次都会开启一个新的。你没有,我也不管你,我就开启一个新的,以事务的方式(开启的新事务)运行,运行完之后外部正常走;如果外部有事务,我也会自己开一个新的,我先走,这个新的走完之后恢复外部的事务,相当于外部有事务的话,外部的事务必须等待我自己的事务走完之后你外部的事务才能走。
    • 这种方式和外部的事务完全是两个连接对象,内部事务的出错不会影响外部事务,外部事务的出错也不会影响内部事务,以独立的事务方式运行。但是内部事务运行的时候外部事务得堵塞,我执行完了才放行你。
    • 在做银行日志系统时,去给数据加日志的时候会用到这个。在去银行取钱时,无论出错了也好,它都会有一笔交易记录,这个日志事务的运行就要独立于额外的业务系统,取钱钱不够了或者转账失败了会发现转账这个功能回滚了,但是会留了一个转账记录。针对于日志这个功能,一般都是以自己的事务方式运行
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRES_NEW"/>

在这里插入图片描述

  • NOT_SUPPORTED

    • NOT_SUPPORTED:不支持事务 如果外层存在事务,外层事务挂起自己以非事务方式执行,执行完成,恢复外部事务执行。
    <tx:method name="save*" propagation="NEVER"/>
    <tx:method name="update*" propagation="NOT_SUPPORTED"/>
    

在这里插入图片描述

  • NEVER
    • NEVER:不能有事务 存在事务报错
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="NEVER"/>

在这里插入图片描述

  • MANDATORY
    • 强制事务 没有事务报错
<tx:method name="save*" propagation="MANDATORY"/>
<tx:method name="update*" propagation="NEVER"/>

在这里插入图片描述

  • NESTED
    • NESTED:嵌套事务 事务之间可以嵌套运行 数据库 oracle mysql 不支持
    • 到现在为止关系型数据库支持对这个都不太友好

实际操作

在这里插入图片描述

需要用到的类及结构:

在这里插入图片描述

AService接口

public interface AService {

    void save();
    String find();
}

BService接口

public interface BService {

    void update();
    String find();
}

BService实现类

public class BServiceImpl implements BService{
    @Override
    public void update() {
        System.out.println("BService Update");
    }

    @Override
    public String find() {
        System.out.println("BService Find");
        return "BService find";
    }
}

AService实现类

public class AServiceImpl implements AService{

    private BService bbbService;

    public void setBbbService(BService bbbService) {
        bbbService.update();
        this.bbbService = bbbService;
    }

    @Override
    public void save() {
        System.out.println("AService Save");
    }

    @Override
    public String find() {
        System.out.println("AService Find");
        return "AService find";
    }
}

spring.xml配置文件

<?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: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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">


    <!--创建数据源-->
    <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/lb?characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    <!--创建sqlSessionFactoy-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
        <!--注入datasource mapperLocations typeAliasesPackage-->
        <property name="dataSource" ref="dataSource"/>
        <property name="mapperLocations" value="classpath:com/baizhi/mapper/*.xml"/>
        <property name="typeAliasesPackage" value="com.baizhi.eneity"/>
    </bean>
    <!--创建DAO-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--注入sqlSessionFactory dao接口所在包-->
        <!--value要的是sqlSessionFactory在工厂中的名字-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <property name="basePackage" value="com.baizhi.dao"/>
    </bean>
    <!--创建事务管理器-->
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--创建事务环绕通知并进行事务细粒度控制-->
                            <!--transaction-manager找ref -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--
                propagation:事务传播属性
                    REQUIRED:需要事务          如果外层没有事务    则开启新的事务    如果外层存在事务,则融入当前事务
                    SUPPORTS:支持事务          如果外层没有事务    不会开启新的事务  如果外层存在事务,则融入当前事务
                REQUIRES_NEW:每次开启新的事务   如果外层存在事务,外层事务挂起,自己开启新的事务执行,执行完成,恢复外部事务继续执行
               NOT_SUPPORTED:不支持事务         如果外层存在事务,外层事务挂起,自己以非事务方式执行,执行完成,恢复外部事务执行
                       NEVER:不能有事务         存在事务报错
                   MANDATORY:强制事务           没有事务报错
                      NESTED:嵌套事务           事务之间可以嵌套运行 数据库 oracle mysql 不支持
            -->
			<!--下面这两行propagation的值不确定,会根据实际的测试而修改-->
            <tx:method name="save*" propagation="NESTED"/>
            <tx:method name="update*" propagation="NEVER"/>
            <tx:method name="delete*"/>
            <tx:method name="find*" propagation="SUPPORTS"/>
        </tx:attributes>
    </tx:advice>
    <!--配置事务切面-->
    <aop:config>
        <aop:pointcut id="pc" expression="execution(* com.baizhi.service.*ServiceImpl.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
    </aop:config>
    <!--管理Service层组件-->
    <bean class="com.baizhi.service.UserServiceImpl" id="userService">
        <property name="userDAO" ref="userDAO"/>
    </bean>

    <bean class="com.baizhi.service.BServiceImpl" id="bService"></bean>

    <bean class="com.baizhi.service.AServiceImpl" id="aService">
        <property name="bbbService" ref="bService"/>
    </bean>

</beans>

在这里插入图片描述

测试类

public class TestAservice {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

        AService aService = (AService) context.getBean("aService");

        aService.save();
    }
}

在这里插入图片描述

REQUIRED:需要事务             如果外层没有事务    则开启新的事务    如果外层存在事务,则融入当前事务
SUPPORTS:支持事务             如果外层没有事务    不会开启新的事务  如果外层存在事务,则融入当前事务
REQUIRES_NEW:每次开启新的事务  如果外层存在事务,外层事务挂起,自己开启新的事务执行,执行完成,恢复外部事务继续执行
NOT_SUPPORTED:不支持事务      如果外层存在事务,外层事务挂起,自己以非事务方式执行,执行完成,恢复外部事务执行
NEVER:不能有事务         	  存在事务报错
MANDATORY:强制事务            没有事务报错
NESTED:嵌套事务               事务之间可以嵌套运行 数据库 oracle mysql 不支持 		
  • REQUIRED:需要事务
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>

在这里插入图片描述

  • SUPPORTS:支持事务
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="SUPPORTS"/>

在这里插入图片描述

<!--不给save方法配置事务,也就是此时save方法不会开启事务,外部无事务,update方法也不会开启新事务,update以非事务的方式运行-->
<!-- <tx:method name="save*" propagation="REQUIRED"/>-->
<tx:method name="update*" propagation="SUPPORTS"/>

在这里插入图片描述

  • REQUIRES_NEW:每次开启新的事务
    • 不管外部有没有事务,我自己都创建一个新的事务
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRES_NEW"/>

在这里插入图片描述

  • NOT_SUPPORTED:不支持事务
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="NOT_SUPPORTED"/>

在这里插入图片描述

  • NEVER:不能有事务 存在事务报错
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="NEVER"/>

在这里插入图片描述

  • MANDATORY:强制事务 没有事务报错
<!--save的事务去掉,对于update方法来说外部无事务-->
<!--<tx:method name="save*" propagation="REQUIRED"/>-->
<tx:method name="update*" propagation="MANDATORY"/>

在这里插入图片描述

  • NESTED:嵌套事务 事务之间可以嵌套运行 数据库 oracle mysql 不支持
    • 这个没办法演示,因为现在的数据库也不支持

2. 事务的其他属性

关于事务的隔离级别以及脏读、不可重复读、幻读是什么,建议大家参考一下我以前写的博客,里面有更清晰的详解:

MySQL中事务相关问题的分析

# 事务的隔离级别

 isolation   :事务隔离级别
 DEFAULT:           使用数据库默认的隔离级别   [推荐]
 READ_UNCOMMITTED:  读未提交    一个客户端读到了另一个客户端没有提交的数据  脏读现象
 READ_COMMITTED  :  读提交      一个客户端只能读到另一个客户端提交的数据    避免脏读现象  [oracle默认的隔离级别]
 REPEATABLE_READ :  可重复读     主要是用来避免不可重复读实现的    行锁    [mysql默认的隔离级别]
 SERIALIZABLE    :  序列化读     主要是用来避免幻影读现象的出现    表锁

注意:隔离级别越高,查询效率越低   一般推荐使用数据库默认隔离级别

# tx:method标签的isolation属性
在spring中可以给tx:method标签的isolation属性赋值设置隔离级别

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="save*" isolation="DEFAULT"/>
            <tx:method name="update*"/>
            <tx:method name="delete*"/>
            <tx:method name="find*" propagation="SUPPORTS"/>
        </tx:attributes>
    </tx:advice>
    
 
# tx:method标签的read-only属性

read-only:事务读写性   true  只读  不能执行增删改操作  false: 可读可写(mysql支持,oracle不支持)
 read-only这里一般都不加

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="save*" isolation="DEFAULT" read-only="true"/>
            <tx:method name="update*"/>
            <tx:method name="delete*"/>
            <tx:method name="find*" propagation="SUPPORTS"/>
        </tx:attributes>
    </tx:advice>
    
# tx:method标签的rollback-for属性

rollback-for:出现什么类型异常回滚   默认出现RuntimeException(运行时异常)及其子类异常回滚

一般不设置

<tx:method name="save*" rollback-for=""/>

# tx:method标签的no-rollback-for属性

no-rollback-for:出现什么类型异常不回滚

一般也不会做设置

<tx:method name="save*" no-rollback-for="">

# tx:method标签的timeout属性

timeout:事务超时性  -1 永不超时(默认)
设置>=0正整数  代表设置超时时间  单位秒  假如设置1秒,1秒还没执行完,直接报错
一般我们也不会修改timeout这个属性


在演示事务传播时经常会出现外部事务挂起,内部事务执行,那外部事务挂起,它挂起多长时间,如果内部事务阻塞,外部事务得一直挂起
如果没有做任何配置,内部事务没有执行完,外部事务就老老实实的等待,永不超时

<tx:method name="save*" timeout="-1"/>

测试类:

@Test
public void testSave(){
    UserService userService = (UserService) context.getBean("userService");
    User user = new User();
    user.setBir(new Date());
    user.setName("小陈");
    user.setAge(23);
    userService.save(user);
}
  • 演示read-only
<tx:method name="save*" read-only="true"/>

在这里插入图片描述

  • 演示no-rollback-for
<!--设置出现运行时异常不回滚-->
<tx:method name="save*" no-rollback-for="java.lang.RuntimeException"/>

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值