跳至主要内容

Spring的Transaction事务管理

Welcome file

简介

本文的介绍主要从JDBC事务API的使用,到常用的SpringBoot中的事务管理,而SpringBoot其实是简化了很多Spring事务管理的配置。

JDBC的事务管理

基本的开启、提交和回滚事务

先复习一下JDBC的事务管理:

	public void jdbcSaveOrder() throws SQLException {
    // 1. 拿到JDBC连接
        Connection connection = dataSource.getConnection();
        try (connection) {
          // 2. 设置自己控制事务
            connection.setAutoCommit(false);

            ....
          
          // 3. 提交事务
            connection.commit();
        }catch (Exception e){
            log.error("ds save order error:", e);
          // 4. 回滚事务
            connection.rollback();
        }
    }

JDBC控制事务的API总体比较直观。

Spring的事务管理

可以从上面的JDBC示例中看到,事务管理最主要的几个操作就是:开启事务、提交事务、回滚事务。那么Spring是如何管理的呢?首先可以从不太常见的Spring编程式事务管理入手。

Spring编程式事务管理

我们可以通过PlatformTransactionManager来编程式开启事务,不过Spring提供了一个更加简便的TransactionTemplate

@Service
public class OrderService {
  	@Autowired
    private TransactionTemplate transactionTemplate;
  
    public void programmaticTransactionSaveTwoOrder(){
        transactionTemplate.executeWithoutResult( status -> {
            var o = newOrder();
            orderRepository.save(o);
            o = newOrder();
            orderRepository.save(o);
        });
    }
}

可以看到Spring已经简化了一部分事务管理的代码,比如:

  1. 不再需要自己关闭connection
  2. 不再需要自己开启、提交和回滚事务

并且在Spring中已经配置好的相关事务配置,也会继续起作用。但是相对来说,还有更常见也更简单的方法,那就是@Transactional注解。

Spring声明式(@Transactional注解)事务管理

更常见的事务管理如下:

@Transactional
public void saveTwoOrder(){
  var o = newOrder();
  orderRepository.save(o);

  o = newOrder();
  orderRepository.save(o);
}

直接在需要事务管理的方法上@Transactional方法就可以(如果是老项目,只使用了Spring而没有SpringBoot,需要再配置@EnableTransactionManagement注解)。

需要注意的是,@Transactional注解在private方法上是不生效的,如果使用intellij idea,那在使用这种错误方式的时候,IDE就会有相应的提示。

@Transactional注解还有事务传播级别属性需要注意,如下所示:

// 默认传播级别
@Transactional(propagation = Propagation.REQUIRED)

这个传播级别属性并不属于JDBC,而是属于Spring的事务管理。propagation属性值如下:

  • REQUIRED (默认):支持事务,如果当前不存在事务则新建一个
  • SUPPORTS:支持事务,如果当前不存在事务则直接执行
  • MANDATORY:支持事务,如果当前不存在事务则抛出异常
  • REQUIRES_NEW:创建新的事务,如果当前存在一个事务,则挂起
  • NOT_SUPPORTED:不支持事务,如果当前存在一个事务,则挂起
  • NEVER:不支持事务,如果当前存在一个事务,则抛出异常
  • NESTED:嵌套事务,如果当前存在一个事务,则嵌套在当前事务中(Save Point)

@Transactional注解使用注意事项

回滚策略

Spring默认只会对RuntimeException异常进行回滚,对检查异常Exception则不会回滚。

如果需要对检查异常也进行回滚,需要修改一下注解的属性:

@Transactional(rollbackFor = Exception.class)

没有被@Transactional注解的方法内部直接调用被@Transactional注解的方法

查看如下示例:

@Slf4j
@Service
public class OrderService {
  
    public void prepareAndSaveOrder(){
        log.info("prepare...");
        saveOrder(); //调用被@Transactional注解的方法
    }

    @Transactional
    public void saveOrder() {
        var o = newOrder();
        orderRepository.save(o);
    }
}

如果有人直接调用OrderService的prepareAndSaveOrder方法,那么在saveOrder上的注解就不会生效。这是因为如果直接内部调用saveOrder,就会绕过Spring的AOP机制,从而使@Transactional不生效。

不过也有一些技巧可以绕过这个限制,如注入Bean自身:

@Slf4j
@Service
public class OrderService {
  
  	@Autowired
    private OrderService orderService; // 注入当前Bean本身
  
    public void prepareAndSaveOrder(){
        log.info("prepare...");
        orderService.saveOrder(); // 通过注入的orderService调用saveOrder
    }
  
    @Transactional
    public void saveOrder() {
       // .....
    }
}

这时注意每次使用时,都必须使用orderService.来调用对应方法。

也可以直接在prepareAndSaveOrder方法上也加上@Transactional注解。

参考资料

[1]Spring文档:https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction

评论

此博客中的热门博文

国密SM2签名封装成PKCS7格式

在国内做金融行业,难免会有被强制使用国密算法的情况,而且一般还会指定必须使用硬件加密机之类的设备,所以我也稍微的研究了一下国密算法,使用软算法签名并封装 PKCS7 格式(文档中的一个交互)。 以下是基于 Bouncy Castle 的示例,密钥对的生成可以参考 Bouncy Castle 中 test 包下 SM2 相关代码 public static String sign ( ) throws Exception { //加载公钥 byte [ ] plainText = "hello, world" . getBytes ( ) ; FileInputStream input = new FileInputStream ( "F:\\certificate\\public.cer" ) ; CertificateFactory certificateFactory = new CertificateFactory ( ) ; X509Certificate certificate = ( X509Certificate ) certificateFactory . engineGenerateCertificate ( input ) ; input . close ( ) ; //加载私钥,private为换成实际的私钥 PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec ( "private" . getBytes ( ) ) ; //SM2算法实际上为ECC算法,并指定了一些参数值,所以这里的参数是EC KeyFactory factory = KeyFactory . getInstance ( "EC" , "BC" ) ; PrivateKey privateKey = factory . generatePrivate ( spec ) ; //以下为签名并封装成PKCS7格式 byte [ ] signedMessag

Spring Boot Actuator 2 示例

Welcome file 简介 Spring Boot Actuator为应用程序提供了各种开箱即用的运维特性,可以与应用方便的交互和监控。 使用环境:Java 11 和 Spring Boot 2.4.3.RELEASE 集成Spring Boot Actuator 在Spring Boot中集成Spring Boot Actuator与集成其他的框架类似,在 pom.xml 里引入相关的starter就可以: < dependency > < groupId > org.springframework.boot </ groupId > < artifactId > spring-boot-starter-actuator </ artifactId > </ dependency > < dependency > < groupId > org.springframework.boot </ groupId > < artifactId > spring-boot-starter-web </ artifactId > </ dependency > 由于大部分的使用场景还是web,所以这里也用Spring MVC做示例。 配置好 pom.xml 后,默认actuator仅暴露一些基本功能,实际使用中,根据需求暴露对应功能。为了简便测试,这里在 application.yml 中配置暴露全部功能: management : endpoints : web : exposure : include : "*" endpoint : health : enabled : true show-details : always probes : enabled : true shutdown : enabled : true metr

NextCloud数据目录迁移

最近服务器的环境坏了,所以迁移了NextCloud的数据目录。不过在迁移过程中有点小问题。 环境: Ubuntu 18.04 Docker 19.03.7 1.NextCloud页面不正常,Docker日志显示XX目录permission denied 参考了 这里 的做法,不过是把  /var/www/html/   整个目录的权限都修改为  chown -R www-data:www-data ,之后就不再报权限问题了。 2.数据库配置修改 因为NextCloud是在初始化时填的数据库连接信息,所以直接迁移数据目录的情况下,会导致应用连不到新的数据库环境。此时可以找到数据目录下的  config/config.php 文件,直接修改数据库连接配置。