Spring 事务管理

文章目录

  • Spring 事务管理
  • 声明式事务
  • 事务传播机制

Spring 事务管理

Spring 事务管理主要通过声明式事务和编程式事务两种方式来实现。

  • 编程式事务(手动写代码操作事务)(不常用)
  • 声明式事务(使用注解自动开启和提交事务)

声明式事务

在方法上添加 @Transactional 注解。进入方法时自动开启事务,方法执行完自动提交事务,如果发生了没有处理的异常会自动回滚事务。

@Service
@Transactional
public class YourService {
    // 业务逻辑代码
}

@Transactional 作用范围

@Transactional 可以用来修饰方法或类:

  • 修饰方法时:需要注意只能应用到 public 方法上,否则不生效。这是推荐的用法
  • 修饰类时:表明该注解对该类中所有 public 方法都生效。

参数:

参数 说明
value 当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器
transactionManager 同上
propagation 事务的传播行为,默认值:Propagation.REQUIRED
isolation 事务的隔离级别,默认值:Isolation.DEFAULT
timeout 事务的超时时间,默认值:-1,如果超时但事务还没完成,则自动回滚事务
readOnly 如果事务实际上是只读的,则可以将布尔标志设置为 true,从而允许在运行时进行相应的优化。默认值:false
rollbackFor 定义零(0)个或多个异常类型,指示哪些异常类型必须导致事务回滚
rollbackForClassName 定义零(0)个或多个异常名称模式,指示哪些异常类型必须导致事务回滚
noRollbackFor 定义零(0)个或多个异常类型,指示哪些异常类型不能导致事务回滚
noRollbackForClassName 定义零(0)个或多个异常名称模式,指示哪些异常类型不能导致事务回滚

手动回滚事务

有时候发生了异常也不会自动回滚,因为可能这个异常被处理了,没有抛出。

此时我们依然想让事务回滚,可以采取手动的方式:

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

事务隔离级别

如果设置为 Isolation.DEFAULT,就以数据库的为准。如果设置了其他的,比如 Isolation.READ_COMMITTED 读已提交,就按 Spring 设置的为准。

事务传播机制

事务传播机制是指在一个包含多个事务操作的方法或代码块中,各个事务之间相互影响和传播的规则。

以下是 7 种事务传播行为:

  1. PROPAGATION_REQUIRED(默认):如果当前没有事务,就新建一个事务;如果已经存在一个事务中,加入该事务。这是最常见的选择,也是 Spring 默认的事务传播行为。
  2. PROPAGATION_REQUIRES_NEW:每次都会新建一个事务,如果当前存在事务,则将当前事务挂起。新建的事务与外层事务无关,它们互不影响。
  3. PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
  4. PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  5. PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
  6. PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,则抛出异常。
  7. PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则新建一个事务。嵌套事务是一个相对独立的事务,它有自己的保存点,可以回滚到保存点,而不影响外层事务。

例:

package org.example.springboottransaction.service;

import org.example.springboottransaction.mapper.UserMapper;
import org.example.springboottransaction.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class TransactionService {
    @Autowired
    UserMapper userMapper;
    
    @Transactional
    public void transactionMethod1() {
        // 一些业务逻辑...
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("ZhangSan");
        userInfo.setPassword("123");
        userMapper.add(userInfo);
        System.out.println("Method 1 executed");
    }

    @Transactional
    public void transactionMethod2() {
        // 另一些业务逻辑...
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("LiSi");
        userInfo.setPassword("1234");
        userMapper.add(userInfo);
        System.out.println("Method 2 executed");
        throw new RuntimeException("Exception in Method 2");
    }
}
package org.example.springboottransaction.controller;

import org.example.springboottransaction.service.TransactionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TransactionClient {
    @Autowired
    private TransactionService transactionService;

    @GetMapping("/transaction")
    public String performTransactions() {
        transactionService.transactionMethod1();
        transactionService.transactionMethod2();
        return "OK";
    }
}

在上述代码中,performTransactions 方法依次调用了两个事务方法。请注意,transactionMethod2 方法抛出了运行时异常。

由于默认的事务传播行为是 PROPAGATION_REQUIRED,整个事务会回滚,包括前面已经执行的 transactionMethod1

查看数据库表,会发现一条数据也没有插入。


我们修改 transactionMethod2 方法的事务传播机制为 PROPAGATION_REQUIRES_NEW,让它新建一个事务,不要影响 transactionMethod1

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void transactionMethod2() {
    // 另一些业务逻辑...
    UserInfo userInfo = new UserInfo();
    userInfo.setUsername("LiSi");
    userInfo.setPassword("1234");
    userMapper.add(userInfo);
    System.out.println("Method 2 executed");
    throw new RuntimeException("Exception in Method 2");
}

重新调用 performTransactions 方法,查看数据库表,会发现 transactionMethod1 执行的数据成功插入了。

嵌套事务(NESTED)和加入事务(REQUIRED)的区别

  • 整个事务如果全部执行成功,二者的结果一样
  • 如果事务执行到一半失败了,那么加入事务的整个事务会全部回滚,而嵌套事务是局部回滚,不会影响上一个方法中执行的结果。

你可能感兴趣的:(Java,spring,数据库,oracle)