zoukankan      html  css  js  c++  java
  • 编码式事务

    1.编程式事务:编码方式实现事务管理(代码演示为JDBC事务管理)

    Spring实现编程式事务,依赖于2大类,分别是上篇文章提到的PlatformTransactionManager,与模版类TransactionTemplate(推荐使用)。下面分别详细介绍Spring是如何通过该类实现事务管理。

    1)PlatformTransactionManager在springboot中的使用

    2)PlatformTransactionManager
    Spring在事务管理时,对事务的处理做了极致的抽象,即PlatformTransactionManager。对事务的操作,简单地来说,只有三步操作:获取事务,提交事务,回滚事务。
    public interfacePlatformTransactionManager{
        // 获取事务
        TransactionStatus getTransaction(@Nullable TransactionDefinition definition)throws TransactionException;
        // 提交事务
        voidcommit(TransactionStatus status)throws TransactionException;
        // 回滚事务
        voidrollback(TransactionStatus status)throws TransactionException;
     
    }

    当然Spring不会仅仅只提供一个接口,同时会有一个抽象模版类,实现了事务管理的具体骨架。AbstractPlatformTransactionManager类可以说是Spring事务管理的控制台,决定事务如何创建,提交和回滚。

    在Spring事务管理(二)-TransactionProxyFactoryBean原理中,分析TransactionInterceptor增强时,在invoke方法中最重要的三个操作:

    创建事务 createTransactionIfNecessary
    异常后事务处理 completeTransactionAfterThrowing
    方法执行成功后事务提交 commitTransactionAfterReturning
    在具体操作中,最后都是通过事务管理器PlatformTransactionManager的接口实现来执行的,其实也就是上面列出的三个接口方法。我们分别介绍这三个方法的实现,并以DataSourceTransactionManager为实现类观察JDBC方式事务的具体实现。

    1. 获取事务
    getTransaction方法根据事务定义来获取事务状态,事务状态中记录了事务定义,事务对象及事务相关的资源信息。对于事务的获取,除了调用事务管理器的实现来获取事务对象本身外,另外的很重要的一点是处理了事务的传播方式。

    public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)throws TransactionException {
      // 1.获取事务对象
      Object transaction = doGetTransaction();
     
      // Cache debug flag to avoid repeated checks.
      boolean debugEnabled = logger.isDebugEnabled();
     
      if (definition == null) {
        // Use defaults if no transaction definition given.
        definition = new DefaultTransactionDefinition();
      }
     
      // 2.如果已存在事务,根据不同的事务传播方式处理获取事务
      if (isExistingTransaction(transaction)) {
        // Existing transaction found -> check propagation behavior to find out how to behave.
        return handleExistingTransaction(definition, transaction, debugEnabled);
      }
     
      // Check definition settings for new transaction.
      if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
        throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
      }
     
      // 3. 如果当前没有事务,不同的事务传播方式不同处理方式
      // 3.1 事务传播方式为mandatory(强制必须有事务),则抛出异常
      // No existing transaction found -> check propagation behavior to find out how to proceed.
      if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
        throw new IllegalTransactionStateException(
            "No existing transaction found for transaction marked with propagation 'mandatory'");
      }
      // 3.2 事务传播方式为required或required_new或nested(嵌套),创建一个新的事务状态
      else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
          definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
          definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
        SuspendedResourcesHolder suspendedResources = suspend(null);
        if (debugEnabled) {
          logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
        }
        try {
          boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
          // 创建新的事务状态对象
          DefaultTransactionStatus status = newTransactionStatus(
              definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
          // 事务初始化
          doBegin(transaction, definition);
          // 准备其他同步操作
          prepareSynchronization(status, definition);
          return status;
        }
        catch (RuntimeException | Error ex) {
          resume(null, suspendedResources);
          throw ex;
        }
      }
      // 3.3 其他事务传播方式,返回一个事务对象为null的事务状态对象
      else {
        // Create "empty" transaction: no actual transaction, but potentially synchronization.
        if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
          logger.warn("Custom isolation level specified but no actual transaction initiated; " +
              "isolation level will effectively be ignored: " + definition);
        }
        boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
        return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
      }
    }

    获取事务的方法主要做两件事情:

    1. 获取事务对象
    2. 根据事务传播方式返回事务状态对象

    获取事务对象,在DataSourceTransactionManager的实现中,返回一个DataSourceTransactionObject对象

    protected Object doGetTransaction(){
    DataSourceTransactionObject txObject = new DataSourceTransactionObject();
    txObject.setSavepointAllowed(isNestedTransactionAllowed());
    ConnectionHolder conHolder =
    (ConnectionHolder) 
    // 从事务同步管理器中根据DataSource获取数据库连接资源    
    TransactionSynchronizationManager.getResource(obtainDataSource());
    txObject.setConnectionHolder(conHolder, false);
    return txObject;
    }

    每次执行doGetTransaction方法,即会创建一个DataSourceTransactionObject对象txObject,并从事务同步管理器中根据DataSource获取数据库连接持有对象ConnectionHolder,然后存入txObject中。**事务同步管理类持有一个ThreadLocal级别的resources对象,存储DataSource和ConnectionHolder的映射关系。**因此返回的txObject中持有的ConnectionHolder可能有值,也可能为空。而不同的事务传播方式下,事务管理的处理根据txObejct中是否存在事务有不同的处理方式。

    关于关注事务传播方式的实现,很多人对事务传播方式都是一知半解,只是因为没有了解源码的实现。现在就来看看具体的实现。事务传播方式的实现分为两种情况,事务不存在和事务已经存在。isExistingTransaction方法判断事务是否存在,默认在AbstractPlatformTransactionManager抽象类中返回false,而在DataSourceTransactionManager实现中,则根据是否有数据库连接来决定。

    protectedbooleanisExistingTransaction(Object transaction){
      DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
      return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
    }
    事务管理器配置
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="jdbcUrl" value="${db.jdbcUrl}" />
        <property name="user" value="${user}" />
        <property name="password" value="${password}" />
        <property name="driverClass" value="${db.driverClass}" />
         <!--连接池中保留的最小连接数。 --> 
         <property name="minPoolSize"> 
             <value>5</value> 
         </property> 
         <!--连接池中保留的最大连接数。Default: 15 --> 
         <property name="maxPoolSize"> 
             <value>30</value> 
         </property> 
         <!--初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 --> 
         <property name="initialPoolSize"> 
             <value>10</value> 
         </property> 
         <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 --> 
         <property name="maxIdleTime"> 
             <value>60</value> 
         </property> 
         <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 --> 
         <property name="acquireIncrement"> 
             <value>5</value> 
         </property> 
         <!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements 属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。  如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0 --> 
         <property name="maxStatements"> 
             <value>0</value> 
         </property> 
         <!--每60秒检查所有连接池中的空闲连接。Default: 0 --> 
         <property name="idleConnectionTestPeriod"> 
             <value>60</value> 
         </property> 
         <!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 --> 
         <property name="acquireRetryAttempts"> 
             <value>30</value> 
         </property> 
         <!--获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效 保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。Default: false --> 
         <property name="breakAfterAcquireFailure"> 
             <value>true</value> 
         </property> 
         <!--因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的 时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable等方法来提升连接测试的性能。Default: false --> 
         <property name="testConnectionOnCheckout"> 
             <value>false</value> 
         </property> 
    </bean>
    <!--DataSourceTransactionManager位于org.springframework.jdbc.datasource包下,数据源事务管理类,提供对单个javax.sql.DataSource数据源的事务管理,主要用于JDBC,Mybatis框架事务管理。 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    业务中使用代码(以测试类展示)
    import java.util.Map;
    import javax.annotation.Resource;
    import javax.sql.DataSource;
    import org.apache.log4j.Logger;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.springframework.transaction.PlatformTransactionManager;
    import org.springframework.transaction.TransactionDefinition;
    import org.springframework.transaction.TransactionStatus;
    import org.springframework.transaction.support.DefaultTransactionDefinition;
     
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = { "classpath:spring-public.xml" })
    public class test {
        @Resource
        private PlatformTransactionManager txManager;
        @Resource
        private  DataSource dataSource;
        private static JdbcTemplate jdbcTemplate;
        Logger logger=Logger.getLogger(test.class);
        private static final String INSERT_SQL = "insert into testtranstation(sd) values(?)";
        private static final String COUNT_SQL = "select count(*) from testtranstation";
        @Test
        public void testdelivery(){
            //定义事务隔离级别,传播行为,
            DefaultTransactionDefinition def = new DefaultTransactionDefinition();  
            def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);  
            def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);  
            //事务状态类,通过PlatformTransactionManager的getTransaction方法根据事务定义获取;获取事务状态后,Spring根据传播行为来决定如何开启事务
            TransactionStatus status = txManager.getTransaction(def);  
            jdbcTemplate = new JdbcTemplate(dataSource);
            int i = jdbcTemplate.queryForInt(COUNT_SQL);  
            System.out.println("表中记录总数:"+i);
            try {  
                jdbcTemplate.update(INSERT_SQL, "1");  
                txManager.commit(status);  //提交status中绑定的事务
            } catch (RuntimeException e) {  
                txManager.rollback(status);  //回滚
            }  
            i = jdbcTemplate.queryForInt(COUNT_SQL);  
            System.out.println("表中记录总数:"+i);
        }
        
    }

     2.TransactionTemplate(推荐使用)

    TransactionTemplate模板类使用的回调接口:

    • TransactionCallback:通过实现该接口的“T doInTransaction(TransactionStatus status) ”方法来定义需要事务管理的操作代码;
    • TransactionCallbackWithoutResult:继承TransactionCallback接口,提供“void doInTransactionWithoutResult(TransactionStatus status)”便利接口用于方便那些不需要返回值的事务操作代码。

    还是以测试类方式展示如何实现

    @Test
    public void testTransactionTemplate(){
        jdbcTemplate = new JdbcTemplate(dataSource);
        int i = jdbcTemplate.queryForInt(COUNT_SQL);  
        System.out.println("表中记录总数:"+i);
        //构造函数初始化TransactionTemplate
        TransactionTemplate template = new TransactionTemplate(txManager);
        template.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);  
        //重写execute方法实现事务管理
        template.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                jdbcTemplate.update(INSERT_SQL, "饿死");   //字段sd为int型,所以插入肯定失败报异常,自动回滚,代表TransactionTemplate自动管理事务
            }}
        );
        i = jdbcTemplate.queryForInt(COUNT_SQL);  
        System.out.println("表中记录总数:"+i);
    }



  • 相关阅读:
    C#深入浅出 修饰符(二)
    HDU 5785 Interesting
    HDU 5783 Divide the Sequence
    HDU 5781 ATM Mechine
    UVA 714 Copying Books
    uva 1471 Defense Lines
    UVA 11134 Fabled Rooks
    UVA 11572 Unique Snowflakes
    UVA 11093 Just Finish it up
    UVA 10954 Add All
  • 原文地址:https://www.cnblogs.com/lukelook/p/11133402.html
Copyright © 2011-2022 走看看