Spring 事务相关配置

Spring事务相关配置

虽然学会了使用Spring回滚事务,但是有一种异常Spring事务默认是不进行回滚的:那就是编译器异常。

说明:对于RuntimeException类型异常或者Error错误,Spring事务能够进行回滚操作。但是对于编译器异常,Spring事务是不进行回滚的,所以需要使用rollbackFor来设置要回滚的异常。

那么对于这些异常,我们怎么设置回滚呢?

这就要用到事务的配置去指定特定的异常回滚

1. 事务配置

事务常见配置包括如下表所示:

属性 作用 示例
readOnly 设置是否为只读事务 readOnly=true 只读事务
timeout 设置事务超时时间 timeout = -1(永不超时)
rollbackFor 设置事务回滚异常(class) rollbackFor = {NullPointException.class}
rollbackForClassName 设置事务回滚异常(String) 同上格式为字符串
noRollbackFor 设置事务不回滚异常(class) noRollbackFor = {NullPointException.class}
noRollbackForClassName 设置事务不回滚异常(String) 同上格式为字符串
propagation 设置事务传播行为 下面就行单独讲解示例

我们在配置时,直接写为Transactional注解参数就可以了,代码示例:

@Transactional(readOnly=true)
public void transfer(String out,String in ,Double money) throws IOException;

对于RuntimeException类型异常或者Error错误,Spring事务能够进行回滚操作。但是对于编译器异常(例如 IOException),Spring事务是不进行回滚的

所以我们使用rollbackFor来设置要回滚的异常,示例代码如下:

实现类业务中抛出IOException

public void transfer(String out,String in ,Double money) throws IOException {
    
    
        accountDao.outMoney(out,money);
        if (true){
    
    
            throw new IOException();
        }
        accountDao.inMoney(in,money);
}

那么我们就需要在事务注解这配置rollbackFor = IOException.class,他才会回滚事务

@Transactional(rollbackFor = IOException.class)
public void transfer(String out,String in ,Double money) throws IOException;

2. 事务传播行为

2.1 事务传播行为概念

事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何运行。

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

2.2 事务的7种传播行为

事务传播行为是Spring框架独有的事务增强特性。

Spring在TransactionDefinition接口中规定了7种类型的事务传播行为。

7种:(required / supports / mandatory / requires_new / not supported / never / nested)

详细参数如下表所示

传播行为 说明
PROPAGATION_REQUIRED 如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,这是最常见的选择,也是Spring默认的事务传播行为。(required需要,没有新建,有加入)
PROPAGATION_SUPPORTS 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。(supports支持,有则加入,没有就不管了,非事务运行)
PROPAGATION_MANDATORY 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。(mandatory强制性,有则加入,没有异常)
PROPAGATION_REQUIRES_NEW 创建新事务,无论当前存不存在事务,都创建新事务。(requires_new需要新的,不管有没有,直接创建新事务)
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。(not supported不支持事务,存在就挂起)
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。(never不支持事务,存在就异常)
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。(nested存在就在嵌套的执行,没有就找是否存在外面的事务,有则加入,没有则新建)

对事务的要求程度可以从大到小排序:mandatory / supports / required / requires_new / nested / not supported / never

image-20210802153014296
例如我们在需要进行事务传播的业务上加上如下注解配置即可实现事务的传播:

 //propagation设置事务属性:传播行为设置为当前操作需要新事务
 @Transactional(propagation = Propagation.REQUIRES_NEW)
 void log(String out, String in, Double money);

3. 代码案例:转账业务追加日志

需求和分析

  • 需求:实现任意两个账户间转账操作,并对每次转账操作在数据库进行留痕
  • 需求微缩:A账户减钱,B账户加钱,数据库记录日志
  • 分析:
    ①:基于转账操作案例添加日志模块,实现数据库中记录日志
    ②:业务层转账操作(transfer),调用减钱、加钱与记录日志功能
  • 实现效果预期:
    无论转账操作是否成功,均进行转账操作的日志留痕
  • 存在的问题:
    日志的记录与转账操作隶属同一个事务,同成功同失败
  • 实现效果预期改进:
    无论转账操作是否成功,日志必须保留
  • 事务传播行为:事务协调员对事务管理员所携带事务的处理态度

image-20210802153216460

环境准备

USE transaction_test;

CREATE TABLE `tb_account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `money` double DEFAULT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE tb_log(
	id INT PRIMARY KEY AUTO_INCREMENT,
	info VARCHAR(255),
	createDate DATE
);

数据表tb_account中插入两条数据

image-20220724135305794

增加日志接口

public interface LogService {
    
    
    //propagation设置事务属性:传播行为设置为当前操作需要新事务
    @Transactional
    void log(String out, String in, Double money);
}

@Service
public class LogServiceImpl implements LogService {
    
    

    @Autowired
    private LogDao logDao;
    public void log(String out,String in,Double money ) {
    
    
        logDao.log("转账操作由"+out+"到"+in+",金额:"+money);
    }
}

public interface LogDao {
    
    
    @Insert("insert into tbl_log (info,createDate) values(#{info},now())")
    void log(String info);
}

在AccountServiceImpl中调用logService中添加日志的方法

@Service
public class AccountServiceImpl implements AccountService {
    
    
    @Autowired
    private AccountDao accountDao;

    @Autowired
    private LogService logService;

    public void transfer(String out,String in ,Double money) {
    
    
        try{
    
    
            accountDao.outMoney(out,money);
            int i = 1/0;
            accountDao.inMoney(in,money);
        }finally {
    
    
            logService.log(out,in,money);
        }
    }
}

在LogService的log()方法上设置事务的传播行为

public interface LogService {
    
    
    //propagation设置事务属性:传播行为设置为当前操作需要新事务
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    void log(String out, String in, Double money);
}

运行测试类,查看结果

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {
    
    
    @Autowired
    private AccountService accountService;

    @Test
    public void testTransfer() throws IOException {
    
    
        accountService.transfer("Tom","Jerry",50D);
    }
}

首先我们先注释除0操作,运行结果如下,没有问题:

image-20220724135536486
再进行除0操作,运行结果如下,没有发生变化,说明事务进行了回滚,没有问题:
image-20220724135536486
我们查看那个嵌套在内的日志事务是否调用:
在这里插入图片描述
说明事务传播配置成功了也。

猜你喜欢

转载自blog.csdn.net/weixin_45525272/article/details/125958489