使用JTOM实现多数据源的分布式事务管理

使用spring和mybatis可以很方便的实现一个数据源的事务管理,但是如果需要同时对多个数据源进行事务控制,并且不想使用重量级容器提供的机制的话,可以使用JOTM达到目的.

下面介绍JTOM整合spring实现多数据源动态切换及分布式事务管理

1.首先,如果不是maven项目需加入jtom整合需要的jar包并buildpath

2.首先创建 JotmFactoryBean.java

package com.crs.ticket.utils.jotmutil;



import javax.naming.NamingException;
import javax.transaction.SystemException;

import org.objectweb.jotm.Current;
import org.objectweb.jotm.Jotm;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;

/**
 * @author pypua
 */
@SuppressWarnings("rawtypes")
public class JotmFactoryBean implements FactoryBean, DisposableBean {

    private Current jotmCurrent;

    private Jotm jotm;

    public JotmFactoryBean() throws NamingException {
        // Check for already active JOTM instance.
        this.jotmCurrent = Current.getCurrent();

        // If none found, create new local JOTM instance.
        if (this.jotmCurrent == null) {
            // Only for use within the current Spring context:
            // local, not bound to registry.
            this.jotm = new Jotm(true, false);
            this.jotmCurrent = Current.getCurrent();
        }
    }

    public void setDefaultTimeout(int defaultTimeout) {
        this.jotmCurrent.setDefaultTimeout(defaultTimeout);
        // The following is a JOTM oddity: should be used for demarcation
        // transaction only,
        // but is required here in order to actually get rid of JOTM's default
        // (60 seconds).
        try {
            this.jotmCurrent.setTransactionTimeout(defaultTimeout);
        } catch (SystemException ex) {
            // should never happen
        }
    }

    public Jotm getJotm() {
        return this.jotm;
    }

    public Object getObject() {
        return this.jotmCurrent;
    }

    public Class getObjectType() {
        return this.jotmCurrent.getClass();
    }

    public boolean isSingleton() {
        return true;
    }

    public void destroy() {
        if (this.jotm != null) {
            this.jotm.stop();
        }
    }

}

2.创建CustomSqlSessionTemplate.java

package com.crs.ticket.utils.jotmutil;
  
import static java.lang.reflect.Proxy.newProxyInstance;
import static org.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable;
import static org.mybatis.spring.SqlSessionUtils.closeSqlSession;
import static org.mybatis.spring.SqlSessionUtils.getSqlSession;
import static org.mybatis.spring.SqlSessionUtils.isSqlSessionTransactional;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.MyBatisExceptionTranslator;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.util.Assert;
  
 /**
  * <b>function:</b> 继承SqlSessionTemplate 重写相关方法
  */
 public class CustomSqlSessionTemplate extends SqlSessionTemplate {
  
     private final SqlSessionFactory sqlSessionFactory;
     private final ExecutorType executorType;
     private final SqlSession sqlSessionProxy;
     private final PersistenceExceptionTranslator exceptionTranslator;
  
     private Map<Object, SqlSessionFactory> targetSqlSessionFactorys;
     private SqlSessionFactory defaultTargetSqlSessionFactory;
  
     public void setTargetSqlSessionFactorys(Map<Object, SqlSessionFactory> targetSqlSessionFactorys) {
         this.targetSqlSessionFactorys = targetSqlSessionFactorys;
     }
  
     public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) {
         this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory;
     }
  
     public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
         this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
     }
  
     public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
         this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration()
                 .getEnvironment().getDataSource(), true));
     }
  
     public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
             PersistenceExceptionTranslator exceptionTranslator) {
  
         super(sqlSessionFactory, executorType, exceptionTranslator);
  
         this.sqlSessionFactory = sqlSessionFactory;
         this.executorType = executorType;
         this.exceptionTranslator = exceptionTranslator;
         
         this.sqlSessionProxy = (SqlSession) newProxyInstance(
                 SqlSessionFactory.class.getClassLoader(),
                 new Class[] { SqlSession.class }, 
                 new SqlSessionInterceptor());
  
         this.defaultTargetSqlSessionFactory = sqlSessionFactory;
     }
  
     @Override
     public SqlSessionFactory getSqlSessionFactory() {
  
         SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(CustomerContextHolder.getContextType());
         if (targetSqlSessionFactory != null) {
             return targetSqlSessionFactory;
         } else if (defaultTargetSqlSessionFactory != null) {
             return defaultTargetSqlSessionFactory;
         } else {
             Assert.notNull(targetSqlSessionFactorys, "Property 'targetSqlSessionFactorys' or 'defaultTargetSqlSessionFactory' are required");
             Assert.notNull(defaultTargetSqlSessionFactory, "Property 'defaultTargetSqlSessionFactory' or 'targetSqlSessionFactorys' are required");
         }
         return this.sqlSessionFactory;
     }
  
     @Override
     public Configuration getConfiguration() {
         return this.getSqlSessionFactory().getConfiguration();
     }
  
     public ExecutorType getExecutorType() {
         return this.executorType;
     }
  
     public PersistenceExceptionTranslator getPersistenceExceptionTranslator() {
         return this.exceptionTranslator;
     }
  
     /**
      * {@inheritDoc}
      */
     public <T> T selectOne(String statement) {
         return this.sqlSessionProxy.<T> selectOne(statement);
     }
  
     /**
      * {@inheritDoc}
      */
     public <T> T selectOne(String statement, Object parameter) {
         return this.sqlSessionProxy.<T> selectOne(statement, parameter);
     }
  
     /**
      * {@inheritDoc}
      */
     public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
         return this.sqlSessionProxy.<K, V> selectMap(statement, mapKey);
     }
  
     /**
      * {@inheritDoc}
      */
     public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
         return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey);
     }
  
     /**
      * {@inheritDoc}
      */
     public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
         return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey, rowBounds);
     }
  
     /**
      * {@inheritDoc}
      */
     public <E> List<E> selectList(String statement) {
         return this.sqlSessionProxy.<E> selectList(statement);
     }
  
     /**
      * {@inheritDoc}
      */
     public <E> List<E> selectList(String statement, Object parameter) {
         return this.sqlSessionProxy.<E> selectList(statement, parameter);
     }
  
     /**
      * {@inheritDoc}
      */
     public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
         return this.sqlSessionProxy.<E> selectList(statement, parameter, rowBounds);
     }
  
     /**
      * {@inheritDoc}
      */
     public void select(String statement, ResultHandler handler) {
         this.sqlSessionProxy.select(statement, handler);
     }
  
     /**
      * {@inheritDoc}
      */
     public void select(String statement, Object parameter, ResultHandler handler) {
         this.sqlSessionProxy.select(statement, parameter, handler);
     }
  
     /**
      * {@inheritDoc}
      */
     public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
         this.sqlSessionProxy.select(statement, parameter, rowBounds, handler);
     }
  
     /**
      * {@inheritDoc}
      */
     public int insert(String statement) {
         return this.sqlSessionProxy.insert(statement);
     }
  
     /**
      * {@inheritDoc}
      */
     public int insert(String statement, Object parameter) {
         return this.sqlSessionProxy.insert(statement, parameter);
     }
  
     /**
      * {@inheritDoc}
      */
     public int update(String statement) {
         return this.sqlSessionProxy.update(statement);
     }
  
     /**
      * {@inheritDoc}
      */
     public int update(String statement, Object parameter) {
         return this.sqlSessionProxy.update(statement, parameter);
     }
  
     /**
      * {@inheritDoc}
      */
     public int delete(String statement) {
         return this.sqlSessionProxy.delete(statement);
     }
  
     /**
      * {@inheritDoc}
      */
     public int delete(String statement, Object parameter) {
         return this.sqlSessionProxy.delete(statement, parameter);
     }
  
     /**
      * {@inheritDoc}
      */
     public <T> T getMapper(Class<T> type) {
         return getConfiguration().getMapper(type, this);
     }
  
     /**
      * {@inheritDoc}
      */
     public void commit() {
         throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
     }
  
     /**
      * {@inheritDoc}
      */
     public void commit(boolean force) {
         throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
     }
  
     /**
      * {@inheritDoc}
      */
     public void rollback() {
         throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
     }
  
     /**
      * {@inheritDoc}
      */
     public void rollback(boolean force) {
         throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
     }
  
     /**
      * {@inheritDoc}
      */
     public void close() {
         throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession");
     }
  
     /**
      * {@inheritDoc}
      */
     public void clearCache() {
         this.sqlSessionProxy.clearCache();
     }
  
     /**
      * {@inheritDoc}
      */
     public Connection getConnection() {
         return this.sqlSessionProxy.getConnection();
     }
  
     /**
      * {@inheritDoc}
      * @since 1.0.2
      */
     public List<BatchResult> flushStatements() {
         return this.sqlSessionProxy.flushStatements();
     }
  
     /**
      * Proxy needed to route MyBatis method calls to the proper SqlSession got from Spring's Transaction Manager It also
      * unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to pass a {@code PersistenceException} to
      * the {@code PersistenceExceptionTranslator}.
      */
     private class SqlSessionInterceptor implements InvocationHandler {
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
             final SqlSession sqlSession = getSqlSession(
                     CustomSqlSessionTemplate.this.getSqlSessionFactory(),
                     CustomSqlSessionTemplate.this.executorType, 
                     CustomSqlSessionTemplate.this.exceptionTranslator);
             try {
                 Object result = method.invoke(sqlSession, args);
                 if (!isSqlSessionTransactional(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory())) {
                     // force commit even on non-dirty sessions because some databases require
                     // a commit/rollback before calling close()
                     sqlSession.commit(true);
                 }
                 return result;
             } catch (Throwable t) {
                 Throwable unwrapped = unwrapThrowable(t);
                 if (CustomSqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                     Throwable translated = CustomSqlSessionTemplate.this.exceptionTranslator
                         .translateExceptionIfPossible((PersistenceException) unwrapped);
                     if (translated != null) {
                         unwrapped = translated;
                     }
                 }
                 throw unwrapped;
             } finally {
                 closeSqlSession(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory());
             }
         }
     }
  
 }

3.创建DynamicCreateDataSourceBean.java

package com.crs.ticket.utils.jotmutil;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.SqlSessionFactory;
import org.enhydra.jdbc.pool.StandardXAPoolDataSource;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.jdbc.core.JdbcTemplate;

import com.crs.ticket.common.entity.DbConfig;
import com.crs.ticket.utils.PropertyUtil;


public class DynamicCreateDataSourceBean implements ApplicationContextAware,
        ApplicationListener<ApplicationEvent> {
    
    private static final String DBFILE_PATH = "/config/jdbc.properties";

    private ConfigurableApplicationContext app;
    
    private JdbcTemplate jdbcTemplate;

    private CustomSqlSessionTemplate sqlSessionTemplate;
    
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public void setSqlSessionTemplate(CustomSqlSessionTemplate sqlSessionTemplate) {
        this.sqlSessionTemplate = sqlSessionTemplate;
    }

    @Override
    public void setApplicationContext(ApplicationContext app)
            throws BeansException {
        this.app = (ConfigurableApplicationContext)app;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        // 如果是容器刷新事件OR Start Event
        if (event instanceof ContextRefreshedEvent) {
            try {
                regDynamicBean();
            } catch (IOException e) {
                e.printStackTrace();
            }
            // System.out.println(event.getClass().getSimpleName()+" 事件已发生!");
        }
        
    }

    private void regDynamicBean() throws IOException {          
        // 解析属性文件,得到数据源Map          
        Map<String, DbConfig> mapCustom = parsePropertiesFile();          
        // 把数据源bean注册到容器中          
//        addSourceBeanToApp(mapCustom);
        addSqlSessionFactorySourceToApp(mapCustom);
    }
    
    /**
     * 功能说明:根据DataSource创建bean并注册到容器中
     * 
     * @param acf
     * @param mapCustom
     */
    private void addSourceBeanToApp(Map<String, DbConfig> mapCustom) {
        DefaultListableBeanFactory acf = (DefaultListableBeanFactory) app.getAutowireCapableBeanFactory();
        
        PropertyUtil propertyUtil = new PropertyUtil();
        String initialPoolSize = propertyUtil.getProperty(DBFILE_PATH, "jdbc.initialPoolSize");
        String minPoolSize = propertyUtil.getProperty(DBFILE_PATH, "jdbc.minPoolSize");
        String maxPoolSize = propertyUtil.getProperty(DBFILE_PATH, "jdbc.maxPoolSize");
        String maxIdleTime = propertyUtil.getProperty(DBFILE_PATH, "jdbc.maxIdleTime");
        String acquireIncrement = propertyUtil.getProperty(DBFILE_PATH, "jdbc.acquireIncrement");
        String maxStatements = propertyUtil.getProperty(DBFILE_PATH, "jdbc.maxStatements");
        String idleConnectionTestPeriod = propertyUtil.getProperty(DBFILE_PATH, "jdbc.idleConnectionTestPeriod");
        String acquireRetryAttempts = propertyUtil.getProperty(DBFILE_PATH, "jdbc.acquireRetryAttempts");
        
        String DATASOURCE_BEAN_CLASS = "org.enhydra.jdbc.pool.StandardXAPoolDataSource";
        BeanDefinitionBuilder bdb;

        Iterator<String> iter = mapCustom.keySet().iterator();

        Map<Object, Object> targetDataSources = new LinkedHashMap<Object, Object>();

//        BeanDefinition beanDefinition = new ChildBeanDefinition("portal");
        // 将默认数据源放入 targetDataSources map中
        targetDataSources.put("1", app.getBean("portal"));
        
        // 根据数据源得到数据,动态创建数据源bean 并将bean注册到applicationContext中去
        StandardXAPoolDataSource dataSource;
        while (iter.hasNext()) {

            // bean ID
            String beanKey = iter.next();
            // 创建bean
            bdb = BeanDefinitionBuilder.rootBeanDefinition(DATASOURCE_BEAN_CLASS);
            bdb.getBeanDefinition().setAttribute("id", beanKey);
            bdb.addPropertyValue("driverClass", mapCustom.get(beanKey).getDriverclass());
            bdb.addPropertyValue("jdbcUrl", mapCustom.get(beanKey).getJdbcurl());
            bdb.addPropertyValue("user", mapCustom.get(beanKey).getUsername());
            bdb.addPropertyValue("password", mapCustom.get(beanKey).getPassword());
            
            bdb.addPropertyValue("initialPoolSize", Integer.parseInt(initialPoolSize));
            bdb.addPropertyValue("minPoolSize", Integer.parseInt(minPoolSize));
            bdb.addPropertyValue("maxPoolSize", Integer.parseInt(maxPoolSize));
            bdb.addPropertyValue("maxIdleTime", Integer.parseInt(maxIdleTime));
            bdb.addPropertyValue("acquireIncrement", Integer.parseInt(acquireIncrement));
            bdb.addPropertyValue("maxStatements", Integer.parseInt(maxStatements));
            bdb.addPropertyValue("idleConnectionTestPeriod", Integer.parseInt(idleConnectionTestPeriod));
            bdb.addPropertyValue("acquireRetryAttempts", Integer.parseInt(acquireRetryAttempts));
            // 注册bean
            acf.registerBeanDefinition("ds" + beanKey, bdb.getBeanDefinition());

            // 放入map中,注意一定是刚才创建bean对象

        
        }

    }

    /**
     * 功能说明:GET ALL SM_STATIONS FROM DB1
     * 
     * @return
     * @throws IOException
     */
    @SuppressWarnings("rawtypes")
    private Map<String, DbConfig> parsePropertiesFile()
            throws IOException {

        String sql = "SELECT ID_COMPANY,DRIVERCLASS,JDBCURL,USERNAME,PASSWORD FROM CRS_DB_CONFIG  WHERE IS_DEL=0 AND IS_ENABLED=10091020";

        List list = jdbcTemplate.queryForList(sql);
        Iterator iterator = list.iterator();
        Map<String, DbConfig> mds = new HashMap<String, DbConfig>();
        while (iterator.hasNext()) {
            Map map4station = (Map) iterator.next();
            DbConfig dsi = new DbConfig();
            dsi.setIdCompany((String)map4station.get("ID_COMPANY"));
            dsi.setDriverclass((String)map4station.get("DRIVERCLASS"));
            dsi.setJdbcurl((String)map4station.get("JDBCURL"));
            dsi.setUsername((String)map4station.get("USERNAME"));
            dsi.setPassword((String)map4station.get("PASSWORD"));
            mds.put(dsi.getIdCompany(), dsi);
        }
        return mds;
    }

    
    private void addSqlSessionFactorySourceToApp(Map<String, DbConfig> mapCustom){
        DefaultListableBeanFactory acf = (DefaultListableBeanFactory) app.getAutowireCapableBeanFactory();
        PropertyUtil propertyUtil = new PropertyUtil();
        String minSize = propertyUtil.getProperty(DBFILE_PATH, "jdbc.minPoolSize");
        String maxSize = propertyUtil.getProperty(DBFILE_PATH, "jdbc.maxPoolSize");
        String sleepTime = propertyUtil.getProperty(DBFILE_PATH, "jdbc.sleepTime");
        String lifeTime = propertyUtil.getProperty(DBFILE_PATH, "jdbc.lifeTime");
        String deadLockMaxWait = propertyUtil.getProperty(DBFILE_PATH, "jdbc.deadLockMaxWait");
        String deadLockRetryWait = propertyUtil.getProperty(DBFILE_PATH, "jdbc.deadLockRetryWait");
        
        String DATASOURCE_BEAN_CLASS = "org.enhydra.jdbc.pool.StandardXAPoolDataSource";
        
        BeanDefinitionBuilder bdb;
        BeanDefinitionBuilder bdb_child;
        BeanDefinitionBuilder bdb_ssf;

        Iterator<String> iter = mapCustom.keySet().iterator();

        Map<Object, SqlSessionFactory> targetSqlSessionFactorys = new LinkedHashMap<Object, SqlSessionFactory>();

        // BeanDefinition beanDefinition = new ChildBeanDefinition("portal");
        // 将默认SqlSessionFactory放入 targetSqlSessionFactorys map中
        targetSqlSessionFactorys.put("portal1", (SqlSessionFactory)app.getBean("portalSqlSessionFactory"));
        
        while (iter.hasNext()) {
            // bean ID
            String beanKey = iter.next();
            // 创建连接池bean
            bdb = BeanDefinitionBuilder.rootBeanDefinition(DATASOURCE_BEAN_CLASS);
            bdb.getBeanDefinition().setAttribute("id", beanKey);
            bdb.getBeanDefinition().setAttribute("destroy-method", "shutdown");
            //创建数据源
            bdb_child = BeanDefinitionBuilder.rootBeanDefinition("org.enhydra.jdbc.standard.StandardXADataSource");
            bdb_child.getBeanDefinition().setAttribute("destroy-method", "shutdown");
            bdb_child.addPropertyValue("transactionManager", app.getBean("jotm"));
//            bdb_child.addPropertyReference("transactionManager", "jotm");
            bdb_child.addPropertyValue("driverName", mapCustom.get(beanKey).getDriverclass());
            bdb_child.addPropertyValue("url", mapCustom.get(beanKey).getJdbcurl());
            acf.registerBeanDefinition("ds_child" + beanKey, bdb_child.getBeanDefinition());
            
//            bdb.addPropertyReference("dataSource", "ds_child" + beanKey);
            bdb.addPropertyValue("dataSource", app.getBean("ds_child" + beanKey));
            
            bdb.addPropertyValue("user", mapCustom.get(beanKey).getUsername());
            bdb.addPropertyValue("password", mapCustom.get(beanKey).getPassword());
            bdb.addPropertyValue("minSize", Integer.parseInt(minSize));
            bdb.addPropertyValue("maxSize", Integer.parseInt(maxSize));
            bdb.addPropertyValue("sleepTime", Integer.parseInt(sleepTime));
            bdb.addPropertyValue("lifeTime", Integer.parseInt(lifeTime));
            bdb.addPropertyValue("deadLockMaxWait", Integer.parseInt(deadLockMaxWait));
            bdb.addPropertyValue("deadLockRetryWait", Integer.parseInt(deadLockRetryWait));
            // 注册连接池bean
            acf.registerBeanDefinition("ds" + beanKey, bdb.getBeanDefinition());
            
            //创建SqlSessionFactory
            bdb_ssf = BeanDefinitionBuilder.rootBeanDefinition("org.mybatis.spring.SqlSessionFactoryBean");
            bdb_ssf.getBeanDefinition().setAttribute("id", "sqlSessionFactory" + beanKey);
            bdb_ssf.addPropertyValue("dataSource", bdb.getBeanDefinition());
            bdb_ssf.addPropertyValue("mapperLocations", "classpath*:com/crs/**/*.xml");
            //注册SqlSessionFactory
            acf.registerBeanDefinition("sqlSessionFactory" + beanKey, bdb_ssf.getBeanDefinition());
            targetSqlSessionFactorys.put(beanKey, (SqlSessionFactory)app.getBean("sqlSessionFactory" + beanKey));
        }
        sqlSessionTemplate.setTargetSqlSessionFactorys(targetSqlSessionFactorys);
        sqlSessionTemplate.setDefaultTargetSqlSessionFactory((SqlSessionFactory)app.getBean("portalSqlSessionFactory"));
    }
}


4.创建CustomerContextHolder.java(多数据源切换工具)

package com.crs.ticket.utils.jotmutil;
  
 /**
  * <b>function:</b> 多数据源
  */
 public abstract class CustomerContextHolder {
  
     public final static String SESSION_FACTORY_PORTAL1 = "portal1";
     public final static String SESSION_FACTORY_BO1 = "bo1";
     
     private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();  
     
     public static void setContextType(String contextType) {  
         contextHolder.set(contextType);  
     }  
       
     public static String getContextType() {  
         return contextHolder.get();  
     }  
       
     public static void clearContextType() {  
         contextHolder.remove();  
     }  
 }

5.创建配置文件.properties读取工具类

package com.crs.ticket.utils;

import java.io.IOException;
import java.util.Properties;

public class PropertyUtil {
    public String getProperty(String fileName,String key){
        String value = null;
        Properties pro = new Properties();
        try {
            pro.load(getClass().getResourceAsStream(fileName));
            if(pro.containsKey(key))
                value = pro.getProperty(key).trim();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return value;        
    }
}

6.创建动态数据源实体类

package com.crs.ticket.common.entity;

public class DbConfig {

    private String idCompany;
    
    private String driverclass;
    
    private String jdbcurl;
    
    private String username;
    
    private String password;

    public String getIdCompany() {
        return idCompany;
    }

    public void setIdCompany(String idCompany) {
        this.idCompany = idCompany;
    }

    public String getDriverclass() {
        return driverclass;
    }

    public void setDriverclass(String driverclass) {
        this.driverclass = driverclass;
    }

    public String getJdbcurl() {
        return jdbcurl;
    }

    public void setJdbcurl(String jdbcurl) {
        this.jdbcurl = jdbcurl;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
    
}

7.配置spring/spring-mybatis配置文件

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd ">
    <!-- 配置jtom实例 -->
    <bean id="jotm" class="com.crs.ticket.utils.jotmutil.JotmFactoryBean"> 
          <property name="defaultTimeout" value="500000"/> 
    </bean>
    <bean id="myJtaManager" 
                    class="org.springframework.transaction.jta.JtaTransactionManager"> 
                <property name="userTransaction"> 
                        <ref local="jotm"/> 
                </property> 
    </bean> 
    
    <!-- 配置mybatis Mapper扫描器 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.crs.**.mapper" />
        <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/>
    </bean>
    
     <!-- 配置自定义的SqlSessionTemplate模板,注入相关配置 -->
        <bean id="sqlSessionTemplate" class="com.crs.ticket.utils.jotmutil.CustomSqlSessionTemplate">
            <constructor-arg ref="portalSqlSessionFactory" />
            <property name="targetSqlSessionFactorys">
                <map>     
                    <!-- <entry value-ref="portalSqlSessionFactory" key="portal1"/> -->
                    <!-- <entry value-ref="boSqlSessionFactory" key="bo1"/> -->
                </map> 
            </property>
        </bean>            
    <!-- 配置datasource数据源 -->
    <!-- destroy-method="close"当数据库连接不使用的时候,就把该连接重新放到数据池中,方便下次使用调用. -->
    <bean id="portal" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown"> 
            <property name="dataSource"> 
                <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown"> 
                        <property name="transactionManager" ref="jotm"/> 
                        <property name="driverName" value="${portal.jdbc.driverClass}"/> 
                        <property name="url" value="${portal.jdbc.jdbcUrl}"/> 
                </bean> 
            </property> 
            <property name="user" value="${portal.jdbc.user}"/> 
            <property name="password" value="${portal.jdbc.password}"/> 
            <property name="minSize" value="${jdbc.minPoolSize}"/>
            <property name="maxSize" value="${jdbc.maxPoolSize}"/>
            <property name="sleepTime" value="${jdbc.sleepTime}"/>
            <property name="lifeTime" value="${jdbc.lifeTime}"/>
            <property name="deadLockMaxWait" value="${jdbc.deadLockMaxWait}"/>
            <property name="deadLockRetryWait" value="${jdbc.deadLockRetryWait}"/>
    </bean>
    
    <!-- 配置session工厂 -->
    <bean id="portalSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="portal"/>
        <property name="configLocation" value="classpath:config/mybatis-configuration.xml"></property>
    </bean>
    
    <bean id="jdbcTemplate" 
         class="org.springframework.jdbc.core.JdbcTemplate"> 
         <property name="dataSource" > 
             <ref bean="portal"/> 
         </property> 
    </bean>
    
    <bean id="applicationEventListener"  class="com.crs.ticket.utils.jotmutil.DynamicCreateDataSourceBean">
        <property name="jdbcTemplate" ref="jdbcTemplate" ></property>
        <property name="sqlSessionTemplate" ref="sqlSessionTemplate" ></property>
        
    </bean >    
    
    
   <!-- 通知配置 --> 
   <tx:advice id="txAdvice" transaction-manager="myJtaManager"> 
        <tx:attributes>
            <tx:method name="del*" propagation="REQUIRED" read-only="false"
                rollback-for="java.lang.Exception" no-rollback-for="java.lang.RuntimeException" />
            <tx:method name="insert*" propagation="REQUIRED" read-only="false"
                rollback-for="java.lang.Exception" />
            <tx:method name="update*" propagation="REQUIRED" read-only="false"
                rollback-for="java.lang.Exception" />
            <tx:method name="add*" propagation="REQUIRED" read-only="false"
                rollback-for="java.lang.Exception" />
            <tx:method name="modify*" propagation="REQUIRED" read-only="false"
                rollback-for="java.lang.Exception" />
            <tx:method name="correction*" propagation="REQUIRED" read-only="false"
                rollback-for="java.lang.Exception" />    

            <tx:method name="find*" propagation="SUPPORTS" />
            <tx:method name="query*" propagation="SUPPORTS" />
            <tx:method name="get*" propagation="SUPPORTS" />
            <tx:method name="select*" propagation="SUPPORTS" />
            <tx:method name="list*" propagation="SUPPORTS" />
            <tx:method name="*" propagation="SUPPORTS" />
        </tx:attributes>
   </tx:advice>     
    
    <!-- 配置spring AOP --><!-- expression="execution(* com.crs..service..*.*(..)))任意返回值的定义在service包以及子包中的任意方法,..代表参数 -->
    <aop:config>
        <aop:pointcut id="pc" expression="execution(* com.crs..service..*.*(..)))" />
        <aop:advisor pointcut-ref="pc" advice-ref="txAdvice" />
    </aop:config>
</beans>

8.controller中使用切换数据源(举例)

    CustomerContextHolder.setContextType(CustomerContextHolder.SESSION_FACTORY_PORTAL1);
        List<Student> studentList = ticketchooseservice.findStudentInfoByStudNo(studentNo);
        System.out.println(studentList.size());
        CustomerContextHolder.setContextType("36b37c0011c945808e655c80ff34457e");
        List<Student> studentLista = ticketchooseservice.findStudentInfoByStudNo(studentNo);

 

posted @ 2017-06-30 15:09  十月围城小童鞋  阅读(626)  评论(0编辑  收藏  举报