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

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

最近项目中需要用到多数据源管,数据访问层采用的是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/

 

 

遇到问题:

1、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文件删除就可以了。

2、数据库突然断开连接,重新连接时,一直报错

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>

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值