【轉帖】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

相關文章
相關標籤/搜索