spring事务管理

时间:2022-07-25
本文章向大家介绍spring事务管理,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

什么是事务?

事务是一个不可分割操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。

个人理解:这个好比一颗导弹,落到一群人中间,如果不炸大家都没事,一炸大家都必死。(要么全部成功,要么全部失败)

事务的特性(ACID):

原子性(atomicity):是事务最小单位,不可再分割,对其修改要么全部执行(成功),要么全部不执行(失败)。

一致性(consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。如果中途失败则必须回滚到原来的状态。

比如:A有1000块,B有1000块,A转给B1000,如果成功,那A肯定是0,B是2000,如果失败A必须是1000,B必须也是1000。不会因为其他原因导致有其他结果。

隔离性(isolation):一个事务的执行不能被其它事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

比如:在高并发情况操作同一张表同一个字段,不同的线程之间的事务是互不干扰的。

持久性(durability):当事务正确完成后,这个结果就是永久性的了。

比如:这个人死了,那不可能再复活了(目前科学水平)。

"脏读"、"幻读"、"不可重复读"是什么?

脏读:读取到另外一个事务未提交前的数据,比如数据库的值为0,但是A提交前内空为1,然后B这里读取到了1,但是A最终执行结果失败,所以导致B获取的结果为1;

幻读:指一个线程读取到另一个线程中提交后的数据,比如A第一次查询到是5条,再次查的时候发现有8条,突然多了3条,所以这种就导致幻读情况。

不可重复读:指同一个事务间隔时间比较长,多次读取同一个数据,获取的返回结果却不一样,导致异常,这种是在读取过程中数据被修改了。

参考文章:(https://blog.csdn.net/qq_33591903/article/details/81672260)

Mysql(InnoDB)数据库事务的隔离级别:

名称

脏读

不可重复读

幻读

读取未提交内容(Read uncommitted)

读取提交内容(Read committed)

×

重读(Repeatable read)

×

×

串行化(Serializable)

×

×

×

Read uncommitted:读取未提交内容,另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据(脏读)、还有不可重读及幻读的问题;

Read committed:读取提交内容,事务读取到的是最新的数据(其他事务提交后的),可以彻底解决脏读的问题。存在的问题:在同一个事务里,前后两次相同的SELECT会读到不同的结果存在不重复读和幻读问题;

Repeatable read:重复读取内容,可能出现幻读,有两个事务,一个在查询,一个在插入,导致第一个查不到,再次查的时候居然存在了。这个问题在mysql高版本的时候已经通过优化并行解决了该问题。

Serializable:串行化数据库最高的隔离级别,就是将事务顺序的串行化一个一个排队的执行,保证顺序和避免"脏读"、"不可重复读"、"幻读",但是该级别由于开销实在太大,所以企业里面基本没有用到。

什么事务传播行为?

指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。

例如:两个带事务的方法A、B,当A调用B时,是以A的事务继续运行还是自己再创建新的事务,这个由B的事务传播行为来决定。

个人理解:比如水库上游的水会流向下游,而具体流多少或者是否过滤上游水中杂质的再分流由下流来控制。

spring的事务解决了什么问题?

spring事务通过注解方式,整合了各种数据库框架的事务,减少了大量编程式事务的代码,使代码更优雅重用性更高,并且减少了各种复杂之间的关系,使其更加高效安全。

显式事务:有明确的事务开始标记,需要手动打开或者代码实现;

隐式事务:默认就是指定事务的开始,默认事务已打开。

代码实现

编程式事务:指的是通过代码来实现事务;

项目结构

mysql表

CREATE TABLE `user`  (  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键 ',  `user_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户名称',  `age` int(11) NULL DEFAULT NULL COMMENT '年龄',  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB  CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
CREATE TABLE `user_role`  (  `id` int(11) NOT NULL AUTO_INCREMENT,  `user_id` int(11) NULL DEFAULT NULL,  `role_id` int(11) NULL DEFAULT NULL,  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB  CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

注:这里引用了jdbc的手动事务,当然也可以使用jpa或其他的事务。

/**
 * @Auther: csh
 * @Date: 2020/8/3 18:12
 * @Description:用户实体
 */
public class User implements Serializable {
    private Integer id;
    private String username;
    private Integer age;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public User(Integer id, String username, Integer age) {
        this.id = id;
        this.username = username;
        this.age = age;
    }
}
/**
 * @Auther: csh
 * @Date: 2020/8/4 15:52
 * @Description:用户dao
 */
@Repository
public class UserDao {

    private static Logger log = LogManager.getLogger(UserDao.class);

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public int save(User user){
        String sql = "Insert into user(user_name,age)values(?,?);";
        int save = jdbcTemplate.update(sql,user.getUsername(),user.getAge());
        log.info("保存结果{}",save>0?"成功":"失败");
        return save;
    }


}
/**
 * @Auther: csh
 * @Date: 2020/8/3 18:12
 * @Description:用户服务接口
 */
public interface IUserService {
    /**
     *
     * 功能描述:添加接口
     *
     * @param:
     * @return:
     * @auther: csh
     * @date: 2020/8/3 18:13
     */
    void add(User user);
    /**
     *
     * 功能描述:更新接口
     *
     * @param:
     * @return:
     * @auther: csh
     * @date: 2020/8/3 18:13
     */
    void update(User user);
    /**
     *
     * 功能描述:手动事务
     *
     * @param:
     * @return:
     * @auther: csh
     * @date: 2020/8/4 15:45
     */
    int handlerTransaction(User user);
}
/**
 * @Auther: csh
 * @Date: 2020/8/3 18:14
 * @Description:用户实现
 */
@Service
public class UserServiceImpl implements IUserService {

    private static Logger log = LogManager.getLogger(UserServiceImpl.class);

    @Autowired
    private PlatformTransactionManager txManager;

    @Autowired
    private UserDao userDao;

    @Autowired
    private TransactionUtils transactionUtils;

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void add(User user) {
        log.info("添加用户信息{}", JSONObject.toJSONString(user));
        update(user);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void update(User user) {
        log.info("更新用户信息{}", JSONObject.toJSONString(user));
    }

    @Override
    public int handlerTransaction(User user) {
        TransactionStatus begin = transactionUtils.begin();
        try {
            int save = userDao.save(user);
            int i = 1/0;
            transactionUtils.commit(begin);
            return save;
        }catch (Exception e){
            transactionUtils.rollabck(begin);
        }
        return 0;

    }
}
/**
 * @Auther: csh
 * @Date: 2020/8/4 16:17
 * @Description:事务公共
 */
@Component
public class TransactionUtils {
    //事务管理
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;

    public TransactionStatus begin(){
        TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionDefinition());
        return transaction;
    }

    public void commit(TransactionStatus transactionStatus){
        dataSourceTransactionManager.commit(transactionStatus);
    }

    public void rollabck(TransactionStatus transactionStatus){
        dataSourceTransactionManager.rollback(transactionStatus);
    }
}

applicationfile-transaction.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:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc" 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 http://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/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--配置propertis文件-->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:jdbc.properties</value>
            </list>
        </property>
    </bean>

    <!--开启注解配置-->
    <context:annotation-config/>
    <!--开启注解-->
    <tx:annotation-driven />
    <!--注解驱动-->
    <mvc:annotation-driven />
    <!--扫描service层-->
    <context:component-scan base-package="com.hong.spring.transaction" />
    <!-- 定义aspectj -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>

    <!-- JDBC 操作模板 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg>
            <ref bean="dataSource"/>
        </constructor-arg>
    </bean>


    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!-- 基础配置 -->
        <property name="url" value="${jdbc.url}"></property>
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="username" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>

        <!-- 关键配置 -->
        <!-- 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 -->
        <property name="initialSize" value="3" />
        <!-- 最小连接池数量 -->
        <property name="minIdle" value="2" />
        <!-- 最大连接池数量 -->
        <property name="maxActive" value="15" />
        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="10000" />

        <!-- 性能配置 -->
        <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
        <property name="poolPreparedStatements" value="true" />
        <property name="maxPoolPreparedStatementPerConnectionSize" value="20" />

        <!-- 其他配置 -->
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="300000" />
        <!--   建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,
                  执行validationQuery检测连接是否有效。 -->
        <property name="testWhileIdle" value="true" />
        <!-- 这里建议配置为TRUE,防止取到的连接不可用 ,申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。-->
        <property name="testOnBorrow" value="true" />
        <!-- 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 -->
        <property name="testOnReturn" value="false" />
    </bean>

    <!--事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

</beans>
package transaction;

import com.hong.spring.transaction.entity.User;
import com.hong.spring.transaction.service.IUserService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @Auther: csh
 * @Date: 2020/8/3 18:11
 * @Description:
 */

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:transaction/applicationfile-transaction.xml"})
public class TransactionTest {
    private static Logger log = LogManager.getLogger(TransactionTest.class);
    @Before
    public void init() {
        System.out.println("初始化");
    }


    @Autowired
    private IUserService userService;

    /**
     *
     * 功能描述:显示事务
     *
     * @param:
     * @return:
     * @auther: csh
     * @date: 2020/8/4 17:07
     */
    @Test
    public void handlerTransactionTest(){
        int save = userService.handlerTransaction(new User(null, "宏", 10));
        log.info("结果{}",save>0?"成功":"失败");
    }

}

结果

初始化
前置通知生效
2020-08-04 16:51:25,298 [main] INFO : {dataSource-1} inited
2020-08-04 16:51:25,338 [main] DEBUG: stmt enter cache
2020-08-04 16:51:25,339 [main] INFO : 保存结果成功
2020-08-04 16:51:25,382 [main] INFO : 结果失败
2020-08-04 16:51:25,385 [Thread-1] INFO : {dataSource-1} closing ...
2020-08-04 16:51:25,386 [Thread-1] DEBUG: stmt exit cache
2020-08-04 16:51:25,387 [Thread-1] INFO : {dataSource-1} closed

注解式事务:通过@Transactional注解来实现事务(简单很多);

属性名

说明

name

当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器。

propagation

事务的传播行为,默认值为 REQUIRED。

isolation

事务的隔离度,默认值采用 DEFAULT。

timeout

事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。

read-only

指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。

rollback-for

用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。

no-rollback- for

抛出 no-rollback-for 指定的异常类型,不回滚事务。

package com.hong.spring.transaction.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.hong.spring.transaction.dao.UserDao;
import com.hong.spring.transaction.entity.User;
import com.hong.spring.transaction.service.IUserService;
import com.hong.spring.transaction.utils.TransactionUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * @Auther: csh
 * @Date: 2020/8/3 18:14
 * @Description:用户实现
 */
@Service
public class UserServiceImpl implements IUserService {

    private static Logger log = LogManager.getLogger(UserServiceImpl.class);

    @Autowired
    private PlatformTransactionManager txManager;

    @Autowired
    private UserDao userDao;

    @Autowired
    private TransactionUtils transactionUtils;

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void add(User user) {
        log.info("添加用户信息{}", JSONObject.toJSONString(user));
        userDao.save(user);
        update(user);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void update(User user) {
        log.info("更新用户信息{}", JSONObject.toJSONString(user));
        user.setAge(111);
        int i=1/0;
    }

    @Override
    public int handlerTransaction(User user) {
        TransactionStatus begin = transactionUtils.begin();
        try {
            int save = userDao.save(user);
            int i = 1/0;
            transactionUtils.commit(begin);
            return save;
        }catch (Exception e){
            transactionUtils.rollabck(begin);
        }
        return 0;

    }
}
package transaction;

import com.hong.spring.transaction.entity.User;
import com.hong.spring.transaction.service.IUserService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @Auther: csh
 * @Date: 2020/8/3 18:11
 * @Description:
 */

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:transaction/applicationfile-transaction.xml"})
public class TransactionTest {
    private static Logger log = LogManager.getLogger(TransactionTest.class);
    @Before
    public void init() {
        System.out.println("初始化");
    }


    @Autowired
    private IUserService userService;

    /**
     *
     * 功能描述:显示事务
     *
     * @param:
     * @return:
     * @auther: csh
     * @date: 2020/8/4 17:07
     */
    @Test
    public void handlerTransactionTest(){
        int save = userService.handlerTransaction(new User(null, "宏", 10));
        log.info("结果{}",save>0?"成功":"失败");
    }


    /**
     *
     * 功能描述:PROPAGATION_REQUIRED 测试
     *
     * @param:
     * @return:
     * @auther: csh
     * @date: 2020/8/3 18:16
     */
    @Test
    public void testTransaction(){
        System.out.println("验证开始");
        log.info("info日志。");
        User test1 = new User(1, "test1", 28);
        userService.add(test1);
    }
}

结果(建议查看源码)

初始化
验证开始
2020-08-04 17:23:18,200 [main] INFO : info日志。2020-08-04 17:23:18,590 [main] INFO : {dataSource-1} inited
前置通知生效
2020-08-04 17:23:18,696 [main] INFO : 添加用户信息{"age":28,"id":1,"username":"test1"}
2020-08-04 17:23:18,807 [main] DEBUG: stmt enter cache
2020-08-04 17:23:18,807 [main] INFO : 保存结果成功
2020-08-04 17:23:18,807 [main] INFO : 更新用户信息{"age":28,"id":1,"username":"test1"}
异常通知/ by zero

java.lang.ArithmeticException: / by zero

spring事务的传播行为有哪些?

spring7种传播行为:

传播行为

含义

PROPAGATION_REQUIRED(spring默认)

如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,这是最常见的选择,也是Spring默认的事务传播行为。

PROPAGATION_SUPPORTS

支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。

PROPAGATION_MANDATORY

支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。

PROPAGATION_REQUIRES_NEW

创建新事务,无论当前存不存在事务,都创建新事务。

PROPAGATION_NOT_SUPPORTED

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

PROPAGATION_NEVER

以非事务方式执行,如果当前存在事务,则抛出异常。

PROPAGATION_NESTED

如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。

事务管理:

代码实现

Propagation.REQUIRED(支持当前事务;如果不存在事务,则创建一个新的事务

/**
 * @Auther: csh
 * @Date: 2020/8/3 18:14
 * @Description:用户实现
 */
@Service
public class UserServiceImpl implements IUserService {

    private static Logger log = LogManager.getLogger(UserServiceImpl.class);

    @Autowired
    private PlatformTransactionManager txManager;

    @Autowired
    private UserDao userDao;

    @Autowired
    private TransactionUtils transactionUtils;

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void add(User user) {
        log.info("添加用户信息{}", JSONObject.toJSONString(user));
        userDao.save(user);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void update(User user) {
        log.info("更新用户信息{}", JSONObject.toJSONString(user));
        user.setAge(111);
        int i=1/0;
    }

    @Override
    public int handlerTransaction(User user) {
        TransactionStatus begin = transactionUtils.begin();
        try {
            int save = userDao.save(user);
            int i = 1/0;
            transactionUtils.commit(begin);
            return save;
        }catch (Exception e){
            transactionUtils.rollabck(begin);
        }
        return 0;

    }
}
/**
 * @Auther: csh
 * @Date: 2020/8/4 18:29
 * @Description:组织实现
 */
@Service
public class UserOrgServiceImpl implements IUserOrgService {

    @Autowired
    private UserRoleDao dao;

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void save(UserRole userRole) {
        dao.save(userRole);
    }

    @Override
    public void update(UserRole userRole) {
    }

    @Override
    public UserRole findById(Integer id) {
        return null;
    }
}
/**
 *
 * 功能描述:未加外围事务测试 (PROPAGATION_REQUIRED 测试)
 * 结果:都可以添加
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/4 18:42
 */
@Test
public void testTransaction3(){
    System.out.println("验证开始");
    log.info("info日志。");
    User test1 = new User(2, "test1", 29);
    userService.add(test1);
    //增加角色
    UserRole userRole = new UserRole();
    userRole.setUserId(2);
    userRole.setRoleId(2);
    userOrgService.save(userRole);
    //异常
    throw  new RuntimeException();
}

结果:由于该junit外围没有事务所以结果都是成功

初始化
验证开始
2020-08-04 18:41:50,565 [main] INFO : info日志。2020-08-04 18:41:50,868 [main] INFO : {dataSource-1} inited
前置通知生效
2020-08-04 18:41:50,966 [main] INFO : 添加用户信息{"age":28,"id":1,"username":"test1"}
2020-08-04 18:41:50,982 [main] DEBUG: stmt enter cache
2020-08-04 18:41:50,982 [main] INFO : 保存结果成功
前置通知生效
2020-08-04 18:41:51,113 [main] DEBUG: stmt enter cache
2020-08-04 18:41:51,113 [main] INFO : 保存结果成功

2.使用(@Transactional(propagation = Propagation.REQUIRED)) junit外围有事务

//**
 *
 * 功能描述:未加外围事务测试 (PROPAGATION_REQUIRED 测试)
 * 结果:都可以添加
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/4 18:42
 */
@Test
public void testTransaction3(){
    System.out.println("验证开始");
    log.info("info日志。");
    User test1 = new User(2, "test1", 29);
    userService.add(test1);
    //增加角色
    UserRole userRole = new UserRole();
    userRole.setUserId(2);
    userRole.setRoleId(2);
    userOrgService.save(userRole);
    //异常
    throw  new RuntimeException();
}

结果:由于该junit外围没有事务所以结果没有成功

初始化
验证开始
2020-08-04 18:41:50,565 [main] INFO : info日志。
2020-08-04 18:41:50,868 [main] INFO : {dataSource-1} inited
前置通知生效
2020-08-04 18:41:50,966 [main] INFO : 添加用户信息{"age":28,"id":1,"username":"test1"}
2020-08-04 18:41:50,982 [main] DEBUG: stmt enter cache
2020-08-04 18:41:50,982 [main] INFO : 保存结果成功
前置通知生效
2020-08-04 18:41:51,113 [main] DEBUG: stmt enter cache
2020-08-04 18:41:51,113 [main] INFO : 保存结果成功

2.使用(@Transactional(propagation = Propagation.REQUIRED)) junit外围有事务

/**
 *
 * 功能描述:事务测试 (PROPAGATION_REQUIRED 测试)
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/4 18:42
 */
@Transactional
@Test
public void testTransaction2(){
    System.out.println("验证开始");
    log.info("info日志。");
    User test1 = new User(2, "test1", 29);
    userService.add(test1);
    //增加角色
    UserRole userRole = new UserRole();
    userRole.setUserId(2);
    userRole.setRoleId(2);
    userOrgService.save(userRole);
    //异常
    throw  new RuntimeException();
}

结果:由于该junit外围没有事务所以结果没有成功

初始化
验证开始
2020-08-04 18:51:05,001 [main] INFO : info日志。
前置通知生效
2020-08-04 18:51:05,084 [main] INFO : 添加用户信息{"age":29,"id":2,"username":"test1"}
2020-08-04 18:51:05,102 [main] DEBUG: stmt enter cache
2020-08-04 18:51:05,102 [main] INFO : 保存结果成功
前置通知生效
2020-08-04 18:51:05,117 [main] DEBUG: stmt enter cache
2020-08-04 18:51:05,117 [main] INFO : 保存结果成功
八月 04, 2020 6:51:05 下午 org.springframework.test.context.transaction.TransactionContext endTransaction
信息: Rolled back transaction for test context [DefaultTestContext@3f3c7bdb testClass = TransactionTest, testInstance = transaction.TransactionTest@456abb66, testMethod = testTransaction2@TransactionTest, testException = java.lang.RuntimeException, mergedContextConfiguration = [MergedContextConfiguration@588ab592 testClass = TransactionTest, locations = '{classpath:transaction/applicationfile-transaction.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]].

java.lang.RuntimeException
  at transaction.TransactionTest.testTransaction2(TransactionTest.java:122)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:498)
  at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
  at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
  at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
  at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
  at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
  at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
  at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
  at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
  at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
  at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
  at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
  at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
  at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
  at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
  at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
  at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
  at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
  at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
  at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
  at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
  at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
  at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
  at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

2020-08-04 18:51:05,239 [Thread-1] INFO : {dataSource-1} closing ...
2020-08-04 18:51:05,241 [Thread-1] DEBUG: stmt exit cache
2020-08-04 18:51:05,241 [Thread-1] DEBUG: stmt exit cache
2020-08-04 18:51:05,242 [Thread-1] INFO : {dataSource-1} closed

总结:Propagation.REQUIRED若外围配置事务,当有一个事务失败将会影响整个事务失败。反之若外围没有配置事务只能以本身的单独事务为主,但是不会影响外围结果。

PROPAGATION_SUPPORTS(支持当前事务;如果不存在,则执行非事务性

1.外围未加事务测试

/**
 *
 * 功能描述:验证propagation = Propagation.SUPPORTS的结果
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/5 10:09
 */
void save2(UserRole userRole);

/**
 *
 * 功能描述:验证Propagation.SUPPORTS
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/5 10:05
 */
void add2(User user);


@Transactional(propagation = Propagation.SUPPORTS)
@Override
public void add2(User user) {
    log.info("添加用户信息2{}", JSONObject.toJSONString(user));
    userDao.save(user);
}


@Transactional(propagation = Propagation.SUPPORTS)
@Override
public void save2(UserRole userRole) {
    dao.save(userRole);
}

junit测试

/**
 *
 * 功能描述:未加外围事务的
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/5 10:06
 */
@Test
public void testSupports(){
    User test1 = new User(3, "test222", 28);
    userService.add2(test1);
    //增加角色
    UserRole userRole = new UserRole(null,2,2);
    userOrgService.save2(userRole);
    //异常
    throw  new RuntimeException();
}

结果(数据添加成功)

初始化
前置通知生效
2020-08-05 10:10:30,069 [main] INFO : 添加用户信息2{"age":28,"id":3,"username":"test222"}
2020-08-05 10:10:30,663 [main] INFO : {dataSource-1} inited
2020-08-05 10:10:30,841 [main] DEBUG: stmt enter cache
2020-08-05 10:10:30,841 [main] INFO : 保存结果成功
前置通知生效
2020-08-05 10:10:30,948 [main] DEBUG: stmt enter cache
2020-08-05 10:10:30,948 [main] INFO : 保存结果成功

java.lang.RuntimeException
  at transaction.TransactionTest.testSupports(TransactionTest.java:141)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:498)
  at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
  at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
  at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
  at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
  at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
  at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
  at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
  at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
  at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
  at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
  at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
  at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
  at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
  at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
  at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
  at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
  at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
  at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
  at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
  at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
  at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
  at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
  at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

2020-08-05 10:10:30,955 [Thread-1] INFO : {dataSource-1} closing ...
2020-08-05 10:10:30,957 [Thread-1] DEBUG: stmt exit cache
2020-08-05 10:10:30,957 [Thread-1] DEBUG: stmt exit cache
2020-08-05 10:10:30,958 [Thread-1] INFO : {dataSource-1} closed

2.外围加事务测试

junit中新增外围事务Test

/*/**
 *
 * 功能描述:Propagation.SUPPORTS 加外围事务的
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/5 10:06
 */
@Test
@Transactional
public void testSupports2(){
    User test1 = new User(null, "test333", 30);
    userService.add2(test1);
    //增加角色
    UserRole userRole = new UserRole(null,33,33);
    userOrgService.save2(userRole);
    //异常
    throw  new RuntimeException();
}

其他代码同上....

结果

数据库:都没有插入

两个库都没有添加成功,由于外围有默认事务,这里注意是默认事务,不是SUPPORTS,因为如果是SUPPORTS事务,也是会添加成功的。

3.一个方法中抛出异常

在add2中添加 throw new RuntimeException();异常

@Transactional(propagation = Propagation.SUPPORTS)
@Override
public void add2(User user) {
   log.info("添加用户信息2{}", JSONObject.toJSONString(user));
    userDao.save(user);
    try{
        throw new RuntimeException();
    }catch (Exception e){
        e.printStackTrace();
    }
}

加事务结果(都添加成功)若不异常处理则会影响下一张表添加成功

不加事务结果(都没有添加成功)

列1:外层不加事务 内层都加PROPAGATION_SUPPORTS事务,则运行结果都是添加成功;

列2:外层加默认事务 内层都加PROPAGATION_SUPPORTS,则都添加失败;

列3:外层不加事务 内层方法2抛出异常,则1表添加成功,2表添加成功;

列4:外层加默认事务 内层方法2抛出异常,则添加都失败。

总结:由于PROPAGATION_SUPPORTS是“支持当前事务;如果不存在,则执行非事务性",也就是说如果上一层有事务,并且是非SUPPORTS如果默认事务,则以上层的为主,如果都是SUPOORTS那都以非事务进行。

PROPAGATION_MANDATORY(支持当前事务;如果不存在当前事务,则抛出一个异常)

IUserOrgService新增方法

/**
 *
 * 功能描述: 验证propagation = Propagation.MANDATORY的结果
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/5 14:24
 */
void save3(UserRole userRole);

UserOrgServiceImpl实现

@Transactional(propagation = Propagation.MANDATORY)
@Override
public void add3(User user) {
    log.info("添加用户信息3{}", JSONObject.toJSONString(user));
    userDao.save(user);
}

IUserService新增方法

/**
 *
 * 功能描述:验证PROPAGATION_MANDATORY
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/5 14:17
 */
void add3(User user);

UserOrgServiceImpl实现

@Transactional(propagation = Propagation.MANDATORY)
@Override
public void save3(UserRole userRole) {
    dao.save(userRole);
}

1.外围没有事务(添加都没成功并且抛出异常)

新增单元测试

@Test
public void testMandatory4(){
    User test1 = new User(null, "test444", 44);
    userService.add3(test1);
    //增加角色
    UserRole userRole = new UserRole(null,444,44);
    userOrgService.save3(userRole);
}

结果

初初始化

org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'

  at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:359)
  at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:447)
  at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:277)
  at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
  at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
  at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
  at com.hong.spring.transaction.service.impl.UserServiceImpl$$EnhancerBySpringCGLIB$$4325da50.add3(<generated>)
  at transaction.TransactionTest.testSupports4(TransactionTest.java:190)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:498)
  at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
  at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
  at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
  at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
  at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
  at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
  at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
  at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
  at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
  at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
  at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
  at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
  at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
  at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
  at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
  at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
  at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
  at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
  at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
  at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
  at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
  at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
  at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

八月 05, 2020 2:20:55 下午 org.springframework.context.support.GenericApplicationContext doClose
信息: Closing org.springframework.context.support.GenericApplicationContext@367ffa75: startup date [Wed Aug 05 14:20:53 CST 2020]; root of context hierarchy

没有新增任何结果

结果:外围没有事务,所以刚执行第一个方法就直接报错了。“如果当前不存在事务,就抛出异常”得到验证。

2.外围添加事务

配置防止执行成功回滚@Rollback

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:transaction/applicationfile-transaction.xml"})
//防止执行成功默认回滚
@Rollback(value = false)
public class TransactionTest {

新增junit,其他同上。

/**
 *
 * 功能描述: 验证PROPAGATION_MANDATORY
 * 结果是插入成功,由于外围有事务,会带入到入部。
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/5 14:34
 */
@Transactional
@Test
public void testMandatory5(){
    User test1 = new User(null, "test555", 55);
    userService.add3(test1);
    //增加角色
    UserRole userRole = new UserRole(null,555,55);
    userOrgService.save3(userRole);
}

结果

总结:PROPAGATION_MANDATORY非常好测试,如果不存在事务就直接抛出异常了,也就是说如果junit这最上层去调service没有事务就直接报错了,走都走不了。

PROPAGATION_REQUIRES_NEW(创建新事务,无论当前存不存在事务,都创建新事务。)

1.外围未加事务(添加成功)

/**
 *
 * 功能描述:验证PROPAGATION_REQUIRES_NEW
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/5 16:47
 */
void add4(User user);

@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void add4(User user) {
    log.info("添加用户信息4{}", JSONObject.toJSONString(user));
    userDao.save(user);
}

IUserService新增接口 add4,UserServiceImpl实现该接口

/**
 *
 * 功能描述:验证PROPAGATION_REQUIRES_NEW
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/5 16:48
 */
void save4(UserRole userRole);
/**
 *
 * 功能描述:验证PROPAGATION_REQUIRES_NEW
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/5 16:51
 */
@Test
public void testRequiresNew6(){
    User test1 = new User(null, "test666", 66);
    userService.add4(test1);
    //增加角色
    UserRole userRole = new UserRole(null,666,66);
    userOrgService.save4(userRole);
    //抛异常
    throw new RuntimeException();
}

结果(添加成功)

初始化
2020-08-05 16:53:03,364 [main] INFO : {dataSource-1} inited
前置通知生效
2020-08-05 16:53:03,741 [main] INFO : 添加用户信息4{"age":66,"username":"test666"}
2020-08-05 16:53:03,891 [main] DEBUG: stmt enter cache
2020-08-05 16:53:03,891 [main] INFO : 保存结果成功
前置通知生效
2020-08-05 16:53:03,951 [main] DEBUG: stmt enter cache
2020-08-05 16:53:03,951 [main] INFO : 保存结果成功

八月 05, 2020 4:53:04 下午 org.springframework.context.support.GenericApplicationContext doClose
信息: Closing org.springframework.context.support.GenericApplicationContext@3857f613: startup date [Wed Aug 05 16:53:01 CST 2020]; root of context hierarchy

java.lang.RuntimeException
  at transaction.TransactionTest.testMandatory6(TransactionTest.java:241)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:498)
  at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
  at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
  at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
  at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
  at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
  at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
  at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
  at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
  at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
  at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
  at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
  at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
  at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
  at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
  at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
  at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
  at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
  at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
  at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
  at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
  at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
  at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
  at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

2020-08-05 16:53:04,068 [Thread-1] INFO : {dataSource-1} closing ...
2020-08-05 16:53:04,069 [Thread-1] DEBUG: stmt exit cache
2020-08-05 16:53:04,069 [Thread-1] DEBUG: stmt exit cache
2020-08-05 16:53:04,070 [Thread-1] INFO : {dataSource-1} closed

2.未围添加事务(都成功)

/**
 *
 * 功能描述:外围事务不会影响到 验证PROPAGATION_REQUIRES_NEW 事务传播 (都添加成功)
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/5 17:20
 */
@Transactional
@Test
public void testRequiresNew7(){
    User test1 = new User(null, "test777", 77);
    userService.add4(test1);
    //增加角色
    UserRole userRole = new UserRole(null,777,77);
    userOrgService.save4(userRole);
    //抛异常
    throw new RuntimeException();
}

结果:数据添加成功

3.在实现中添加异常

@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void save4(UserRole userRole) {
    dao.save(userRole);
    throw new RuntimeException();
}

结果:未添加异常的表可以正常添加,但是添加异常的表无法添加。该事务只影响本身。

总结:外围事务或其他事务不会影响到事务的成功与失败,只有本事务内部有问题才会导致失败。验证了:Propagation.REQUIRES_NEW,"无论当前存不存在事务,都创建新事务"。

PROPAGATION_NOT_SUPPORTED(以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。)

1.外围未加事务(验证结果可以添加)

IUserService新增接口 add5,UserServiceImpl实现该接口

/**
 *
 * 功能描述:验证PROPAGATION_NOT_SUPPORTED
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/5 17:30
 */
void add5(User user);

@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override
public void add5(User user) {
    log.info("添加用户信息5{}", JSONObject.toJSONString(user));
    userDao.save(user);
}

IUserOrgService新增save5而UserOrgServiceImpl实现该接口

/**
 *
 * 功能描述:验证PROPAGATION_NOT_SUPPORTED
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/5 17:30
 */
void save5(UserRole userRole);

@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override
public void save5(UserRole userRole) {
    dao.save(userRole);
}
/**
 *
 * 功能描述: 外围不加事务,验证PROPAGATION_NOT_SUPPORTED
 * 结果都可以添加成功。
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/5 17:40
 */
@Test
public void testNotSupported8(){
    User test1 = new User(null, "test888", 88);
    userService.add5(test1);
    //增加角色
    UserRole userRole = new UserRole(null,888,88);
    userOrgService.save5(userRole);
    //抛异常
    throw new RuntimeException();
}

结果:

初始化
前置通知生效
2020-08-05 17:39:22,655 [main] INFO : 添加用户信息5{"age":88,"username":"test888"}
2020-08-05 17:39:23,080 [main] INFO : {dataSource-1} inited
2020-08-05 17:39:23,153 [main] DEBUG: stmt enter cache
2020-08-05 17:39:23,153 [main] INFO : 保存结果成功
前置通知生效
2020-08-05 17:39:23,199 [main] DEBUG: stmt enter cache
2020-08-05 17:39:23,199 [main] INFO : 保存结果成功

java.lang.RuntimeException
  at transaction.TransactionTest.testNotSupported8(TransactionTest.java:272)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:498)
  at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
  at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
  at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
  at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
  at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
  at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
  at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
  at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
  at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
  at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
  at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
  at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
  at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
  at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
  at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
  at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
  at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
  at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
  at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
  at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
  at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
  at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
  at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

2020-08-05 17:39:23,208 [Thread-1] INFO : {dataSource-1} closing ...
八月 05, 2020 5:39:23 下午 org.springframework.context.support.GenericApplicationContext doClose
信息: Closing org.springframework.context.support.GenericApplicationContext@3857f613: startup date [Wed Aug 05 17:39:21 CST 2020]; root of context hierarchy
2020-08-05 17:39:23,210 [Thread-1] DEBUG: stmt exit cache
2020-08-05 17:39:23,210 [Thread-1] DEBUG: stmt exit cache
2020-08-05 17:39:23,211 [Thread-1] INFO : {dataSource-1} closed

2.外围添加事务(添加一样成功)

/**
 *
 * 功能描述:外围添加事务
 * 同样可以添加成功
 *
 * @param:
 * @return:
 * @auther: csh
 * @date: 2020/8/5 17:44
 */
@Transactional
@Test
public void testNotSupported9(){
    User test1 = new User(null, "test999", 99);
    userService.add5(test1);
    //增加角色
    UserRole userRole = new UserRole(null,999,99);
    userOrgService.save5(userRole);
    //抛异常
    throw new RuntimeException();
}

结果:可以添加成功。证明该事务“以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。”

初始化
前置通知生效
2020-08-05 17:45:02,841 [main] INFO : 添加用户信息5{"age":99,"username":"test999"}
2020-08-05 17:45:02,908 [main] DEBUG: stmt enter cache
2020-08-05 17:45:02,908 [main] INFO : 保存结果成功
前置通知生效
八月 05, 2020 5:45:02 下午 org.springframework.test.context.transaction.TransactionContext endTransaction
信息: Committed transaction for test context [DefaultTestContext@1bdbf9be testClass = TransactionTest, testInstance = transaction.TransactionTest@1e7f2e0f, testMethod = testNotSupported9@TransactionTest, testException = java.lang.RuntimeException, mergedContextConfiguration = [MergedContextConfiguration@1af7f54a testClass = TransactionTest, locations = '{classpath:transaction/applicationfile-transaction.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]].
2020-08-05 17:45:02,961 [main] DEBUG: stmt enter cache
2020-08-05 17:45:02,961 [main] INFO : 保存结果成功

java.lang.RuntimeException
  at transaction.TransactionTest.testNotSupported9(TransactionTest.java:302)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:498)
  at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
  at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
  at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
  at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
  at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
  at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
  at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
  at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
  at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
  at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
  at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
  at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
  at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
  at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
  at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
  at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
  at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
  at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
  at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
  at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
  at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
  at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
  at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

2020-08-05 17:45:02,973 [Thread-1] INFO : {dataSource-1} closing ...
2020-08-05 17:45:02,975 [Thread-1] DEBUG: stmt exit cache
2020-08-05 17:45:02,976 [Thread-1] DEBUG: stmt exit cache
2020-08-05 17:45:02,977 [Thread-1] INFO : {dataSource-1} closed

3.在某个方法中添加异常抛出

在save5中添加异常抛出

@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override
public void save5(UserRole userRole) {
    dao.save(userRole);
    throw  new RuntimeException();
}

结果:都可以正常添加。

总结:PROPAGATION_NOT_SUPPORTED,这个就是不管你有没有事务,外围还是自身加了事务都会被自动去掉,变成无事务的,然后执行以无事务的进行,验证了:“如果当前存在事务,就把当前事务挂起,相当于以非事务方式执行”。

最后

spring事务的传播行为,在实际项目中大都是以默认为主,除了特别的业务或者特别的处理可能会根据自身的业务来选择。当然大部分实现事务也都是通过注解形式及少场景下需要定制化来实现编程式事务。本文只是针对全局或本地的事务较浅,非分布式事务,统一再详写。

参考文献:

https://baike.baidu.com/item/%E4%BA%8B%E5%8A%A1/5945882

http://blog.sina.com.cn/s/blog_499740cb0100ugs7.html

https://dev.mysql.com/doc/refman/5.6/en/set-transaction.html

https://blog.csdn.net/Oct31_Dec25/article/details/89286312?biz_id=102

https://www.cnblogs.com/leeSmall/p/10306672.html

https://juejin.im/post/6844903938928427022

https://developer.ibm.com/zh/articles/j-master-spring-transactional-use/