【转帖】Atomikos 分布式事物实现方式

今天恰好项目碰到JDBCTemplate配置多数据源 ,可是事物中没法动态操做数据源,故度娘后发现该文章,为了后期方便本身查看,故转载了 http://blog.csdn.net/lkx444368875/article/details/76449760 。java

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

解决的方案也有多种,这里只说起我实践过的两种:mysql

  1. 开启多个数据源去进行操做,这种是能够本身去实现的.
  2. 利用JTA和Atomikos的多数据源分布事物管理

方案一:

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

代码: 
applicationContext-datasource.xmlspring

<!-- 数据源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 业务层配置文件sql

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

<!-- 多数据源切面类 -->
    <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的事物实现方式:express

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 :apache

/**
 * 一些实验性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实现类:api

/**
 * 实现性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/

 

 

遇到问题:

一、Log already in use?

com.atomikos.icatch.SysException: Error in init: Log already in use? tmlog in ./

根据官方回答为以下:

After a crash or improper VM shutdown, you may see an error like this upon startup:
ERROR: the specified log seems to be in use already. Make sure that no other instance is running, and then delete the file tmlog.lck 
java.lang.RuntimeException: Log already in use?
The lock file in question is created to protect the transaction logs against accidental duplicate startups. Otherwise, the logs could get corrupted when two instances of the same transaction manager are recovering on the same data. Normally, it suffices to follow the hints and then delete the lock file manually if needed.

Note: as of release 3.3, there are no more pending lock files after processes are terminated (not even in case of a VM crash or kill).

最残暴的解决方法就是在tomcat/bin 目录中找到.lck文件删除就能够了。

二、数据库忽然断开链接,从新链接时,一直报错

ERROR Atomikos:115 com.atomikos.datasource.xa.XAResourceTransaction:22 - XAResourceTransaction: 3139322E3136382E35362E312E746D313531353537343632303035303032313431:3139322E3136382E35362E312E746D31363836: no XAResource to commit?
ERROR Atomikos:115 com.atomikos.icatch.imp.CommitMessage:47 - Unexpected error in commit

如出现改问题,须要在datasource配置文件中配置 加入:<property name="testQuery" value="SELECT 1"></property>

<bean id="dataSource1" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
        <property name="uniqueResourceName" value="ds1"/>
        <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/>
        <property name="xaProperties">
            <props>
                <prop key="url">${center.jdbc.jdbcUrl}</prop>
                <prop key="user">${center.jdbc.user}</prop>
                <prop key="password">${center.jdbc.password}</prop>
                <prop key="pinGlobalTxToPhysicalConnection">true</prop>
            </props>
        </property>
        <property name="maxPoolSize" value="${center.jdbc.maxPoolSize}"></property>
        <property name="testQuery" value="SELECT 1"></property>
    </bean>

三、其余问题可见官方问题集 https://www.atomikos.com/Documentation/KnownProblems

相关文章
相关标签/搜索