天天看點

Spring分布式事務在service中動态切換資料源Spring分布式事務在service中動态切換資料源

項目采用的是struts2的+彈簧+ ibatis的架構,下面是關鍵部分代碼:

applicationContext.xml中:

<b>[html] </b>檢視純文字

&lt;?xml version = “1.0” encoding = “UTF-8” ?&gt;

&lt; 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:aop = “http://www.springframework.org/schema/aop”

       xmlns:tx = “http://www.springframework.org/schema/tx”

       xsi:schemaLocation =“http://www.springframework.org/schema/beans

           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

           http://www.springframework.org/schema/context

           http://www.springframework.org/schema/context/spring-context-2.5.xsd

           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd

           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd“

           default-autowire = “byName” default-lazy-init = “false” &gt;

    &lt; context:component-scan base-package = “com.ssi。*” /&gt;

    &lt;! - 屬性檔案讀入 - &gt;

    &lt; bean id = “propertyConfigurer” class = “org.springframework.beans.factory.config.PropertyPlaceholderConfigurer” &gt;&gt;

        &lt; property name = “locations” &gt;

            &lt; list &gt;

                &lt; value &gt; classpath *:jdbc.properties &lt;/ value &gt;

            &lt;/ list &gt;

        &lt;/ property &gt;

    &lt;/ bean &gt;

    &lt;! - 配置sqlMapclient - &gt;

    &lt; bean id = “sqlMapClient” class = “org.springframework.orm.ibatis.SqlMapClientFactoryBean” &gt;

        &lt; property name = “configLocation” value = “classpath:ibatis-sqlmap-config.xml” /&gt;

        &lt; property name = “dataSource” ref = “dataSource” /&gt;

    &lt; bean id = “sqlMapClient1” class = “org.springframework.orm.ibatis.SqlMapClientFactoryBean” &gt;

        &lt; property name = “dataSource” ref = “db1” /&gt;

    &lt; bean id = “sqlMapClient2” class = “org.springframework.orm.ibatis.SqlMapClientFactoryBean” &gt;

        &lt; property name = “dataSource” ref = “db2” /&gt;

    &lt; bean id = “sqlMapClientCenter” class = “org.springframework.orm.ibatis.SqlMapClientFactoryBean” &gt;

        &lt; property name = “dataSource” ref = “center” /&gt;

    &lt; bean id = “dynamicSqlMapClientDaoSupport” class = “com.ssi.dao.DynamicSqlClientDaoSupport” &gt;&gt;

        &lt; property name = “targetSqlMapClients” &gt;

            &lt; map &gt;

                &lt; entry key = “db1” value-ref = “sqlMapClient1” /&gt;

                &lt; entry key = “db2” value-ref = “sqlMapClient2” /&gt;

                &lt; entry key = “center” value-ref = “sqlMapClientCenter” /&gt;

            &lt;/ map &gt;

        &lt; property name = “defaultSqlMapClient” ref = “sqlMapClientCenter” /&gt;

    &lt; bean id = “ibatisDaoSupport” class = “com.ssi.dao.IbatisDaoSupport” parent = “dynamicSqlMapClientDaoSupport” &gt; &lt;/bean &gt;

    &lt; bean id = “userDao” class = “com.ssi.dao.impl.UserDaoImpl” parent = “ibatisDaoSupport” &gt; &lt;/ bean &gt;

    &lt;! - 支援@AspectJ标記 - &gt;

    &lt; aop:aspectj-autoproxy proxy-target-class = “true” /&gt;

    &lt;! - 配置JTA的事務管理器 - &gt;

    &lt; bean id = “atomikosTransactionManager” class = “com.atomikos.icatch.jta.UserTransactionManager” init-method = “init” destroy-method = “close” &gt;

        &lt; property name = “forceShutdown” value = “true” /&gt;

    &lt; bean id = “atomikosUserTransaction” class = “com.atomikos.icatch.jta.UserTransactionImp” &gt;

        &lt; property name = “transactionTimeout” value = “300” /&gt;

    &lt; bean id = “springTransactionManager” class = “org.springframework.transaction.jta.JtaTransactionManager”&gt;

        &lt; property name = “transactionManager” ref = “atomikosTransactionManager” /&gt;

        &lt; property name = “userTransaction” ref = “atomikosUserTransaction” /&gt;

    &lt;! - 配置通知 - &gt;

    &lt; tx:advice id = “txAdvice” transaction-manager = “springTransactionManager” &gt;

        &lt; tx:attributes &gt;

             &lt; tx:method name = “*” rollback-for = “Exception,RuntimeException,com.ssi.exception.SystemException” propagation = “REQUIRED” /&gt;

        &lt;/ tx:attributes &gt;

    &lt;/ tx:advice &gt;

    &lt;! - 以AspectJ方式定義AOP - &gt;

    &lt; aop:config &gt;

        &lt; aop:advisor pointcut = “execution(* com.ssi.service .. * Service *。*(..))” advice-ref = “txAdvice”/&gt;

    &lt;/ aop:config &gt;

    &lt;! - spring定時器任務開始 - &gt;

    &lt; bean name = “job” class = “org.springframework.scheduling.quartz.JobDetailBean” &gt;

         &lt; property name = “jobClass” &gt;

             &lt; value &gt; com.ssi.action.TimerAction &lt;/ value &gt;

         &lt;/ property &gt;

         &lt; property name = “jobDataAsMap” &gt;

             &lt; map &gt;

                  &lt;! - timeout屬性設定了當伺服器啟動後過10秒鐘首次調用你的JobAction - &gt;

                  &lt; entry key = “timeout” &gt;

                     &lt; value &gt; 10 &lt;/ value &gt;

                  &lt;/ entry &gt;

             &lt;/ map &gt;

    &lt; bean id = “cronTrigger” class = “org.springframework.scheduling.quartz.CronTriggerBean” &gt;

         &lt; property name = “jobDetail” &gt;

             &lt; ref bean = “job” /&gt;

         &lt; property name = “cronExpression” &gt;

             &lt; 值&gt; 0 53 15?* MON-FRI &lt;/ value &gt;

    &lt; bean class = “org.springframework.scheduling.quartz.SchedulerFactoryBean” autowire = “no” &gt;

         &lt; property name = “triggers” &gt;

             &lt; list &gt;

                 &lt; ref local = “cronTrigger” /&gt;

             &lt;/ list &gt;

    &lt;! - spring定時器任務結束 - &gt;

&lt;/ beans &gt;

的applicationContext-datasource.xml

    xmlns:xsi = “http://www.w3.org/2001/XMLSchema-instance”

    xmlns:aop = “http://www.springframework.org/schema/aop”

    xmlns:tx = “http://www.springframework.org/schema/tx”

    xsi:schemaLocation =“

    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd

    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd“ &gt;

    &lt;! - 指定春天配置中用到的屬性檔案 - &gt;

    &lt; bean id = “propertyConfig” class = “org.springframework.beans.factory.config.PropertyPlaceholderConfigurer”&gt;&gt;

                &lt; value &gt; classpath:jdbc.properties &lt;/ value &gt;

    &lt;! - JTA資料源配置 - &gt;

    &lt; bean id = “center” class = “com.atomikos.jdbc.AtomikosDataSourceBean” init-method = “init” destroy-method = “close” &gt;

        &lt; property name = “uniqueResourceName” &gt;

            &lt; 值&gt; mysql / center &lt;/ value &gt;

        &lt; property name = “xaDataSourceClassName” &gt;

            &lt; value &gt; $ {jta.driver.className} &lt;/ value &gt;

        &lt; property name = “xaProperties” &gt;

            &lt; 道具&gt;

                &lt; prop key = “url” &gt; $ {center.jdbc.driver.url} &lt;/ prop &gt;

                &lt; prop key = “user” &gt; $ {center.sql.user.name} &lt;/ prop &gt;

                &lt; prop key = “password” &gt; $ {center.sql.user.password} &lt;/ prop &gt;

            &lt;/ 道具&gt;

        &lt; property name = “testQuery” value = “select 1” /&gt;

        &lt; property name = “poolSize” &gt;

            &lt; value &gt; $ {poolsize} &lt;/ value &gt;

        &lt; property name = “maxPoolSize” &gt;

            &lt; value &gt; $ {maxPoolSize} &lt;/ value &gt;

        &lt; property name = “borrowConnectionTimeout” &gt; &lt; value &gt; $ {borrowConnectionTimeout} &lt;/ value &gt; &lt;/ property &gt;

    &lt; bean id = “db1” class = “com.atomikos.jdbc.AtomikosDataSourceBean” init-method = “init” destroy-method = “close”&gt;

            &lt; value &gt; mysql / db1 &lt;/ value &gt;

                &lt; prop key = “url” &gt; $ {db1.jdbc.driver.url} &lt;/ prop &gt;

                &lt; prop key = “user” &gt; $ {company.sql.user.name} &lt;/ prop &gt;

                &lt; prop key = “password” &gt; $ {company.sql.user.password} &lt;/ prop &gt;

    &lt; bean id = “db2” class = “com.atomikos.jdbc.AtomikosDataSourceBean” init-method = “init” destroy-method = “close”&gt;

            &lt; value &gt; mysql / db2 &lt;/ value &gt;

                &lt; prop key = “url” &gt; $ {db2.jdbc.driver.url} &lt;/ prop &gt;

    &lt; bean id = “dataSource” class = “com.ssi.datasource.DynamicDataSource” &gt;

        &lt; property name = “targetDataSources” &gt;

            &lt; map key-type = “java.lang.String” &gt;

                &lt; entry key = “db1” value-ref = “db1” /&gt;

                &lt; entry key = “db2” value-ref = “db2” /&gt;

                &lt; entry key = “center” value-ref = “center” /&gt;

        &lt; property name = “defaultTargetDataSource” ref = “center” /&gt;

DynamicSqlClientDaoSupport.java

<b>[java] </b>檢視純文字

包 com.ssi.dao;

import  java.util.Map;

import  javax.sql.DataSource;

import  org.springframework.beans.factory.InitializingBean;

import  org.springframework.dao.support.DaoSupport;

import  org.springframework.orm.ibatis.SqlMapClientTemplate;

import  org.springframework.util.Assert;

import  com.ibatis.sqlmap.client.SqlMapClient;

import  com.ssi.datasource.DbContextHolder;

公共類 DynamicSqlClientDaoSupport  擴充 DaoSupport  實作 InitializingBean {

    私人 SqlMapClientTemplate sqlMapClientTemplate =  新的 SqlMapClientTemplate();

    私人 地圖&lt;String,SqlMapClient&gt; targetSqlMapClients;

    私人 SqlMapClient的defaultSqlMapClient;

    private boolean  externalTemplate =  false ;

    / **

     *設定此DAO使用的JDBC資料源。

     *不需要:SqlMapClient可能攜帶共享資料源。

     * @see #setSqlMapClient

     * /

    public final void  setDataSource(DataSource dataSource){

        如果 (!this .externalTemplate){

        這個.sqlMapClientTemplate.setDataSource(dataSource);

        }

    }

     *傳回此DAO使用的JDBC資料源。

    public final  DataSource getDataSource(){

        傳回這個.sqlMapClientTemplate.getDataSource();

     *設定iBATIS資料庫層SqlMapClient使用。

     *這個或者“sqlMapClientTemplate”是必需的。

     * @see #setSqlMapClientTemplate

    public final void  setSqlMapClient(SqlMapClient sqlMapClient){

            這個.sqlMapClientTemplate.setSqlMapClient(sqlMapClient);

     *傳回該模闆使用的iBATIS資料庫層SqlMapClient。

    public final  SqlMapClient getSqlMapClient(){

        傳回這個.sqlMapClientTemplate.getSqlMapClient();

     *明确地為此DAO設定SqlMapClientTemplate,

     *作為指定SqlMapClient的替代方法。

    public final void  setSqlMapClientTemplate(SqlMapClientTemplate sqlMapClientTemplate){

        Assert.notNull(sqlMapClientTemplate,  “SqlMapClientTemplate不能為空” );

        這個.sqlMapClientTemplate = sqlMapClientTemplate;

        這個.externalTemplate =  true ;

     *傳回此DAO的SqlMapClientTemplate,

     *用SqlMapClient預先初始化或明确設定。

    public final  SqlMapClientTemplate getSqlMapClientTemplate(){

      字元串dbtype = DbContextHolder.getDbType();

      if (targetSqlMapClients!= null &amp;&amp; targetSqlMapClients.containsKey(dbtype)){

          SqlMapClient sqlMapClient = targetSqlMapClients.get(dbtype);

          sqlMapClientTemplate =  新的 SqlMapClientTemplate(sqlMapClient);

      }

      傳回這個.sqlMapClientTemplate;

    @覆寫

    protected final void  checkDaoConfig(){

            這個.sqlMapClientTemplate.afterPropertiesSet();

    public  Map &lt;String,SqlMapClient&gt; getTargetSqlMapClients(){

        傳回 targetSqlMapClients;

    public void  setTargetSqlMapClients(Map &lt;String,SqlMapClient&gt; targetSqlMapClients){

        這個.targetSqlMapClients = targetSqlMapClients;

    public  SqlMapClient getDefaultSqlMapClient(){

        傳回 defaultSqlMapClient;

    public void  setDefaultSqlMapClient(SqlMapClient defaultSqlMapClient){

        這個.defaultSqlMapClient = defaultSqlMapClient;

}

IbatisDaoSupport.java

import  java.io.Serializable;

import  java.sql.SQLException;

import  java.util.List;

import  org.apache.commons.logging.Log;

import  org.apache.commons.logging.LogFactory;

import  org.springframework.orm.ibatis.SqlMapClientCallback;

import  com.ibatis.sqlmap.client.SqlMapExecutor;

@SuppressWarnings (“未選中” )

公共類 IbatisDaoSupport &lt;實體&gt;  擴充 DynamicSqlClientDaoSupport  實作 IEntityDao &lt;實體&gt; {

    受保護的最終 日志日志= LogFactory.getLog(getClass());

    公共 實體get(String sqlId,Serializable id){

        return  (Entity)getSqlMapClientTemplate()。queryForObject(sqlId,id);

    公共 實體getByParamMap(字元串sqlId,對象參數){

        return  (Entity)getSqlMapClientTemplate()。queryForObject(sqlId,param);

    public  Object save(String sqlId,Object o){

        傳回 getSqlMapClientTemplate()。insert(sqlId,o);

    public  Object batchSave(final  String sqlId,final  List &lt;Entity&gt; entityList)  throws  Exception {

        //執行回調   

        傳回 getSqlMapClientTemplate()。execute(新的 SqlMapClientCallback(){

            //實作回調接口   

            公共 對象doInSqlMapClient(SqlMapExecutor執行器)  throws  SQLException {

                //開始批處理   

                executor.startBatch();

                for  (Entity entity:entityList){

                    executor.insert(sqlId,entity);

                }

                return  executor.executeBatch();

            }

        });

    public  Integer remove(String sqlId,Object o){

        傳回 getSqlMapClientTemplate()。delete(sqlId,o);

    public  Integer removeById(String sqlId,Serializable id){

        傳回 getSqlMapClientTemplate()。delete(sqlId,id);

    public  Integer update(String sqlId,Object o){

        傳回 getSqlMapClientTemplate()。update(sqlId,o);

    public  long totalCount(String sqlId,Object o){

        return  (Long)getSqlMapClientTemplate()。queryForObject(sqlId,o);

    public  List &lt;Entity&gt; pagedList(String sqlId,Map &lt;String,Object&gt; map,int  pageSize,  int  pageNum){

        int  start =(pageNum -  1 )* pageSize;

        map.put(“開始” ,開始);

        map.put(“pageSize” ,pageSize);

        List &lt;Entity&gt; list = getSqlMapClientTemplate()。queryForList(sqlId,map);

        退貨 清單;

    public  List &lt;Entity&gt; list(String sqlId,Object o){

        傳回 getSqlMapClientTemplate()。queryForList(sqlId,o);

    public  List &lt;Entity&gt; list(String sqlId){

        傳回 getSqlMapClientTemplate()。queryForList(sqlId);

UserDaoImpl.java:

包 com.ssi.dao.impl;

import  org.springframework.stereotype.Repository;

import  com.ssi.dao.IUserDao;

import  com.ssi.dao.IbatisDaoSupport;

import  com.ssi.model.User;

@Repository (“userDAO的” )

公共類 UserDaoImpl  擴充 IbatisDaoSupport &lt;使用者&gt;  實作 IUserDao {

    公共 整數addUser(使用者使用者)  抛出 異常{

        傳回 (整數)  這個.save(“User.insert” ,使用者);

UserServiceImpl.java

包 com.ssi.service.impl;

import  javax.annotation.Resource;

import  org.springframework.stereotype.Service;

import  com.ssi.service.IUserService;

@Service (“userService” )

公共類 UserServiceImpl  實作 IUserService {

    @Resource private  IUserDao userDao;

     *測試在服務中切換資料源異常是否復原

    公共無效 addUser(使用者使用者)  抛出 異常{

        DbContextHolder.setDbType(“db1” );

        userDao.addUser(使用者);

        DbContextHolder.setDbType(“db2” );

        user.setUserName(“user2” );

        DbContextHolder.setDbType(“center” );

        user.setUserName(“user3” );

        //System.out.println(1/0);

DynamicDataSource.java:

public class  DynamicDataSource  extends  AbstractRoutingDataSource {

    靜态 記錄儀日志= Logger.getLogger(DynamicDataSource。類);

    protected  Object determineCurrentLookupKey(){

        傳回 DbContextHolder.getDbType();

DbContextHolder.java:

公共類 DbContextHolder {

    private static final  ThreadLocal contextHolder =  new  ThreadLocal();

    public static void  setDbType(String dbType){

        contextHolder.set(DBTYPE);

    public static  String getDbType(){

        return  (String)contextHolder.get();

    public static void  clearDbType(){

        contextHolder.remove();

三個資料庫:dbcenter,db1,db2表結構均相同

腳本:

<b>[sql] </b>檢視純文字

DROP TABLE  IF EXISTS`tb_user`;

CREATE TABLE  `tb_user`(

  `id`  int (11)  NOT NULL  AUTO_INCREMENT,

  `userName`  varchar (20)  DEFAULT NULL ,

  ` 密碼`  VARCHAR (60)  DEFAULT NULL ,

  PRIMARY KEY  (`id`)

)ENGINE = InnoDB  DEFAULT  CHARSET = utf8;

單元測試:

公共類 JunitTest {

    public  ApplicationContext cxt;

    @測試

    公共無效的 init()  抛出 異常{

        cxt =  new  ClassPathXmlApplicationContext(new  String [] { “applicationContext.xml” ,“applicationContext-datasource.xml” });

        testInsertUser();

    私人無效 testInsertUser()  抛出 異常{

        IUserService userService =(IUserService)cxt.getBean(“userService” );

        使用者使用者=  新 使用者();

        user.setUserName(“user1” );

        user.setPassword(“0” );

        userService.addUser(使用者);

    私人無效 testInsertUser2()  抛出 異常{