Spring + Jta +JDBCTemplate 分布式事物实现方式

最近项目中需要用到多数据源管,数据访问层采用的是JDBCTemplate去做的,一开始是在数据源这块做了一个多数据源的操作类,通过拦截器注解去动态赋值指定的数据源操作,这种做法在查询中是没有问题的,但是DML操作时,会出现问题:事物中无法动态操作数据源,导致很多操作指针对第一个库。查询资料的时候发现:
DataSourceTransactionManager这个事物管理类只针对单个数据源进行事物控制.

解决的方案也有多种,这里只提及我实践过的两种:
  1. 开启多个数据源去进行操作,这种是可以自己去实现的.
  2. 利用JTA和Atomikos的多数据源分布事物管理

方案一:

思路:

  • 自定义一个注解类,通过传递参数告诉这个业务需要用到哪几个数据源
  • 然后仿照Spring中的@Transactional的实现模式,去构建一个集合来开启多个事物
  • 然后通过拦截器去动态分配业务给这个集合告诉他,要开几个事物

代码:
applicationContext-datasource.xml

<!-- 数据源1 -->
 <bean id="mvp" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="driverClassName" value="${mvp.driverClassName}"></property>
        <property name="url" value="${mvp.url}"></property>
        <property name="username" value="${mvp.username}"></property>
        <property name="password" value="${mvp.password}"></property>
        <property name="filters" value="${mvp.filters}"/>
        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="${mvp.initialSize}"/>
        <property name="minIdle" value="${mvp.minIdle}"/>
        <property name="maxActive" value="${mvp.maxActive}"/>
        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="${mvp.maxWait}"/>
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="${mvp.timeBetweenEvictionRunsMillis}"/>
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="${mvp.minEvictableIdleTimeMillis}"/>
        <property name="testWhileIdle" value="${mvp.testWhileIdle}"/>
        <property name="testOnBorrow" value="${mvp.testOnBorrow}"/>
        <property name="testOnReturn" value="${mvp.testOnReturn}"/>
        <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
        <property name="poolPreparedStatements" value="${mvp.poolPreparedStatements}"/>
        <property name="maxPoolPreparedStatementPerConnectionSize" value="20"/>
        <property name="removeAbandoned" value="${mvp.removeAbandoned}" />
        <property name="removeAbandonedTimeout" value="${mvp.removeAbandonedTimeout}" />
        <property name="maxOpenPreparedStatements" value="${mvp.maxOpenPreparedStatements}" />
        <property name="logAbandoned" value="${mvp.logAbandoned}"/>
        <property name="queryTimeout" value="${mvp.querytimeout}"/>
        <property name="validationQuery" value="${mvp.validationQuery}"/>
    </bean>
    
    <!-- 数据源2 -->
    <bean id="system" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="driverClassName" value="${system.driverClassName}"></property>
        <property name="url" value="${system.url}"></property>
        <property name="username" value="${system.username}"></property>
        <property name="password" value="${system.password}"></property>
        <property name="filters" value="${system.filters}"/>
        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="${system.initialSize}"/>
        <property name="minIdle" value="${system.minIdle}"/>
        <property name="maxActive" value="${system.maxActive}"/>
        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="${system.maxWait}"/>
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="${system.timeBetweenEvictionRunsMillis}"/>
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="${system.minEvictableIdleTimeMillis}"/>
        <property name="testWhileIdle" value="${system.testWhileIdle}"/>
        <property name="testOnBorrow" value="${system.testOnBorrow}"/>
        <property name="testOnReturn" value="${system.testOnReturn}"/>
        <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
        <property name="poolPreparedStatements" value="${system.poolPreparedStatements}"/>
        <property name="maxPoolPreparedStatementPerConnectionSize" value="20"/>
        <property name="removeAbandoned" value="${system.removeAbandoned}" />
        <property name="removeAbandonedTimeout" value="${system.removeAbandonedTimeout}" />
        <property name="maxOpenPreparedStatements" value="${system.maxOpenPreparedStatements}" />
        <property name="logAbandoned" value="${system.logAbandoned}"/>
        <property name="queryTimeout" value="${system.querytimeout}"/>
        <property name="validationQuery" value="${system.validationQuery}"/>
    </bean>
    
    <!-- dao层访问处理工具 分别指定两个不同的datasource -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="default"/>
    </bean>

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

applicationContext-service.xml 业务层配置文件

<!-- 一些扫描包或者其他定义的业务类 我这边就不列入了 -->

<!-- 多数据源切面类 -->
    <bean id="multiTransactionalAspect" class="com.elab.execute.utils.MultiTransactionalAspect"/>
     多数据Aop配置 
    <aop:config proxy-target-class="true">
        <!-- 定义一个切入点表达式: 拦截哪些方法 -->
        <aop:aspect ref="multiTransactionalAspect">
            <aop:pointcut id="pointUserMgr" expression="execution(* com.test.execute.services..*(..))"/>
            <aop:around method="around" pointcut-ref="pointUserMgr" />
        </aop:aspect>
    </aop:config>

注解类 :

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MultiTransactional {

 
    String[] values() default ""; 
    
    // TODO 当然你如果想做的更完善一点,可以参考@Transactional这个类,自己去做,什么传播机制啊,隔离级别啊 等等,思路是一样的
}

注解实现类 - 其实这里差不多都是沿用Spring的事物实现方式:

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.lang.reflect.Method;
import java.util.Stack;

/**
 * @Description: 多数据源动态管理
 * @Author: Liukx on 2017/7/28 - 16:41
 */
@Component("multiTransactionalAspect")
public class MultiTransactionalAspect {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private SpringUtils springUtils;

    /**
     * 切入点
     *
     * @param point
     * @return
     * @throws Throwable
     */
    public Object around(ProceedingJoinPoint point) throws Throwable {
        Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack = new Stack<DataSourceTransactionManager>();
        Stack<TransactionStatus> transactionStatuStack = new Stack<TransactionStatus>();

        try {
            Object target = point.getTarget();
            String method = point.getSignature().getName();
            Class classz = target.getClass();
            Class[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes();
            Method m = classz.getMethod(method, parameterTypes);
            if (m != null && m.isAnnotationPresent(MultiTransactional.class)) {
                MultiTransactional multiTransactional = m.getAnnotation(MultiTransactional.class);
                if (!openTransaction(dataSourceTransactionManagerStack, transactionStatuStack, multiTransactional)) {
                    return null;
                }
                Object ret = point.proceed();
                commit(dataSourceTransactionManagerStack, transactionStatuStack);
                return ret;
            }
            Object ret = point.proceed();
            return ret;
        } catch (Exception e) {
            rollback(dataSourceTransactionManagerStack, transactionStatuStack);
            logger.error(String.format(
                    "MultiTransactionalAspect, method:%s-%s occors error:", point
                            .getTarget().getClass().getSimpleName(), point
                            .getSignature().getName()), e);
            throw e;
        }
    }

    /**
     * 打开一个事物方法
     *
     * @param dataSourceTransactionManagerStack
     * @param transactionStatuStack
     * @param multiTransactional
     * @return
     */
    private boolean openTransaction(
            Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
            Stack<TransactionStatus> transactionStatuStack,
            MultiTransactional multiTransactional) {
        // 获取注解中要打开的事物类型
        String[] transactionMangerNames = multiTransactional.values();
        if (ArrayUtils.isEmpty(multiTransactional.values())) {
            return false;
        }

        for (String beanName : transactionMangerNames) {
            // 创建一个新的事物管理器,用来管理接下来要用到的事物
            DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
            // 根据注解中获取到的数据标识,从spring容器中去查找对应的数据源
            DruidDataSource dataSource = (DruidDataSource) springUtils.getBean(beanName);
            //然后交给事物管理器去管理
            dataSourceTransactionManager.setDataSource(dataSource);
            // 定义一个新的事物定义
            DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
            // 设置一个默认的事物传播机制,注意的是这里可以拓展注解中没有用到的属性
            // defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(defaultTransactionDefinition);
            // 将这个事物的定义放入Stack中
            /**
             * 其中为什么要用Stack来保存TransactionManager和TransactionStatus呢?
             * 那是因为Spring的事务处理是按照LIFO/stack behavior的方式进行的。
             * 如若顺序有误,则会报错:
             */
            transactionStatuStack.push(transactionStatus);
            dataSourceTransactionManagerStack
                    .push(dataSourceTransactionManager);
        }
        return true;
    }

    /**
     * 提交事物方法实现
     *
     * @param dataSourceTransactionManagerStack
     * @param transactionStatuStack
     */
    private void commit(
            Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
            Stack<TransactionStatus> transactionStatuStack) {
        while (!dataSourceTransactionManagerStack.isEmpty()) {
            dataSourceTransactionManagerStack.pop().commit(
                    transactionStatuStack.pop());
        }
    }

    /**
     * 回滚事物方法实现
     *
     * @param dataSourceTransactionManagerStack
     * @param transactionStatuStack
     */
    private void rollback(
            Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
            Stack<TransactionStatus> transactionStatuStack) {
        while (!dataSourceTransactionManagerStack.isEmpty()) {
            dataSourceTransactionManagerStack.pop().rollback(
                    transactionStatuStack.pop());
        }
    }
}

service 1 - 2 :

/**
 * 一些实验性Demo接口
 *
 * @author Liukx
 * @create 2017-07-28 10:11
 * @email liukx@elab-plus.com
 **/
public interface IDemoService {

    public void mvpManage() throws Exception;

}

/**
 * 一些实验性Demo接口
 *
 * @author Liukx
 * @create 2017-07-28 10:11
 * @email liukx@elab-plus.com
 **/
public interface IDemoService2 {

    public void beidouMange() throws CoreException;

}

service实现类:


/**
 * 实现性Demo接口实现
 *
 * @author Liukx
 * @create 2017-07-28 10:12
 * @email liukx@elab-plus.com
 **/
@Service("demoService")
public class DemoServiceImpl implements IDemoService {

    @Autowired
    @Qualifier("demoTestDao")
    private IDemoTestDao testDao;

    @Autowired
    private IDemoService2 demoService2;

//    @Autowired
//    private DataSourceTransactionManager transactionManager;

    //TODO 这个注解很关键 - 必须是这个注解才能被拦截器拦截到
    @MultiTransactional(values = {DataSource.mvp, DataSource.system})
    public void mvpManage() throws CoreException {
        System.out.println("=====================开始处理MVP");
        testDao.insert();
        List<Map<String, Object>> select = testDao.select();
        System.out.println("===============>>>>>>>>>>>>>>>>>" + select.size());
        System.out.println("=====================结束处理MVP");

        demoService2.beidouMange();

//        System.out.println("业务处理完成,开始报错");
        int i = 1 / 0;
    }

}

service 2实现

/**
 * @author Liukx
 * @create 2017-07-28 11:07
 * @email liukx@elab-plus.com
 **/
@Service("demo2")
public class DemoServiceImpl2 implements IDemoService2 {
    @Autowired
    @Qualifier("beidouTestDao")
    private IDemoTestDao testDao;

    @MultiTransactional(values = DataSource.system)
    public void beidouMange() throws CoreException {
        System.out.println("=====================开始处理北斗");
        testDao.insert();
        List<Map<String, Object>> select = testDao.select();
        System.out.println("========================================>" + select.size());
        System.out.println("=====================结束处理北斗");
    }
}

dao实现: 这里就只列入实现类

@Repository("beidouTestDao")
public class BeiDouTestDaoImpl extends BaseDao implements IDemoTestDao {
    
    
    /**
     * 我们这块目前是有一些封装的,不过你不用太关注;
     * 你只要把对应的数据源指定好就行了
     */
    @Autowired
    @Qualifier("jdbcTemplate2")
    public JdbcTemplate jdbcTemplate;

    public int insert() throws CoreException {
        LinkedHashMap<String, Object> params = new LinkedHashMap<>();
        params.put("name", "某某某" + RandomUtils.randomNum(3));
        params.put("created", new Date());
        return executeInsert("test.insert", params);
    }

    @Override
    public List<Map<String, Object>> select() throws CoreException {
        return findList("test.findAll", new LinkedHashMap<String, Object>());
    }
}
@Repository("demoTestDao")
public class DemoTestDaoImpl extends BaseDao implements IDemoTestDao {
    /**
     * 我们这块目前是有一些封装的,不过你不用太关注;
     * 你只要把对应的数据源指定好就行了
     */
    @Autowired
    @Qualifier("jdbcTemplate")
    public JdbcTemplate jdbcTemplate;

    public int insert() throws CoreException {
        LinkedHashMap<String, Object> params = new LinkedHashMap<>();
        params.put("name", "某某某" + RandomUtils.randomNum(2));
        params.put("created", new Date());
        return executeInsert("test.insert", params);
    }

    public List<Map<String, Object>> select() throws CoreException {
        return findList("test.findAll", new LinkedHashMap<String, Object>());
    }
}

另外有一个动态获取bean的工具类:

@Component
public class SpringUtils implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public Object getBean(String beanId) {
        return applicationContext.getBean(beanId);
    }
}

PS : 这个方案会存在一定的问题 -> 就是如果你下一个另一个数据库操作的业务serivce方法中如果定义了@Transactional它将会又开启一个事物去执行...

方案2 : 利用JTA+Atomikios实现事物管理,业务层代码和上面差不多,主要是配置文件这块.

applicationContext-datasource.xml

<!-- 父配置 -->
<bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init"
          destroy-method="close" abstract="true">
        <property name="xaDataSourceClassName"
                  value="com.alibaba.druid.pool.xa.DruidXADataSource"/>  <!-- SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase, Hana]  -->
        <property name="poolSize" value="10"/>
        <property name="minPoolSize" value="10"/>
        <property name="maxPoolSize" value="30"/>
        <property name="borrowConnectionTimeout" value="60"/>
        <property name="reapTimeout" value="20"/>
        <property name="maxIdleTime" value="60"/>
        <property name="maintenanceInterval" value="60"/>
        <property name="loginTimeout" value="60"/>
        <property name="testQuery" value="${default.validationQuery}"/>
    </bean>

    <!-- 主配置 -->
    <bean id="masterDataSource" parent="abstractXADataSource">
        <property name="uniqueResourceName" value="masterDB"/>
        <property name="xaProperties">
            <props>
                <prop key="driverClassName">${default.driverClassName}</prop>
                <prop key="url">${default.url}</prop>
                <prop key="password">${default.password}</prop>
                <!--  <prop key="user">${jdbc.username}</prop> --> <!-- mysql -->
                <prop key="username">${default.username}</prop>   <!-- durid -->
                <prop key="initialSize">0</prop>
                <prop key="maxActive">20</prop> <!-- 若不配置则代码执行"{dataSource-1} inited"此处停止  -->
                <prop key="minIdle">0</prop>
                <prop key="maxWait">60000</prop>
                <prop key="validationQuery">${default.validationQuery}</prop>
                <prop key="testOnBorrow">false</prop>
                <prop key="testOnReturn">false</prop>
                <prop key="testWhileIdle">true</prop>
                <prop key="removeAbandoned">true</prop>
                <prop key="removeAbandonedTimeout">1800</prop>
                <prop key="logAbandoned">true</prop>
                <prop key="filters">mergeStat</prop>
            </props>
        </property>
    </bean>

    <bean id="slaveDataSource" parent="abstractXADataSource">
        <property name="uniqueResourceName" value="slaveDB"/>
        <property name="xaProperties">
            <props>
                <prop key="driverClassName">${system.driverClassName}</prop>
                <prop key="url">${system.url}</prop>
                <prop key="password">${system.password}</prop>
                <!--  <prop key="user">${jdbc.username}</prop> -->
                <prop key="username">${system.username}</prop>
                <prop key="initialSize">0</prop>
                <prop key="maxActive">20</prop>
                <prop key="minIdle">0</prop>
                <prop key="maxWait">60000</prop>
                <prop key="validationQuery">${system.validationQuery}</prop>
                <prop key="testOnBorrow">false</prop>
                <prop key="testOnReturn">false</prop>
                <prop key="testWhileIdle">true</prop>
                <prop key="removeAbandoned">true</prop>
                <prop key="removeAbandonedTimeout">1800</prop>
                <prop key="logAbandoned">true</prop>
                <prop key="filters">mergeStat</prop>
            </props>
        </property>
    </bean>
    
    <!-- 将数据源交给数据库操作模版工具 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="masterDataSource"/>
    </bean>

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

applicationContext-atomikos.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" default-lazy-init="true">

    <description>配置事物</description>
    <!-- atomikos事务管理器 -->
    <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init"
          destroy-method="close">
        <property name="forceShutdown">
            <value>true</value>
        </property>
    </bean>

    <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
        <property name="transactionTimeout" value="300"/>
    </bean>
    <!-- spring 事务管理器 -->
    <bean id="springTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager" ref="atomikosTransactionManager"/>
        <property name="userTransaction" ref="atomikosUserTransaction"/>
        <!-- 必须设置,否则程序出现异常 JtaTransactionManager does not support custom isolation levels by default -->
        <property name="allowCustomIsolationLevels" value="true"/>
    </bean>

    <aop:config proxy-target-class="true">
        <aop:pointcut id="pointUserMgr" expression="execution(* com.elab.execute.services..*(..))"/>
        <aop:advisor pointcut-ref="pointUserMgr" advice-ref="txAdvice"/>
    </aop:config>
    <!-- 通过切入点去实现事物触发机制 -->
    <tx:advice id="txAdvice" transaction-manager="springTransactionManager">
        <tx:attributes>
            <tx:method name="get*" propagation="REQUIRED" read-only="true"/>
            <tx:method name="find*" propagation="REQUIRED" read-only="true"/>
            <tx:method name="has*" propagation="REQUIRED" read-only="true"/>
            <tx:method name="locate*" propagation="REQUIRED" read-only="true"/>
            <tx:method name="mvp*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
            <tx:method name="beidou*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
        </tx:attributes>
    </tx:advice>
    <!--<tx:annotation-driven transaction-manager="springTransactionManager"-->
    <!--proxy-target-class="true"></tx:annotation-driven>-->
</beans>

pom.xml

<!-- JTA -->
        <dependency>
            <groupId>javax.transaction</groupId>
            <artifactId>jta</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>com.atomikos</groupId>
            <artifactId>atomikos-util</artifactId>
            <version>4.0.2</version>
        </dependency>
        <dependency>
            <groupId>com.atomikos</groupId>
            <artifactId>transactions</artifactId>
            <version>4.0.2</version>
        </dependency>
        <dependency>
            <groupId>com.atomikos</groupId>
            <artifactId>transactions-jta</artifactId>
            <version>4.0.2</version>
        </dependency>
        <dependency>
            <groupId>com.atomikos</groupId>
            <artifactId>transactions-jdbc</artifactId>
            <version>4.0.2</version>
        </dependency>
        <dependency>
            <groupId>com.atomikos</groupId>
            <artifactId>transactions-api</artifactId>
            <version>4.0.2</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib-nodep</artifactId>
            <version>3.2.2</version>
        </dependency>

业务层代码和方案一的差不多,看了一下大概的思路:

  1. 在调用被拦截器匹配的方法时,开启一个新的事物

  2. 当执行到DML操作时,会获取他对应的数据源,并且会和当前线程的事物管理器中的数据源进行匹配,如果不存在,则到连接池中获取一个新的连接,并把这个连接放入当前线程的事物管理器中进行管理

  3. 当所有业务执行完毕,并且没有报错的时候,会执行一个两阶段提交的方式

    PREPARE TRANSACTION transaction_id PREPARE TRANSACTION 为当前事务的两阶段提交做准备。 在命令之后,事务就不再和当前会话关联了;它的状态完全保存在磁盘上, 它提交成功有非常高的可能性,即使是在请求提交之前数据库发生了崩溃也如此。这条命令必须在一个用BEGIN显式开始的事务块里面使用。
    COMMIT PREPARED transaction_id 提交已进入准备阶段的ID为transaction_id的事务
    ROLLBACK PREPARED transaction_id 回滚已进入准备阶段的ID为transaction_id的事务

推荐使用第二种方式,因为它有比较好的连接池以及相对完善的机制,第一种考虑的情况比较少,会出现问题,当然,你如果愿意折腾自己写一套,可以参考一下..

以上为个人学习参考,有什么不足的地方欢迎指正.

参考博客:
Mybatis + JTA http://blog.csdn.net/zmx729618/article/details/54344296
分布式事物提交以及JTA的概念 : http://www.jasongj.com/big_data/two_phase_commit/
JTA一些实现原理:https://www.ibm.com/developerworks/cn/java/j-lo-jta/
两阶段事物提交:http://m635674608.iteye.com/blog/2322853

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容