一、细分事务颗粒度 避免@Transactional的长事务工具类

package com.dj.property.util;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.function.Function;

/**
 * liuzehua spring 编程式事务工具类 细分事务颗粒度 避免长事务
 */
@Component
public class TransactionUtil {
    private static PlatformTransactionManager transactionManager;

    static Logger log = Logger.getLogger(TransactionUtil.class);

    @Autowired
    public TransactionUtil(PlatformTransactionManager transactionManager) {
        TransactionUtil.transactionManager = transactionManager;
    }

    /**
     * @param param    参数
     * @param function 函数式接口 p -> 引用要调用的方法 方法必须有返回值
     * @return 函数式接口的返回值
     */
    public static <T, R> R transactional(T param, Function<? super T, ? extends R> function) {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            R apply = function.apply(param);
            transactionManager.commit(status);
            return apply;
        } catch (Exception e) {
            transactionManager.rollback(status);
            log.error("编程式事务业务异常回滚", e);
            return null;
        }
    }

  /**
     *  可传两个参数
     * @param param 参数1
     * @param param2 参数2
     * @param function 函数式接口
     * @return  函数式接口的返回值
     */
    public static <T, U, R> R transactional2(T param, U param2, BiFunction<? super T, ? super U, ? extends R> function) {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            R apply = function.apply(param, param2);
            transactionManager.commit(status);
            return apply;
        } catch (Exception e) {
            transactionManager.rollback(status);
            log.error("编程式事务业务异常回滚", e);
            return null;
        }
    }

}

二、详解Spring的事务管理PlatformTransactionManager



(博客来源!!)

详解Spring的事务管理PlatformTransactionManager

常规的事务大致有许多种,比如jdbc事务, Hibernate的事务, JpaTransactionObject事务 关于他们的对比可以看看事务比较 我们直接看PlatformTransactionManager

Spring进行了统一的抽象,形成了PlatformTransactionManager事务管理器接口,事务的提交、回滚等操作全部交给它来实现。

Spring的事务体系也是在PlatformTransactionManager事务管理器接口上开展开来的(不管是JPA还是JDBC等都实现自接口 PlatformTransactionManager 如果你添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager实例。如果你添加的是 spring-boot-starter-data-jpa 依赖,框架会默认注入 JpaTransactionManager 实例。,所以先来了解下PlatformTransactionManager事务管理器。

事务功能的总体接口设计

先来看下三大接口,三个接口功能一句话总的来说事务管理器基于事务基础信息在操作事务时候对事务状态进行更新。

  • PlatformTransactionManager : 事务管理器
  • TransactionDefinition : 事务的一些基础信息,如超时时间、隔离级别、传播属性等
  • TransactionStatus : 事务的一些状态信息,如是否是一个新的事务、是否已被标记为回滚

一. 看下PlatformTransactionManager如何来操作事务:

public interface PlatformTransactionManager {
 
    //根据事务定义TransactionDefinition,获取事务
    TransactionStatus getTransaction(TransactionDefinition definition);
 
    //提交事务
    void commit(TransactionStatus status);
 
    //回滚事务
    void rollback(TransactionStatus status);
}

复制

二. 事务定义接口TransactionDefinition

  • 1.事务的定义包含:事务的隔离级别、事务的传播属性、超时时间设置、是否只读
    1. 红线上方是些常量定义,关于常量定义(事务的隔离级别和事务的传播属性等等) 具体事务常量定义
这里我们要明白的地方:

事务的隔离级别是数据库本身的事务功能,我们只是基于对数据库的Connection,对书屋操作做封装,而事务的传播属性则是Spring自己为我们提供的功能,数据库事务没有事务的传播属性这一说法。

DefaultTransactionDefinitio实现了该接口(TransactionDefinition):进行了一些默认的事务定义
public class DefaultTransactionDefinition implements TransactionDefinition, Serializable {
    private int propagationBehavior = PROPAGATION_REQUIRED;
    private int isolationLevel = ISOLATION_DEFAULT;
    private int timeout = TIMEOUT_DEFAULT;
    private boolean readOnly = false;
    //略
}

复制

  • 事务的传播属性PROPAGATION_REQUIRED,如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。被设置成这个级别时,会为每一个被调用的方法创建一个逻辑事务域。如果前面的方法已经创建了事务,那么后面的方法支持当前的事务,如果当前没有事务会重新建立事务,其他请看事务的传播属性
  • 事务的隔离级别 采用底层数据库默认的隔离级别
  • 超时时间 采用底层数据库默认的超时时间
  • 是否只读为false

三. 事务的状态信息定义TransactionStatus

先引出Connection连接中的保存点功能:

//创建一个保存点
conn.setSavepoint(name);
//回滚到某个保存点
conn.rollback(savepoint);
//释放某个保存点
conn.releaseSavepoint(savepoint);

复制

TransactionStatus它继承了SavepointManager接口,SavepointManager是对事务中上述保存点功能的封装,如下:

public interface SavepointManager {
    Object createSavepoint() throws TransactionException;
    void rollbackToSavepoint(Object savepoint) throws TransactionException;
    void releaseSavepoint(Object savepoint) throws TransactionException;
}

复制

Spring利用保存点功能实现了事务的嵌套功能。后面会详细说明。

至于我们说的TransactionStatus本身更多存储的是事务的一些状态信息:

  • 是否是一个新的事物
  • 是否有保存点
  • 是否已被标记为回滚

常用的TransactionStatus接口实现为DefaultTransactionStatus,真正用来操作事务的

目前jdbc事务是通过Connection来实现事务的,Hibernate是通过它自己定义的Transaction来实现的,所以各家的事务都不同,所以

Spring只能以Object transaction的形式来表示各家的事务,事务的回滚和提交等操作都会最终委托给上Object transaction来完成。

Object transaction的职责就是提交回滚事务,这个transaction的选择可能如下:

  • DataSourceTransactionObject
  • HibernateTransactionObject
  • JpaTransactionObject(之后再详细说)

详细信息分别如下:

  • 对于DataSourceTransactionObject: 我们使用了dataSource来获取连接,要想实现事务功能,必然需要使用Connection,所以它中肯定有一个Connection来执行事务的操作。 DataSourceTransactionObject中有一个ConnectionHolder,它封装了一个Connection
  • 对于HibernateTransactionObject: 我们使用了hibenrate,此时要想实现事务功能,必然需要通过hibernate自己定义的Transaction来实现。 HibernateTransactionObject中含有一个SessionHolder,和上面的ConnectionHolder一样,它封装了一个Session,有了Session,我们就可以通过Session来产生一个Hibernate的Transaction,从而实现事务操作。

四. 事务管理器接口定义PlatformTransactionManager

类图关系如下:

重点来说下

  • AbstractPlatformTransactionManager
    • DataSourceTransactionManager
    • HibernateTransactionManager
    • JpaTransactionManager(之后详细再说)

这就需要来看看事务管理器的接口,上述的他们都是怎么实现的:

  • 1 第一个接口:TransactionStatus getTransaction(TransactionDefinition definition) 根据事务定义获取事务状态 大体内容就是先获取上述说明的Object transaction,判断当前事务是否已存在,如果存在则进行事务的传播属性处理,后面详细说明,如果不存在new DefaultTransactionStatus,新创建一个事务,同时使用Object transaction开启事务。 分成了几个过程: 不同的事务管理器获取不同的Object transaction
    • Spring获取Object transaction: DataSourceTransactionManager就是获取上述的DataSourceTransactionObject

    从当前线程中获取绑定的ConnectionHolder,可能为null,如果为null,则会在下一个开 启事务的过程中,从dataSource中获取一个Connection,封装成ConnectionHolder,然后再绑定到当前线程 然后我们new 一个DataSourceTransactionObject了,具体过程如下:

构建DefaultTransactionStatus,使用Object transaction开启事务

DataSourceTransactionManager的DataSourceTransactionObject开启过程如下: 首先判断之前的获取当前线程绑定的ConnectionHolder是否为null,如果为null,从dataSource中获取一个Connection,封装成ConnectionHolder,然后再绑定到当前线程(通过ThreadLocal来实现,可以看我别的文章)

因为开启了一个事务,则必须要关闭DataSourceTransactionObject中Connection的自动提交,代码如下(省略一些):

protected void doBegin(Object transaction, TransactionDefinition definition) {
        DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
        Connection con = null;
 
        //如果ConnectionHolder是否为null,从新获取
        if (txObject.getConnectionHolder() == null ||
                txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
            Connection newCon = this.dataSource.getConnection();
            txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
        }
        con = txObject.getConnectionHolder().getConnection();
 
        Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
        txObject.setPreviousIsolationLevel(previousIsolationLevel);
 
        //取消自动提交
        if (con.getAutoCommit()) {
            txObject.setMustRestoreAutoCommit(true);
            if (logger.isDebugEnabled()) {
                logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
            }
            con.setAutoCommit(false);
        }
        txObject.getConnectionHolder().setTransactionActive(true);
 
 
        //如果是新增的ConnectionHolder,则绑定到当前线程
        if (txObject.isNewConnectionHolder()) {
            TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
        }
    }

复制

第二个接口:void rollback(TransactionStatus status) 回滚事务

回滚,则还是利用DefaultTransactionStatus内部的Object transaction来执行回滚操作

DataSourceTransactionManager就是使用DataSourceTransactionObject中的Connection来进行回滚操作

protected void doRollback(DefaultTransactionStatus status) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
    Connection con = txObject.getConnectionHolder().getConnection();
    try {
        con.rollback();
    }
    catch (SQLException ex) {
        throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
    }
}

复制

第三个接口: void commit(TransactionStatus status) 提交事务

同理,DataSourceTransactionManager依托内部的Connection来完成提交操作

这里对于使用提供一个小demo

比如我们现在涉及到一个付款成功的业务,涉及到数据库金额更新和数据库订单状态数据更新,那么前端发送一个请求到我们的controller,我们在controller做向上反馈,在service做事务管理的业务操作以及数据库操作

@PostMapping("moneyOperation")
    public String moneyOperation() {
        if (transactionOperation.moneyOperation()) {
            return "付款成功";
        }
        else {
            return "付款失败!";
        }
    }

复制

/**
 * @description: money相关事务demo
 * @author: zyh
 * @create: 2021-06-23 14:06
 **/
@Service
@Slf4j
@RequiredArgsConstructor
public class TransactionOperation {
    private final PlatformTransactionManager transactionManager;


    public boolean moneyOperation() {
        TransactionStatus status;
        // 手动开启事务初始化
        status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        //操作
        try {
            // 数据库操作后(例如业务上需先更新金额,再更新订单信息)
            moneyDaoOperation();
            DefaultTransactionDefinition
            // 操作无异常:提交事务
            transactionManager.commit(status);
            log.debug("操作xxxx成功");
            return true;
        } catch (Exception e) {
            log.debug("操作xxxx成功出错,正在回滚,错误信息为:"+e.getMessage());
            // 捕获异常, 事务回滚
            transactionManager.rollback(status);
            log.debug("操作xxxx已回滚");
            return false;
        }
    }
}

复制

参考https://blog.csdn.net/luzhensmart/article/details/90167871 ``

事务的失效场景

https://mp.weixin.qq.com/s/wrs5rUlFKdemU6m_QUYCZg

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐