Spring事务传播行为详解

本文详细介绍了Spring中的7种事务传播行为,包括PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW和PROPAGATION_NESTED等。通过多个场景的代码验证,分析了这些传播行为在事务嵌套时如何影响事务的回滚和执行。文章强调了理解事务传播行为对于正确使用Spring事务管理的重要性,并通过实例解析了各传播行为的异同。
摘要由CSDN通过智能技术生成

前言

Spring在TransactionDefinition接口中规定了7种类型的事务传播行为。事务传播行为是Spring框架独有的事务增强(通知)特性,是Spring为我们提供的强大的工具箱,使用事务传播行可以为我们的开发工作提供许多便利。但是人们对他的误解也颇多,你一定也听过“service方法事务最好不要嵌套”的传言。要想正确的使用工具首先需要了解工具。本文对七种事务传播行为做详细介绍,内容主要代码示例的方式呈现。

基础概念

1.什么是事务传播行为?

事务传播行为用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法的事务如何传播。也就是说:当事务方法被另一个事务方法调用时,必须指定事务应该如何传播

2.Spring中7种事务传播行为

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

上述对七种传播行为的说明,简单易懂,但有很多细节值得我们去深究,下面我们将对required、requires_new、nested三个重点且常用的传播行为进行分析。

代码验证

文中代码以传统三层结构中两层呈现,即Service和Dao层,由Spring负责依赖注入和注解式事务管理,DAO层由Mybatis实现,你也可以使用任何喜欢的方式,例如,Hibernate,JPA,JDBCTemplate等。数据库使用的是MySQL数据库,你也可以使用任何支持事务的数据库,并不会影响验证结果。

首先我们在数据库中创建两张表:

user1

CREATE TABLE `user1` (
  `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(45) NOT NULL DEFAULT '',
  PRIMARY KEY(`id`)
)
ENGINE = InnoDB;

user2

CREATE TABLE `user2` (
  `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(45) NOT NULL DEFAULT '',
  PRIMARY KEY(`id`)
)
ENGINE = InnoDB;

然后编写相应的Bean和DAO层代码:

User1

package com.bruce.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User1 {
   

    private Integer id;
    private String name;
}

User2

package com.bruce.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User2 {
   


    private Integer id;
    private String name;
}

User1Mapper

package com.bruce.mapper;

import com.bruce.pojo.User1;

public interface User1Mapper {
   

    int insert(User1 u);
}

User1Mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bruce.mapper.User1Mapper">

    <insert id="insert">
        insert into tb_user1 values(null,#{name})
    </insert>

</mapper>

User2Mapper

package com.bruce.mapper;

import com.bruce.pojo.User1;
import com.bruce.pojo.User2;

public interface User2Mapper {
   

    int insert(User2 u);
}

User2Mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bruce.mapper.User2Mapper">

    <insert id="insert">
        insert into tb_user2 values(null,#{name})
    </insert>

</mapper>

最后也是具体验证的代码由service层实现,下面我们分情况列举。

1.PROPAGATION_REQUIRED

如果当前没有事务,就新建一个事务;如果已经存在一个事务,就加入该事务中(使用该事务)

我们为User1Service和User2Service相应方法加上PROPAGATION_REQUIRED属性。

1.1 场景1

外围方法没有开启事务,外围方法有异常。

User1ServiceImpl

package com.bruce.service.impl;

import com.bruce.mapper.User1Mapper;
import com.bruce.pojo.User1;
import com.bruce.service.User1Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class User1ServiceImpl implements User1Service {
   

    @Autowired
    User1Mapper user1Mapper;

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public int insert(User1 u) {
   
        return user1Mapper.insert(u);
    }
}

User2ServiceImpl

package com.bruce.service.impl;

import com.bruce.mapper.User2Mapper;
import com.bruce.pojo.User2;
import com.bruce.service.User2Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class User2ServiceImpl  implements User2Service {
   

    @Autowired
    User2Mapper user2Mapper;

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public int insert(User2 u) {
   
        return user2Mapper.insert(u);
    }
}

UserServiceImpl:外围方法

package com.bruce.service.impl;

import com.bruce.pojo.User1;
import com.bruce.pojo.User2;
import com.bruce.service.User1Service;
import com.bruce.service.User2Service;
import com.bruce.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
   

    @Autowired
    User1Service user1Service;

    @Autowired
    User2Service user2Service;

    @Override
    public int addUser1AndUser2(User1 u1, User2 u2) {
   
        int count1 = user1Service.insert(u1);
        System.out.println("count1="+count1);
        
        System.out.println(100/0);//异常

        int count2 = user2Service.insert(u2);
        System.out.println("count2="+count2);
        
        return count1+count2;
    }
}

TestUser

package com.bruce.test;

import com.bruce.pojo.User1;
import com.bruce.pojo.User2;
import com.bruce.service.UserService;
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;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestUser {
   

    @Autowired
    UserService userService;

    @Test
    public void testUU(){
   
        int count = userService.addUser1AndUser2(new User1(null, "小舞"), new User2(null, "唐三"));
        System.out.println(count);
    }
}

结果:数据库中,唐三和小舞都插入

结果分析:当外围方法未开启事务时,外围方法中的异常不会影响内部方法,内部方法都在自己的事务中独立运行

1.2 场景2

外围方法没有开启事务,内部方法有异常。

User1ServiceImpl

package com.bruce.service.impl;

import com.bruce.mapper.User1Mapper;
import com.bruce.pojo.User1;
import com.bruce.service.User1Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class User1ServiceImpl implements User1Service {
   

    @Autowired
    User1Mapper user1Mapper;

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public int insert(User1 u) {
   
        return user1Mapper.insert(u);
    }
}

User2ServiceImpl

package com.bruce.service.impl;

import com.bruce.mapper.User2Mapper;
import com.bruce.pojo.User2;
import com.bruce.service.User2Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class User2ServiceImpl  implements User2Service {
   

    @Autowired
    User2Mapper user2Mapper;

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public int insert(User2 u) {
   
        System.out.println(100/0);//异常
        return user2Mapper.insert(u);
    }
}

UserServiceImpl:外围方法

package com.bruce.service.impl;

import com.bruce.pojo.User1;
import com.bruce.pojo.User2;
import com.bruce.service.User1Service;
import com.bruce.service.User2Service;
import com.bruce.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
   

    @Autowired
    User1Service user1Service;

    @Autowired
    User2Service user2Service;

    @Override
    public int addUser1AndUser2(User1 u1, User2 u2) {
   
        int count1 = user1Service.insert(u1);
        System.out.println("count1="+count1);

        int count2 = user2Service.insert(u2);
        System.out.println("count2="+count2);
        
        return count1+count2;
    }
}

TestUser

package com.bruce.test;

import com.bruce.pojo.User1;
import com.bruce.pojo.User2;
import com.bruce.service.UserService;
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;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestUser {
   

    @Autowired
    UserService userService;

    @Test
    public void testUU(){
   
        int count = userService.addUser1AndUser2(new User1(null, "小舞"), new User2(null, "唐三"));
        System.out.println(count);
    }
}

结果:数据库中,唐三没有插入,小舞插入

结果分析:当外围方法未开启事务时,内部方法中的异常只会回滚到自己方法上,其他方法不受影响

结论1

通过以上两个场景我们证明了在外围方法未开启事务的情况下,Propagation.REQUIRED修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。

1.3 场景3

外围方法开启事务,外围方法有异常。(常用)

User1ServiceImpl

package com.bruce.service.impl;

import com.bruce.mapper.User1Mapper;
import com.bruce.pojo.User1;
import com.bruce.service.User1Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class User1ServiceImpl implements User1Service {
   

    @Autowired
    User1Mapper user1Mapper;

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public int insert(User1 u) {
   
        return user1Mapper.insert(u);
    }
}

User2ServiceImpl

package com.bruce.service.impl;

import com.bruce.mapper.User2Mapper;
import com.bruce.pojo.User2;
import com.bruce.service.User2Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class User2ServiceImpl  implements User2Service {
   

    @Autowired
    User2Mapper user2Mapper;

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public int insert(User2 u) {
   
        return user2Mapper.insert(u);
    }
}

UserServiceImpl:外围方法

package com.bruce.service.impl;

import com.bruce.pojo.User1;
import com.bruce.pojo.User2;
import com.bruce.service.User1Service;
import com.bruce.service.User2Service;
import com.bruce.service.UserService;
import 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

釣餌锒鐺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值