Spring + Jta +JDBCTemplate 分布式事物實現(xiàn)方式

最近項目中需要用到多數(shù)據(jù)源管,數(shù)據(jù)訪問層采用的是JDBCTemplate去做的,一開始是在數(shù)據(jù)源這塊做了一個多數(shù)據(jù)源的操作類,通過攔截器注解去動態(tài)賦值指定的數(shù)據(jù)源操作,這種做法在查詢中是沒有問題的,但是DML操作時,會出現(xiàn)問題:事物中無法動態(tài)操作數(shù)據(jù)源,導(dǎo)致很多操作指針對第一個庫崭倘。查詢資料的時候發(fā)現(xiàn):
DataSourceTransactionManager這個事物管理類只針對單個數(shù)據(jù)源進行事物控制.

解決的方案也有多種,這里只提及我實踐過的兩種:
  1. 開啟多個數(shù)據(jù)源去進行操作,這種是可以自己去實現(xiàn)的.
  2. 利用JTA和Atomikos的多數(shù)據(jù)源分布事物管理

方案一:

思路:

  • 自定義一個注解類,通過傳遞參數(shù)告訴這個業(yè)務(wù)需要用到哪幾個數(shù)據(jù)源
  • 然后仿照Spring中的@Transactional的實現(xiàn)模式,去構(gòu)建一個集合來開啟多個事物
  • 然后通過攔截器去動態(tài)分配業(yè)務(wù)給這個集合告訴他,要開幾個事物

代碼:
applicationContext-datasource.xml

<!-- 數(shù)據(jù)源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}"/>
        <!-- 配置間隔多久才進行一次檢測司光,檢測需要關(guān)閉的空閑連接琅坡,單位是毫秒 -->
        <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>
    
    <!-- 數(shù)據(jù)源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}"/>
        <!-- 配置間隔多久才進行一次檢測坞淮,檢測需要關(guān)閉的空閑連接茴晋,單位是毫秒 -->
        <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 業(yè)務(wù)層配置文件

<!-- 一些掃描包或者其他定義的業(yè)務(wù)類 我這邊就不列入了 -->

<!-- 多數(shù)據(jù)源切面類 -->
    <bean id="multiTransactionalAspect" class="com.elab.execute.utils.MultiTransactionalAspect"/>
     多數(shù)據(jù)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 當(dāng)然你如果想做的更完善一點,可以參考@Transactional這個類,自己去做,什么傳播機制啊,隔離級別啊 等等,思路是一樣的
}

注解實現(xiàn)類 - 其實這里差不多都是沿用Spring的事物實現(xiàn)方式:

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: 多數(shù)據(jù)源動態(tài)管理
 * @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) {
            // 創(chuàng)建一個新的事物管理器,用來管理接下來要用到的事物
            DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
            // 根據(jù)注解中獲取到的數(shù)據(jù)標(biāo)識,從spring容器中去查找對應(yīng)的數(shù)據(jù)源
            DruidDataSource dataSource = (DruidDataSource) springUtils.getBean(beanName);
            //然后交給事物管理器去管理
            dataSourceTransactionManager.setDataSource(dataSource);
            // 定義一個新的事物定義
            DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
            // 設(shè)置一個默認的事物傳播機制,注意的是這里可以拓展注解中沒有用到的屬性
            // defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(defaultTransactionDefinition);
            // 將這個事物的定義放入Stack中
            /**
             * 其中為什么要用Stack來保存TransactionManager和TransactionStatus呢诺擅?
             * 那是因為Spring的事務(wù)處理是按照LIFO/stack behavior的方式進行的。
             * 如若順序有誤啡直,則會報錯:
             */
            transactionStatuStack.push(transactionStatus);
            dataSourceTransactionManagerStack
                    .push(dataSourceTransactionManager);
        }
        return true;
    }

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

    /**
     * 回滾事物方法實現(xiàn)
     *
     * @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實現(xiàn)類:


/**
 * 實現(xiàn)性Demo接口實現(xiàn)
 *
 * @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 這個注解很關(guān)鍵 - 必須是這個注解才能被攔截器攔截到
    @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("=====================結(jié)束處理MVP");

        demoService2.beidouMange();

//        System.out.println("業(yè)務(wù)處理完成,開始報錯");
        int i = 1 / 0;
    }

}

service 2實現(xiàn)

/**
 * @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("=====================結(jié)束處理北斗");
    }
}

dao實現(xiàn): 這里就只列入實現(xiàn)類

@Repository("beidouTestDao")
public class BeiDouTestDaoImpl extends BaseDao implements IDemoTestDao {
    
    
    /**
     * 我們這塊目前是有一些封裝的,不過你不用太關(guān)注;
     * 你只要把對應(yīng)的數(shù)據(jù)源指定好就行了
     */
    @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 {
    /**
     * 我們這塊目前是有一些封裝的,不過你不用太關(guān)注;
     * 你只要把對應(yīng)的數(shù)據(jù)源指定好就行了
     */
    @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>());
    }
}

另外有一個動態(tài)獲取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 : 這個方案會存在一定的問題 -> 就是如果你下一個另一個數(shù)據(jù)庫操作的業(yè)務(wù)serivce方法中如果定義了@Transactional它將會又開啟一個事物去執(zhí)行...

方案2 : 利用JTA+Atomikios實現(xiàn)事物管理,業(yè)務(wù)層代碼和上面差不多,主要是配置文件這塊.

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> <!-- 若不配置則代碼執(zhí)行"{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>
    
    <!-- 將數(shù)據(jù)源交給數(shù)據(jù)庫操作模版工具 -->
    <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事務(wù)管理器 -->
    <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 事務(wù)管理器 -->
    <bean id="springTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager" ref="atomikosTransactionManager"/>
        <property name="userTransaction" ref="atomikosUserTransaction"/>
        <!-- 必須設(shè)置烁涌,否則程序出現(xiàn)異常 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>
    <!-- 通過切入點去實現(xiàn)事物觸發(fā)機制 -->
    <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>

業(yè)務(wù)層代碼和方案一的差不多,看了一下大概的思路:

  1. 在調(diào)用被攔截器匹配的方法時,開啟一個新的事物

  2. 當(dāng)執(zhí)行到DML操作時,會獲取他對應(yīng)的數(shù)據(jù)源,并且會和當(dāng)前線程的事物管理器中的數(shù)據(jù)源進行匹配,如果不存在,則到連接池中獲取一個新的連接,并把這個連接放入當(dāng)前線程的事物管理器中進行管理

  3. 當(dāng)所有業(yè)務(wù)執(zhí)行完畢,并且沒有報錯的時候,會執(zhí)行一個兩階段提交的方式

    PREPARE TRANSACTION transaction_id PREPARE TRANSACTION 為當(dāng)前事務(wù)的兩階段提交做準(zhǔn)備。 在命令之后酒觅,事務(wù)就不再和當(dāng)前會話關(guān)聯(lián)了撮执;它的狀態(tài)完全保存在磁盤上, 它提交成功有非常高的可能性舷丹,即使是在請求提交之前數(shù)據(jù)庫發(fā)生了崩潰也如此抒钱。這條命令必須在一個用BEGIN顯式開始的事務(wù)塊里面使用。
    COMMIT PREPARED transaction_id 提交已進入準(zhǔn)備階段的ID為transaction_id的事務(wù)
    ROLLBACK PREPARED transaction_id 回滾已進入準(zhǔn)備階段的ID為transaction_id的事務(wù)

推薦使用第二種方式,因為它有比較好的連接池以及相對完善的機制,第一種考慮的情況比較少,會出現(xiàn)問題,當(dāng)然,你如果愿意折騰自己寫一套,可以參考一下..

以上為個人學(xué)習(xí)參考,有什么不足的地方歡迎指正.

參考博客:
Mybatis + JTA http://blog.csdn.net/zmx729618/article/details/54344296
分布式事物提交以及JTA的概念 : http://www.jasongj.com/big_data/two_phase_commit/
JTA一些實現(xiàn)原理:https://www.ibm.com/developerworks/cn/java/j-lo-jta/
兩階段事物提交:http://m635674608.iteye.com/blog/2322853

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市继效,隨后出現(xiàn)的幾起案子症杏,更是在濱河造成了極大的恐慌,老刑警劉巖瑞信,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厉颤,死亡現(xiàn)場離奇詭異,居然都是意外死亡凡简,警方通過查閱死者的電腦和手機逼友,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來秤涩,“玉大人帜乞,你說我怎么就攤上這事】鹁欤” “怎么了黎烈?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長匀谣。 經(jīng)常有香客問我照棋,道長,這世上最難降的妖魔是什么武翎? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任烈炭,我火速辦了婚禮,結(jié)果婚禮上宝恶,老公的妹妹穿的比我還像新娘符隙。我一直安慰自己,他們只是感情好垫毙,可當(dāng)我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布霹疫。 她就那樣靜靜地躺著,像睡著了一般露久。 火紅的嫁衣襯著肌膚如雪更米。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天毫痕,我揣著相機與錄音征峦,去河邊找鬼。 笑死消请,一個胖子當(dāng)著我的面吹牛栏笆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播臊泰,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蛉加,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起针饥,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤厂抽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后丁眼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體筷凤,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年苞七,在試婚紗的時候發(fā)現(xiàn)自己被綠了藐守。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蹂风,死狀恐怖卢厂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情惠啄,我是刑警寧澤慎恒,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站撵渡,受9級特大地震影響巧号,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜姥闭,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望越走。 院中可真熱鬧棚品,春花似錦、人聲如沸廊敌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽骡澈。三九已至锅纺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間肋殴,已是汗流浹背囤锉。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工寸士, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留钩骇,地道東北人辕宏。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓垦写,卻偏偏與公主長得像名党,于是被迫代替她去往敵國和親秩命。 傳聞我的和親對象是個殘疾皇子俐载,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容