Spring对事物的管理

本文详细介绍了Spring中的事务管理,包括编程式事务和声明式事务。声明式事务基于AOP实现,分为基于XML配置和注解两种方式。事务的传播行为是Spring特有的功能,定义了如PROPAGATION_REQUIRED、PROPAGATION_SUPPORTS等不同的处理策略,用于控制方法间事务的交互。通过实例展示了事务如何在不同场景下生效,并分析了事务失效的常见原因。
摘要由CSDN通过智能技术生成

事物是什么

事物可以看作是对一次对数据库的若干操作组成的一个单元。

我们在企业开发级的项目时,面对业务人员的一个操作,但实际上要对数据库读写的多步操作的集合。由于数据操作在顺序执行的过程中,任何的一步操作都是有可能发生异常的。异常就会导致后面的步骤午饭完成。但此时业务逻辑没有正确的完成(走通一整个流程)。那之前的操作数据结果可能就根本不可靠,那就在这种情况下,应该进行回退。

而事务的作用就是为了保证用户的每一个操作都是可靠的,事务中的每一步操作都是必须成功顺利执行的。只要出现了异常了就回退到事务的开始未进行操作的状态。这些操作要么都成功完成,要么都不执行取消,从而保证数据都满足一致性的要求。

事务的分类

Spring中的事务管理分为两种形式:

  1. 编程式事务
  2. 声明式事务

编程式事务

在项目中很少的使用,这种方式需要注入一个事务管理对象TransactionTemplate,然后在我们代码中需要提交事务或回滚事务时需要自己书写代码实现。
就是在我们的代码中需要像Mybatis那样手动的(显示的)提交回滚事务。

声明式事务

这个是建立在AOP基础上的,本质是对方法前后的一个拦截,所以声明式事务是方法级别的。

Spring声明式事务管理方式有两种:

  1. 基于xml的配置
  2. 基于注解实现

代码演示:
这个是Spring的总控制

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--注意约束的添加-->


    <!--开启Spring的注解扫描解析它-->
    <!--写package一定要注意好自己的包名,在高级部分之下的都是可以扫描到的-->
    <context:component-scan base-package="com.demo.spring"></context:component-scan>

    <import resource="db.xml"></import>
    <!--导入db.xml的配置文件-->
</beans>
<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--注意约束的添加-->

    <!--配置与数据库连接的相关配置-->

    <!--Spring读入属性文件-->
    <context:property-placeholder location="config.properties"></context:property-placeholder>

    <!--配置Druid的数据库连接对象-->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driverClassName}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${ume}"></property>
        <property name="password" value="${pwd}"></property>
        <property name="initialSize" value="${initialSize}"></property>
        <!--initialSize初始化几个-->
        <property name="maxActive" value="${maxActive}"></property>
        <!--maxActive最大创建几个连接池-->
    </bean>

    <!--Spring提供的JdbcTemplate封装类-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="druidDataSource"></property>
        <!--将德鲁伊Druid注入到Spring提供的JdbcTemplate中去-->
    </bean>
</beans>

如果不加事务的话,在遇到这种情况下:

package com.demo.spring.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository(value = "userDao")
public class UserDao {

    @Autowired
    JdbcTemplate jdbcTemplate;
    public void save() {
        jdbcTemplate.update("insert into admin(account,password,sex) values (?,?,?)","jim","111","男");
        int a = 10/0;
        jdbcTemplate.update("insert into admin(account,password,sex) values (?,?,?)","tom","111","女");
    }
}

运行测试代码:

package com.demo.spring.test;

import com.demo.spring.dao.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

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

        UserDao userDao = applicationContext.getBean("userDao", UserDao.class);
        userDao.save();
    }
}

数据库还是会运行第一个的向数据库发送的请求,它的最小单元是 jdbcTemplate
在这里插入图片描述
我们现在就想要UserDao这个类中的save方法作为一个整体(以整体为单位),要么都执行,要么就都不要执行,在这也就出现了事务。

基于xml的配置声明式事务

使用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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--注意约束的添加-->

    <!--配置与数据库连接的相关配置-->

    <!--Spring读入属性文件-->
    <context:property-placeholder location="config.properties"></context:property-placeholder>

    <!--配置Druid的数据库连接对象-->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driverClassName}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${ume}"></property>
        <property name="password" value="${pwd}"></property>
        <property name="initialSize" value="${initialSize}"></property>
        <!--initialSize初始化几个-->
        <property name="maxActive" value="${maxActive}"></property>
        <!--maxActive最大创建几个连接池-->
    </bean>

    <!--Spring提供的JdbcTemplate封装类-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="druidDataSource"></property>
        <!--将德鲁伊Druid注入到Spring提供的JdbcTemplate中去-->
    </bean>




    <!--声明式事务:
    Spring配置事务管理-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--与数据库的连接-->
        <property name="dataSource" ref="druidDataSource"></property>
    </bean>

    <!--配置Spring的传播行为,这是Spring框架特有的一个功能-->
    <tx:advice id="txadvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="save" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!--这里采用Spring的AOP的思想来给方法加上事务管理-->
    <aop:config>
        <aop:pointcut id="save" expression="execution(* com.demo.spring.dao.UserDao.save(..))"/>
        <aop:advisor advice-ref="txadvice" pointcut-ref="save"></aop:advisor>
    </aop:config>

</beans>

在xml的管理下,那么再运行代码就会,遇到异常的时候,不会让数据库执行全部的代码。
在这里插入图片描述

不会再在数据库中录入

删除掉手动写入的异常
在这里插入图片描述
再次执行,结果如下:
在这里插入图片描述

以注解的方式实现

开启注解事务方式:

<!--开启注解方式管理事务管理-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

在配置文件中写到

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--注意约束的添加-->

    <!--配置与数据库连接的相关配置-->

    <!--Spring读入属性文件-->
    <context:property-placeholder location="config.properties"></context:property-placeholder>

    <!--配置Druid的数据库连接对象-->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driverClassName}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${ume}"></property>
        <property name="password" value="${pwd}"></property>
        <property name="initialSize" value="${initialSize}"></property>
        <!--initialSize初始化几个-->
        <property name="maxActive" value="${maxActive}"></property>
        <!--maxActive最大创建几个连接池-->
    </bean>

    <!--Spring提供的JdbcTemplate封装类-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="druidDataSource"></property>
        <!--将德鲁伊Druid注入到Spring提供的JdbcTemplate中去-->
    </bean>


    <!--声明式事务:
    Spring配置事务管理-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--与数据库的连接-->
        <property name="dataSource" ref="druidDataSource"></property>
    </bean>

	<!--开启注解方式管理事务管理-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>

@Transactional注解标签,加在类上,表示这个类中的所有方法都加上事务管理,加在方法上,表会当前所加上的方法。

package com.demo.spring.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository(value = "userDao")
public class UserDao {

    @Autowired
    JdbcTemplate jdbcTemplate;

//    事务:看做一次对数据库的若干操作组成的序列,是一个整体的过程,要么都成功,要么都失败
//    jdbcTemplate默认是使用jdbc的事务提交方式,执行完是自动提交的

//    在我们一次的save时,包含了两个或者对个对数据库的操作
//    那么我们认为这若干个操作为一个单元(整体)
//    这就是事务的原子性(是不可分割的),多个操作要么都执行成功, 要么都不成功
    @Transactional
    public void save() {
        jdbcTemplate.update("insert into admin(account,password,sex) values (?,?,?)","jim","111","男");
//        int a = 10/0;
        jdbcTemplate.update("insert into admin(account,password,sex) values (?,?,?)","tom","111","女");
    }
}

在这里插入图片描述
以注解标签的形式执行结果为:
在这里插入图片描述


Spring的事务传播行为

在我们实际的开发的时候,用户虽然一个操作,但实际上在底层有多个方法分别实现执行对数据库的操作,那么我们就要对多个方法进行事务管理。

用户的一个操作

package com.demo.spring.service;

import com.demo.spring.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service(value = "userService")
public class UserService {

    @Autowired
    private UserDao userDao;

    //在这里通过在Spring的注入将UserDao赋给UserService中的userDao属性
    // 然后在UserService中的直接使用
    public void saveUser() {
        System.out.println("这是UserService中的saveUser中的方法");
        userDao.save1();
        int a = 10 / 0;
        userDao.save2();
    }
}

底层要操作两个方法来实现

package com.demo.spring.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository(value = "userDao")
public class UserDao {

    @Autowired
    JdbcTemplate jdbcTemplate;

//    事务:看做一次对数据库的若干操作组成的序列,是一个整体的过程,要么都成功,要么都失败
//    jdbcTemplate默认是使用jdbc的事务提交方式,执行完是自动提交的

//    在我们一次的save时,包含了两个或者对个对数据库的操作
//    那么我们认为这若干个操作为一个单元(整体)
//    这就是事务的原子性(是不可分割的),多个操作要么都执行成功, 要么都不成功
    @Transactional
    public void save1() {
        jdbcTemplate.update("insert into admin(account,password,sex) values (?,?,?)","jim","111","男");
    }

	@Transactional
    public void save2(){
        jdbcTemplate.update("insert into admin(account,password,sex) values (?,?,?)","tom","111","女");

    }
}

运行的测试代码:

package com.demo.spring.test;

import com.demo.spring.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

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

        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.saveUser();
    }
}

结果:
在这里插入图片描述

如果在像在这里UserService 中就有出错了,那么在这里对事务的管理就失效了。基于这样的问题,我们只要将@Transactional标签加在UserService他也就不能进入了。加在类上,表示所有的类都是在事务管理当中。在某个人方法上,就表示此方法是在事务管理中进行。

package com.demo.spring.service;

import com.demo.spring.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service(value = "userService")
public class UserService {

    @Autowired
    private UserDao userDao;

    //在这里通过在Spring的注入将UserDao赋给UserService中的userDao属性
    // 然后在UserService中的直接使用
    
    @Transactional
    public void saveUser() {
        System.out.println("这是UserService中的saveUser中的方法");
        userDao.save1();
        int a = 10/0;
        userDao.save2();
    }
}

然后测试就没问题了。
在这里插入图片描述
但是我们还是要考虑底层两个方法之间的事务关系。

什么叫做事务的传播行为?

既然叫做传播,那么至少就要有两个东西,才可以发生传播。单体是不存在传播这个行为。事务传播行为(propagation behavior)指的就是当一个事务的方法被另一个事务的方法调用,那这个事务应该怎样如何进行。事务传播行为是Spring框架独有的事务增强特性,它不输入事务实际提供方法的数据库行为。

例如:methodA事务方法调用了methodB事务方法时,methodB是继续在调用这个methodA的事务运行还是自己在开启一个事务管理进行呢,这就由metodB的事务传播行为来决定的。

配置文件:
config.properties属性文件:

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/demo?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
ume=root
pwd=wasd
initialSize=5
maxActive=15

配置数据库连接和事务管理方面:

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--注意约束的添加-->


    <!--配置与数据库连接的相关配置-->

    <!--Spring读入属性文件-->
    <context:property-placeholder location="config.properties"></context:property-placeholder>

    <!--配置Druid的数据库连接对象-->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driverClassName}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${ume}"></property>
        <property name="password" value="${pwd}"></property>
        <property name="initialSize" value="${initialSize}"></property>
        <!--initialSize初始化几个-->
        <property name="maxActive" value="${maxActive}"></property>
        <!--maxActive最大创建几个连接池-->
    </bean>

    <!--Spring提供的JdbcTemplate封装类-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="druidDataSource"></property>
        <!--将德鲁伊Druid注入到Spring提供的JdbcTemplate中去-->
    </bean>


    <!--开启事务管理-->
    <!--声明式事务:Spring配置事务管理-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--与数据库的连接-->
        <property name="dataSource" ref="druidDataSource"></property>
    </bean>

    <!--开启注解方式管理事务管理-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
    
</beans>

Spring总的配置文件:

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--注意约束的添加-->


    <!--开启Spring的注解扫描解析它-->
    <!--写package一定要注意好自己的包名,在高级部分之下的都是可以扫描到的-->
    <context:component-scan base-package="com.demo.spring"></context:component-scan>

    <import resource="db.xml"></import>
    <!--导入db.xml的配置文件-->
</beans>

代码演示:

dao层:

package com.demo.spring.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class DeptDao {

    @Autowired
    JdbcTemplate jdbcTemplate;

    public void save(){
        jdbcTemplate.update("insert into dept (name,adminId) values (?,?)","研发部",1);
    }
}
package com.demo.spring.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class CommonDao {

    @Autowired
    JdbcTemplate jdbcTemplate;

    public void save(){
        jdbcTemplate.update("insert into test(id) values (?)",2);
    }
}

service层:

package com.demo.spring.service;

import com.demo.spring.dao.CommonDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service(value = "commonService")
public class CommonService {

    @Autowired
    CommonDao commonDao;

    @Transactional
    public void saveCommon(){
        commonDao.save();
    }
}

在这个方法中A反方的事务调用了B。B也是事务,那么B这个如何进行。

package com.demo.spring.service;

import com.demo.spring.dao.DeptDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


@Service(value = "deptService")
public class DeptService {
    
    @Autowired
    DeptDao deptDao;

    @Autowired
    CommonService commonService;

    @Transactional//添加A为事务管理
    public void saveDept(){
        deptDao.save();
        commonService.saveCommon();//调用B
        //那么这里就产生了相对的矛盾,A,B都是有事务的,A调用了B方法。
        // 那么A在进行事务的处理时,B应该如何执行,这里就是事务的执行
    }
}

事务传播方式的加入书写方式:
@Transactional(propagation=Propagation.REQUIRED)

在Spring中定义了七种播行为。

事务传播行为说明
PROPAGATION_REQUIRED如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与 PROPAGATION_REQUIRED 类似的操作。

PROPAGATION_REQUIRED

如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。
从前到后,有且仅有一个事务的创建。A和B只有一个事务,可以理解为都在一个容器中,要活一起活,要死一起死的这种。前提是都写了事务传播的标签(因为PROPAGATION_REQUIRED也是一种默认的事务处理方式)

指定的方法必须是在事务内执行的。若当前存在事务,加入到当前的事务之中。若当前没有事务,就创建一个事务,在事务中执行。这种情况下的创博行为是最常见的,也是Spring的默认传播 行为。

代码演示:

在B的事务管理中,出现(int a = 10/0)错误,那么理论就是对数据库没有任何数据的保存

package com.demo.spring.service;

import com.demo.spring.dao.CommonDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class CommonService {

    @Autowired
    CommonDao commonDao;

    //A和B的传播行为都是REQUIRED,那么B就直接加入到A的传播行为中
    // 如果A没有传播行为,那么B有,那么就将B单独创建一个事务的进行
    //如果B里面出错
    @Transactional(propagation = Propagation.REQUIRED)
    public void saveCommon(){
        commonDao.save();
        int a = 10/0;
    }
}
package com.demo.spring.service;

import com.demo.spring.dao.DeptDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


@Service(value = "deptService")
public class DeptService {

    @Autowired
    DeptDao deptDao;

    @Autowired
    CommonService commonService;
    
    @Transactional(propagation = Propagation.REQUIRED)//添加A为事务管理
    public void saveDept(){
        deptDao.save();
        commonService.saveCommon();//调用B
        //那么这里就产生了相对的矛盾,A,B都是有事务的,A调用了B方法。
        // 那么A在进行事务的处理时,B应该如何执行,这里就是事务的执行
    }
}

测试运行结果的代码:

package com.demo.spring.test;

import com.demo.spring.service.DeptService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

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

        DeptService deptService = applicationContext.getBean("deptService", DeptService.class);
        
        deptService.saveDept();
    }
}

结果也是不在数据库中做任何的输入,出了错误,虽然没有在同一个方法里面,但是有(REQUIRED)事务的管理,所以他们可以看作是在一个方法当中,要错一起错,执行结果都对,才能对数据库中保存进去数据。

如果A根本就没有开启事务处理,那么A中的数据会存入其中,而B依然不能存入。若开启了事务,那么它有一个出了错就不能存入数据库。

PROPAGATION_SUPPORTS

支持当前事务,如果当前没有事务,就以非事务方式执行。
就是A有事务,那么B也就有事务,且事务传播和B一样(就是在一个容器里面)。若A没有的话,那么B也是没有的。

A存在事务:

package com.demo.spring.service;

import com.demo.spring.dao.DeptDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;


@Service(value = "deptService")
public class DeptService {

    @Autowired
    DeptDao deptDao;

    @Autowired
    CommonService commonService;

    @Transactional(propagation = Propagation.REQUIRED)//添加A为事务管理
    public void saveDept(){
        deptDao.save();
        commonService.saveCommon();//调用B
        //那么这里就产生了相对的矛盾,A,B都是有事务的,A调用了B方法。
        // 那么A在进行事务的处理时,B应该如何执行,这里就是事务的执行
    }
}

B的事务是SUPPORTS,且B中由error

package com.demo.spring.service;

import com.demo.spring.dao.CommonDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service(value = "commonService")
public class CommonService {

    @Autowired
    CommonDao commonDao;

    //A的传播方式是REQUIRED,B的传播方式是SUPPORTS,那么B就加入到A中去执行
    //就是A是什么,B就是什么
    @Transactional(propagation = Propagation.SUPPORTS)
    public void saveCommon(){
        commonDao.save();
        int a = 10/0;
    }
}

执行结果就是两个都对数据库的输入数据都不会成功的向数据库中打入了数据。

如果A没有事务的话:

package com.demo.spring.service;

import com.demo.spring.dao.DeptDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;


@Service(value = "deptService")
public class DeptService {

    @Autowired
    DeptDao deptDao;

    @Autowired
    CommonService commonService;

    public void saveDept(){
        deptDao.save();
        commonService.saveCommon();//调用B
        //那么这里就产生了相对的矛盾,A,B都是有事务的,A调用了B方法。
        // 那么A在进行事务的处理时,B应该如何执行,这里就是事务的执行
    }
}

B还是采用的是SUPPORTS。
那么这两个都是没有事务处理的,按照正常的执行数据一步一步执行。执行到错误的时候,机会停下来。

结果就是尽管有错,但是A和B都对数据库的代码都执行保存了。(因为B中的错误10/0在代码执行之后)。

PROPAGATION_REQUIRES_NEW

新建事务,如果当前存在事务,把当前事务挂起。

就是A调用到事务B时,无论A有没有事务B都是就要创建一个事务的。
A没有事务的话,那么B就是单独一个小的事务。
如果A有事务的话,那么B再创建一个事务,执行时,将A事务暂停,执行B事务。就是A和B的都有事务,但是这两个事务是不相干的。互不影响。

A的事务处理方式还是默认的事务处理,但是A中会有一个error

package com.demo.spring.service;

import com.demo.spring.dao.DeptDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;


@Service(value = "deptService")
public class DeptService {

    @Autowired
    DeptDao deptDao;

    @Autowired
    CommonService commonService;

    @Transactional(propagation = Propagation.REQUIRED)//添加A为事务管理
    public void saveDept(){
        deptDao.save();
        commonService.saveCommon();//调用B
        int a = 10/0;
        //代码是从上到下依次进行的 int a = 10/0 这个是问题代码
        // 但是在这之前调用了B所以,B在执行的时候是一个单独的事务
        //所以B应该能正常的存入代码,而A不能。
    }
}
package com.demo.spring.service;

import com.demo.spring.dao.CommonDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service(value = "commonService")
public class CommonService {

    @Autowired
    CommonDao commonDao;

    //A的传播方式是REQUIRED,B的传播方式是REQUIRES_NEW
    // ,意为新建了一个事务的处理,是一个单独的事务处理,
    // B不会受到A的影响
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveCommon(){
        commonDao.save();
    }
}

B的事务处理中是创建了一个新的事务处理。只要调用的了B,它就会执行,且B的执行时不会受到A的影响。

运行结果:
dept表中没有存入数据
在这里插入图片描述test表中就可以存入数据:
在这里插入图片描述

事务生效的场景

  1. 同一个类中方法调用,就会导致@Transactional 失效
  2. @Transactional 应用在非 public 修饰的方法上,就是只能用在public(公共的)的修饰上
  3. @Transactional 注解属性 propagation 设置错误(不是自己所需求的合理的属性)
  4. 异常被 catch 捕获导致@Transactional 失效(应为被catch捕获,所以可以理解为catch 会解决掉这个问题,使得代码表面上是没有问题的)
  5. 数据库引擎不支持事务(在Mysql中也是只有InnoDB引擎是支持事务的)

下一篇:>>> Spring集成Mybatis
上一篇:>>> Spring中AOP的概述、搭建和实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值