spring-tx 事务

前言

数据准确性是每个后端开端必备要求之一。而关系数据库提供的事务《mysql事务》保证了这一点。spring框架也提供了spring-tx针对事务设计,让开发者自由接入。

快速搭建

引入spring-tx包,利用《spring-aop 扩展代理》中介绍的FactoryBean模式进业务对得事务代理。

    <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost/test"></property>
    </bean>
    
    <bean id="userService" class="mapper.UserSevice">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="transactionManager" class="utils.CustomTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <!-- 配置一个目标对象 -->
        <property name="target" ref="userService"/>
        <!-- 注入事务管理器 -->
        <property name="transactionManager" ref="transactionManager"/>
        <!-- 注入事务的一些属性 -->
        <property name="transactionAttributes">
            <props>
                <!-- 格式:
                    <prop key="方法">参数</prop>
					* PROPAGATION	:事务的传播行为
					* ISOLATION		:事务的隔离级别
					* readOnly		:只读.(不可以进行修改、插入、删除)
					* -Exception	:发生哪些异常回滚事务
					* +Exception	:发生哪些异常事务不回滚
					<prop key="insert*">PROPAGATION_REQUIRED</prop>
					<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
				-->
                <prop key="transfer">PROPAGATION_REQUIRED,+java.lang.ArithmeticException</prop>
            </props>
        </property>
    </bean>
//业务类
public class UserSevice {

    private DataSource dataSource;

    public void transfer() throws Exception {
        Object object = TransactionSynchronizationManager.getResource(dataSource);
        ConnectionHolder connectionHolder = (ConnectionHolder) object;
        Connection conn = connectionHolder.getConnection();
        Statement stat = conn.createStatement();
        int c = stat.executeUpdate("update users set type = 0 where id='65'");
        throw new RuntimeException("yoyo");
    }
}

public class ConnectionHolder extends ResourceHolderSupport {

	private final Connection connection;
	
	public ConnectionHolder(Connection connection) {
        this.connection = connection;
	}
	
	public Connection getConnection() {
		return this.connection;
	}
}

public class CustomTransactionObject {

    private ConnectionHolder connectionHolder;

    public void setConnectionHolder(@Nullable ConnectionHolder connectionHolder) {
        this.connectionHolder = connectionHolder;
    }

    public ConnectionHolder getConnectionHolder(){
       return this.connectionHolder;
    }
}

//自定义事务管理器
public class CustomTransactionManager extends AbstractPlatformTransactionManager {

    private DataSource dataSource;

    protected Object doGetTransaction() throws TransactionException {
        CustomTransactionObject txObject = new CustomTransactionObject();
        ConnectionHolder conHolder =
                (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
        txObject.setConnectionHolder(conHolder);
        return txObject;
    }

    protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException {
        {
            CustomTransactionObject txObject = (CustomTransactionObject) transaction;
            Connection con = null;

            try {

                if (txObject.getConnectionHolder() ==null ||
                        txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
                    Connection newCon = obtainDataSource().getConnection();
                    txObject.setConnectionHolder(new ConnectionHolder(newCon));
                }

                txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
                con = txObject.getConnectionHolder().getConnection();
                con.setAutoCommit(false);
                int timeout = determineTimeout(definition);
                if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
                    txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
                }

                TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());

            } catch (Throwable ex) {
                  con.close();
                
            }
        }

    }

    protected void doCommit(DefaultTransactionStatus status) throws TransactionException {
        CustomTransactionObject txObject = (CustomTransactionObject) status.getTransaction();
        Connection con = txObject.getConnectionHolder().getConnection();
        con.commit();   
        }
    }

    protected void doRollback(DefaultTransactionStatus status) throws TransactionException {
        CustomTransactionObject txObject = (CustomTransactionObject) status.getTransaction();
        Connection con = txObject.getConnectionHolder().getConnection();
       con.rollback();  
    }
    
    protected void doCleanupAfterCompletion(Object transaction) {
        CustomTransactionObject txObject = (CustomTransactionObject) transaction;
        TransactionSynchronizationManager.unbindResource(obtainDataSource());
        try {
            txObject.getConnectionHolder().getConnection().close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        txObject.getConnectionHolder().clear();
    }

    public  DataSource obtainDataSource() {
        return dataSource;
    }
}

ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("spring-tx.xml");
UserSevice userService = (UserSevice) context.getBean("accountServiceProxy");
userService.transfer();

核心概述

为了看懂以上的Demo,我们先了解下几个核心类

RuleBasedTransactionAttribute

事务基本信息配置类。
在这里插入图片描述

TransactionDefinition

事务的基本属性

public interface TransactionDefinition {

    //未提交读,值为1
    int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
    //提交读,值为2 
	int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
	//可重复读,值为4
	int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
	//可串行化,值为8
	int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
	//不设置,走数据库默认级别
	int ISOLATION_DEFAULT = -1;

    //返回事务隔离级别
	int getIsolationLevel();

    //是否只读事务
	boolean isReadOnly();

    /**------以下是spring对事务扩展的概念--------**/

	//若当前存在事务,则加入该事务,若不存在事务,则新建一个事务
	int PROPAGATION_REQUIRED = 0;
	//支持当前事务,若当前不存在事务,以非事务的方式执行。
	int PROPAGATION_SUPPORTS = 1;
	//强制事务执行,若当前不存在事务,则抛出异常
	int PROPAGATION_MANDATORY = 2;
	//若当前没有事务,则新建一个事务。若当前存在事务,则新建一个事务
	int PROPAGATION_REQUIRES_NEW = 3;
	//以非事务的方式执行,若当前存在事务,则把当前事务挂起。
	int PROPAGATION_NOT_SUPPORTED = 4;
	//以非事务的方式执行,如果当前存在事务,则抛出异常。
	int PROPAGATION_NEVER = 5;
	//如果当前存在事务,则嵌套在当前事务中执行。如果当前没有事务,则新建一个事务,类似于REQUIRE_NEW。mysql 使用savePoint来完成
	int PROPAGATION_NESTED = 6;

    //返回事务的传输级别 0-6 
	int getPropagationBehavior();

	//默认为无超时时间
	int TIMEOUT_DEFAULT = -1;
    //返回默认事务超时时间
	int getTimeout();

	//事务名称,用于监听器标识事务的来源应用,无具体作用
	String getName();
}
DefaultTransactionDefinition

对TransactionDefinition接口的基本实现

public class DefaultTransactionDefinition {

	//通过反射,把TransactionDefinition定义的值转为Map类型
	static final Constants constants = new Constants(TransactionDefinition.class);

    //设置默认的传播级别
	private int propagationBehavior = PROPAGATION_REQUIRED;
    
    //设置默认事务隔离级别
	private int isolationLevel = ISOLATION_DEFAULT;

    //事务超时时间
	private int timeout = TIMEOUT_DEFAULT;
   
    //是否只读事务
	private boolean readOnly = false;
     
    //事务名称,用于监听器标识事务的来源应用
	private String name;
}
TransactionAttribute

扩展了TransactionDefinition,增加了spring事务的功能点

public interface TransactionAttribute {

	//用于spring多manager,可根据qualifier值去容器中找到对应值
	String getQualifier();

	//回滚异常判断,有些异常可以不回滚直接提交
	boolean rollbackOn(Throwable ex);

}
DefaultTransactionAttribute

对TransactionAttribute进行实现,并对应的业务方法进行映射

public class DefaultTransactionAttribute  {

	//对应事务manager名称
	private String qualifier;

	//对应事务所在方法的标识,实现业务方法于配置的映射
	//@see AbstractFallbackTransactionAttributeSource.getTransactionAttribute
	private String descriptor;
}
DefaultTransactionAttribute

对rollbackOn的异常进行黑白名单的配置。如果二者都名单就以那个异常层级高为胜者

public class RuleBasedTransactionAttribute  {

	//白名单,以-为前缀的异常直接回滚
	//@see RollbackRuleAttribute
	public static final String PREFIX_ROLLBACK_RULE = "-";

	//黑名单,以+为异常的名字不回滚提交
	//@see NoRollbackRuleAttribute
	public static final String PREFIX_COMMIT_RULE = "+";

    // 回滚策略集合
    //@see TransactionAttributeEditor.setAsText
	private List<RollbackRuleAttribute> rollbackRules;
}
TransactionAttributeSource

主要实现切点与配置的映射。


public interface TransactionAttributeSource {

	//根据class-method获取方法上事务的配置
	//根据不同的代理方式,其映射实现不同
	TransactionAttribute getTransactionAttribute(Method method,  Class<?> targetClass);
}
NameMatchTransactionAttributeSource

FactoryBean方式采用的切点映射实现

public class NameMatchTransactionAttributeSource{
   
   // 方法class-method与配置的Map
   private Map<String, TransactionAttribute> nameMap = new HashMap<>();
 
  // 参数对应Demo中的transactionAttributes配置
  public void setProperties(Properties transactionAttributes) {
		TransactionAttributeEditor tae = new TransactionAttributeEditor();
		Enumeration<?> propNames = transactionAttributes.propertyNames();
		while (propNames.hasMoreElements()) {
		    //demo中key就是切点
			String methodName = (String) propNames.nextElement();
			//value就是配置行
			String value = transactionAttributes.getProperty(methodName);
			//通过TransactionAttributeEditor解析配置行
			tae.setAsText(value);
			TransactionAttribute attr = (TransactionAttribute) tae.getValue();
			//将解析好的配置缓存到nameMap中
			addTransactionalMethod(methodName, attr);
		}
	}
}
DefaultTransactionStatus

对应是每个cutpoint的事务状态
在这里插入图片描述

SavepointManager

mysql自带的savapoint功能, 实现 PROPAGATION_NESTED 嵌套事务

// @see java.sql.Savepoint
public interface SavepointManager {

	//创建建savepoint
	Object createSavepoint() throws TransactionException;

	//回滚事务到savepoint
	void rollbackToSavepoint(Object savepoint) throws TransactionException;

	//释放savepoint,相当于提交
	void releaseSavepoint(Object savepoint) throws TransactionException;
}
TransactionStatus

事务状态,每个pointcut对应一个状态

public interface TransactionStatus  {

	//是否针对这切面为新创事务
	boolean isNewTransaction();

	//是否有 savapoint
	boolean hasSavepoint();

	// 设置回滚标记
	void setRollbackOnly();

	// 当回滚标记为true, commit失效全部rollback
	// @see AbstractPlatformTransactionManager.commit
	boolean isRollbackOnly();

	// 把事务中内存修改的数据提交到数据库,一些orm框架有用到
	// 例如: Hibernate/JPA sessions
	void flush();

	// 事务是否已完成, commit/rollback 后
	boolean isCompleted();
}
AbstractTransactionStatus

对TransactionStatus的部分实现


public abstract class AbstractTransactionStatus  {
    // 本地事务回滚标记
	private boolean rollbackOnly = false;
    // 事务是否已完成
	private boolean completed = false;
	
	private Object savepoint;
}
DefaultTransactionStatus

对TransactionStatus的实现

public class DefaultTransactionStatus  {

	//真实的事务对象
	private final Object transaction;

    // 是否新建事务标识
	private final boolean newTransaction;

    // 是否事务状态同步, 相当于缓存
    // @see TransactionSynchronizationManager
	private final boolean newSynchronization;

    // 是否只读
	private final boolean readOnly;

	// 暂存事务状态, 用于2事务交互 
	// 每个cutpoint,如果有外部事务,会把部事务挂在此对象上
	// @see SuspendedResourcesHolder
	private final Object suspendedResources;
}
AbstractPlatformTransactionManager

事务管理器的基本实现
在这里插入图片描述

public abstract class AbstractPlatformTransactionManager  {

	// 一直保持事务同步,不管事务是否为空, PROPAGATION_SUPPORTS
	// @see newSynchronization
	public static final int SYNCHRONIZATION_ALWAYS = 0;

	// 如果事务为空,则不同步。PROPAGATION_SUPPORTS
    // @see newSynchronization
	public static final int SYNCHRONIZATION_ON_ACTUAL_TRANSACTION = 1;

	// 一直不同步,不管有没有事务
    // @see newSynchronization
	public static final int SYNCHRONIZATION_NEVER = 2;

    // 默认为地直保持事务同步
	private int transactionSynchronization = SYNCHRONIZATION_ALWAYS;

	// 把AbstractPlatformTransactionManager的常量,变成map形式
	private static final Constants constants = new Constants(AbstractPlatformTransactionManager.class);

    // 默认为事务不超时
	private int defaultTimeout = TransactionDefinition.TIMEOUT_DEFAULT;

    // 是否允许嵌套事务
	private boolean nestedTransactionAllowed = false;
    
    // 是否开启事务验证,如果外部事务和内部事务不兼容, PROPAGATION_REQUIRED or PROPAGATION_SUPPORTS
    // 如果为 false, 则直接忽略内部事务配置
	private boolean validateExistingTransaction = false;
    
    // 事务全局否标记回滚,和本地事务回滚相互作用
    // @see rollbackOnly
	private boolean globalRollbackOnParticipationFailure = true;

    // 如果提交成功被打 rollback = true 导致抛出UnexpectedRollbackException异常
    // 默认为false不抛出
	private boolean failEarlyOnGlobalRollbackOnly = false;

    // 提交失败是否回滚,否则触发 TransactionSynchronization 通知事务
	private boolean rollbackOnCommitFailure = false;

    
    // 根据配置属性获取事务状态
    // 判断是否使用已有事务
    // 如果已有事务
    //  PROPAGATION_NEVER 报错
    //  PROPAGATION_NOT_SUPPORTED 挂起已有事务,创建空事务
    //  PROPAGATION_REQUIRES_NEW  挂起已有事务, 创建事务
    //  PROPAGATION_NESTED        创建savepoint
    //  PROPAGATION_REQUIRED || PROPAGATION_SUPPORTS || PROPAGATION_MANDATORY 继续使用当前事务
    // 如果没有事务 
    //  PROPAGATION_MANDATORY 报错
    //  PROPAGATION_REQUIRED || PROPAGATION_REQUIRES_NEW || PROPAGATION_NESTED 创建新事务状态
    //  PROPAGATION_SUPPORTS || PROPAGATION_NOT_SUPPORTED || PROPAGATION_NEVER 创建空事务
    // @see suspend 挂起事务
    // @see prepareSynchronization 同步事务状态到全局 
	public final TransactionStatus getTransaction(TransactionDefinition definition);

	// 根据当前的事务状态提交事务
	// 如果本地标记了rollback, 则调用回滚方法
	// 触发事件 BeforeCommit || BeforeCompletion
	// 如果是savepoint 释放 savepoint
	// 如果是普通事务, 则手动提交
	// 触发事件 AfterCommit AfterCompletion
	// 如果有外部事务,则恢复
	// @see resume 恢复事务
	// @see trigger***
	void commit(TransactionStatus status) throws TransactionException;

    // 根据当前的事务状态回滚事务
	// 触发事件  BeforeCompletion
	// 如果是savepoint 回退到 savepoint
	// 如果是普通事务, 则手动回滚
	// 触发事件  AfterCompletion
	// 如果有外部事务,则恢复
	// @see resume 恢复事务
	// @see trigger***
	void rollback(TransactionStatus status) throws TransactionException;
}
ResourceHolderSupport

标识事务连接conn的状态

public abstract class ResourceHolderSupport implements ResourceHolder {

    // 是否已关系到事务对象
	private boolean synchronizedWithTransaction = false;

    // 是否rollback
	private boolean rollbackOnly = false;

	// 事务timeout时间
	private Date deadline;

    // 连接调用次数
	private int referenceCount = 0;

    // 是否事务连接失效
	private boolean isVoid = false;
}
TransactionSynchronizationManager

事务全局状态管理器

public abstract class TransactionSynchronizationManager {

    // key:线程+连接池  value: 事务连接ResourceHolder
    // 相当于一个事务下可以搞多个连接池的连接, 同时开启,同时提交,同时回滚,但还有很小可能造成数据不一致
	private static final ThreadLocal<Map<Object, Object>> resources =
			new NamedThreadLocal<>("Transactional resources");
    
    // key: 线程 value: 监听事务发的事件
    // 其生命周期跟着事务。
	// 在prepareSynchronization初始化之后,可调用registerSynchronization进行注册
	private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
			new NamedThreadLocal<>("Transaction synchronizations");

    // 当前线程正在执行的事务配置名
	// @see TransactionDefinition.getName		
	private static final ThreadLocal<String> currentTransactionName =
			new NamedThreadLocal<>("Current transaction name");

    // 当前线程正在执行事务是否只读事务
	// @see TransactionDefinition.isReadOnly		
	private static final ThreadLocal<Boolean> currentTransactionReadOnly =
			new NamedThreadLocal<>("Current transaction read-only status");

    // 当前线程正在执行事务隔离级别
	// @see TransactionDefinition.getIsolationLevel	
	private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
			new NamedThreadLocal<>("Current transaction isolation level");
    
    // 当前线程正在执行事务是否活跃
	// @see DefaultTransactionStatus.hasTransaction				
	private static final ThreadLocal<Boolean> actualTransactionActive =
			new NamedThreadLocal<>("Actual transaction active");
}

事务扩展

通过AbstractPlatformTransactionManager类的分析,发现spring-tx只是定义一个一个事务的流转过程,并不约束具体实现。其实大致可以分为以下三种:

  1. JDNI方式连接数据库,对应CciLocalTransactionManager
  2. JTA分布式事务规范,对应JtaTransactionManager
  3. DS直接使用Jdbc数据源方式,对应DataSourceTransactionManager

上述三种都需要对接第三方包,其中DataSourceTransactionManager在spring-jdbc包中实现

引入spring-jdbc包,demo进阶为

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
</bean>
public class UserSevice {

    private DataSource dataSource;

    public void transfer() throws Exception {
        Connection conn = DataSourceUtils.getConnection(dataSource);
        Statement stat = conn.createStatement();
        int c = stat.executeUpdate("update users set type = 0 where id='38'");
        throw new RuntimeException("yoyo");
    }
}
  1. DataSourceTransactionManager的原理和我们的CustomTransactionManager类似
  2. DataSourceUtils 也是辅助在业务代码中更方便的获取conn

另外jdbc包可引入JdbcTemplate把一些获取conn,stat的操作给隐藏掉

   <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="userService" class="mapper.UserSevice">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>

    </bean>
public class UserSevice {

    private JdbcTemplate jdbcTemplate;

    public void transfer() throws Exception {
        int c =jdbcTemplate.update("update users set type = 0 where id='38'");
        throw new RuntimeException("yoyo");
    }
}

调用链路

通过《spring-aop 扩展代理》的分析,其代码方式主要有2种。

FactoryBean方式

通过分析TransactionProxyFactoryBean类

// TransactionProxyFactoryBean实现了InitializingBean
// 初始化时触发afterPropertiesSet方法
public void afterPropertiesSet() {
        //创建代理工厂类
		ProxyFactory proxyFactory = new ProxyFactory();

		// TransactionInterceptor 做为Advisor
		// TransactionAttributeSourcePointcut 做为pointcut
		transactionAttributes 属性做判断
	    proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(createMainInterceptor()));

		// 设置代理目标对象
		TargetSource targetSource = createTargetSource(this.target);
		proxyFactory.setTargetSource(targetSource);

		// 创建代理对象,在getObject返回
		// 调用方法,通过TransactionAttributeSourcePointcut的matches方法判断是否代理,matches主要判断是根据transactionAttributes属性来
		// 如果方法合理,通过TransactionInterceptor的父类 TransactionAspectSupport.invokeWithinTransaction开始事务的判断
		this.proxy = proxyFactory.getProxy(this.proxyClassLoader);
}

Aspect方式

通过<tx:annotation-driven>的解析类AnnotationDrivenBeanDefinitionParser来分析

  1. 注册BeanFactoryTransactionAttributeSourceAdvisor Bean
  2. 设置1的transactionAttributeSource属性为AnnotationTransactionAttributeSource Bean
  3. 设置1的adviceBeanName属性为 TransactionInterceptor Bean 的名字
  4. 根据spring-context原理,spring会先加载Advisor
  5. 根据spring-aop原理,spring加载Bean时会取容器中Advisor进行pointcut判断matchs
  6. BeanFactoryTransactionAttributeSourceAdvisor 用的 pointcut 为 TransactionAttributeSourcePointcut,其matchs逻辑依赖 AnnotationTransactionAttributeSource
  7. AnnotationTransactionAttributeSource 是专门用来识别 @Transactional
  8. 如果matchs通过,则通过TransactionInterceptor的父类 TransactionAspectSupport.invokeWithinTransaction开始事务的判断
最终Demo形式
<tx:annotation-driven transaction-manager="transactionManager" />
public class UserSevice {
    private JdbcTemplate jdbcTemplate;

    @Transactional
    public void transfer() throws Exception {

        int c =jdbcTemplate.update("update users set type = 0 where id='38'");
        throw new RuntimeException("yoyo");
    }
}

主要参考

Spring事务实现原理及源码分析
Spring-tx模块分析

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值