国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > Java > 正文

關(guān)于Spring3 + Mybatis3整合時(shí)多數(shù)據(jù)源動(dòng)態(tài)切換的問(wèn)題

2019-11-26 12:31:00
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

以前的項(xiàng)目經(jīng)歷中,基本上都是spring + hibernate + Spring JDBC這種組合用的多。至于MyBatis,也就這個(gè)項(xiàng)目才開(kāi)始試用,閑話不多說(shuō),進(jìn)入正題。

以前的這種框架組合中,動(dòng)態(tài)數(shù)據(jù)源切換可謂已經(jīng)非常成熟了,網(wǎng)上也有非常多的博客介紹,都是繼承AbstractRoutingDataSource,重寫(xiě)determineCurrentLookupKey()方法。具體做法就不在此廢話了。

所以當(dāng)項(xiàng)目中碰到這個(gè)問(wèn)題,我?guī)缀跸攵紱](méi)有想,就采用了這種做法,但是一測(cè)試,一點(diǎn)反應(yīng)都沒(méi)有。當(dāng)時(shí)覺(jué)得不可能,于是斷點(diǎn),加log調(diào)試,發(fā)現(xiàn)determineCurrentLookupKey()根本沒(méi)有調(diào)用。 

為什么列? 這不可能啊。靜下心來(lái),仔細(xì)想想,才想到一個(gè)關(guān)鍵的問(wèn)題: Mybatis整合Spring,而不是Spring整合的Mybatis! 直覺(jué)告訴我,問(wèn)題就出在這里。

于是花時(shí)間去研究一下mybatis-spring.jar 這個(gè)包,發(fā)現(xiàn)有SqlSession這東西,很本能的就注意到了這一塊,然后大致看一下他的一些實(shí)現(xiàn)類。于是就發(fā)現(xiàn)了他的實(shí)現(xiàn)類里面有一個(gè)內(nèi)部類SqlSessionInterceptor(研究過(guò)程就不多說(shuō)了,畢竟是個(gè)痛苦的過(guò)程)

好吧,這個(gè)類的作用列,就是產(chǎn)生sessionProxy。關(guān)鍵代碼如下

final SqlSession sqlSession = getSqlSession(  SqlSessionTemplate.this.sqlSessionFactory,  SqlSessionTemplate.this.executorType,  SqlSessionTemplate.this.exceptionTranslator); 

這個(gè)sqlSessionFactory 我們就很眼熟啦,是我們?cè)趕pring配置文件中配了的,是吧,也就是說(shuō)這東西是直接從我們配置文件中讀進(jìn)來(lái),但這東西,就關(guān)聯(lián)了Datasource。所以就想到,如果能把這東西,做到動(dòng)態(tài),那么數(shù)據(jù)源切換,也就動(dòng)態(tài)了。

于是第一反應(yīng)就是寫(xiě)了一個(gè)類,然后在里面定義一個(gè)Map,用來(lái)存放多個(gè)SqlSessionFactory,并采用Setter方法進(jìn)行屬性注入。

public class EjsSqlSessionTemplate extends SqlSessionTemplate {   private Map<String, SqlSessionFactory> targetSqlSessionFactory = new HashMap<String, SqlSessionFactory>();  public void setTargetSqlSessionFactory(Map<String, SqlSessionFactory> targetSqlSessionFactory) {   this.targetSqlSessionFactory = targetSqlSessionFactory;  } 

所以Spring的配置文件就變成了這樣:

<bean id="sqlSessionTemplate" class="com.ejushang.spider.datasource.EjsSqlSessionTemplate">   <constructor-arg ref="sqlSessionFactory" />   <property name="targetSqlSessionFactory">    <map>     <entry value-ref="sqlSessionFactory" key="spider"/>     <entry value-ref="sqlSessionFactoryTb" key="sysinfo"/>    </map>   </property>  </bean>  <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">   <property name="basePackage" value="com.foo.bar.**.mapper*" />   <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/>  </bean> 

那么這個(gè)思想是那里來(lái)的列? 當(dāng)然就是借鑒了Spring的動(dòng)態(tài)數(shù)據(jù)源的思想啦,對(duì)比一下Spring動(dòng)態(tài)數(shù)據(jù)源的配置,看看是不是差不多?

然后重寫(xiě)了個(gè)關(guān)鍵的方法:

/**   * 重寫(xiě)得到SqlSessionFactory的方法   * @return   */  @Override  public SqlSessionFactory getSqlSessionFactory() {    SqlSessionFactory targetSqlSessionFactory = this.targetSqlSessionFactory.get(SqlSessionContextHolder.getDataSourceKey());   if (targetSqlSessionFactory != null) {    return targetSqlSessionFactory;   } else if ( this.getSqlSessionFactory() != null) {    return this.getSqlSessionFactory();   }   throw new IllegalArgumentException("sqlSessionFactory or targetSqlSessionFactory must set one at least");  } 

而SqlSessionContextHolder就很簡(jiǎn)單,就是一個(gè)ThreadLocal的思想

public class SqlSessionContextHolder {  private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();  private static Logger logger = LoggerFactory.getLogger(SqlSessionContextHolder.class);  public static void setSessionFactoryKey(String dataSourceKey) {   contextHolder.set(dataSourceKey);  }  public static String getDataSourceKey() {   String key = contextHolder.get();   logger.info("當(dāng)前線程Thread:"+Thread.currentThread().getName()+" 當(dāng)前的數(shù)據(jù)源 key is "+ key);   return key;  } } 

博主信心滿滿就開(kāi)始測(cè)試了。。結(jié)果發(fā)現(xiàn)不行,切換不過(guò)來(lái),始終都是綁定的是構(gòu)造函數(shù)中的那個(gè)默認(rèn)的sqlSessionFactory,當(dāng)時(shí)因?yàn)榭戳艘惶煸创a,頭也有點(diǎn)暈。其實(shí)為什么列?

看看我們產(chǎn)生sessionProxy的地方代碼,他的sqlSessionFactory是直接從構(gòu)造函數(shù)來(lái)拿的。而構(gòu)造函數(shù)中的sqlSessionFactory在spring容器啟動(dòng)時(shí),就已經(jīng)初始化好了,這點(diǎn)也可以從我們Spring配置文件中得到證實(shí)。

那這個(gè)問(wèn)題,怎么解決列? 于是博主便想重寫(xiě)那個(gè)sqlSessionInterceptor。 擦,問(wèn)題就來(lái)了,這個(gè)類是private的,沒(méi)辦法重寫(xiě)啊。于是博主又只能在自己的EjsSqlSessionTemplate類中,也定義了一個(gè)內(nèi)部類,把源碼中的代碼都copy過(guò)來(lái),唯一不同的就是我不是讀取構(gòu)造函數(shù)中的sqlSessionFactory.而是每次都去調(diào)用 getSqlSessionFactory()方法。代碼如下:

final SqlSession sqlSession = getSqlSession(    EjsSqlSessionTemplate.this.getSqlSessionFactory(),    EjsSqlSessionTemplate.this.getExecutorType(),     EjsSqlSessionTemplate.this.getPersistenceExceptionTranslator()); 

再試,發(fā)現(xiàn)還是不行,再找原因,又回歸到了剛才那個(gè)問(wèn)題。因?yàn)槲覜](méi)有重寫(xiě)SqlSessionTemplate的構(gòu)造函數(shù),而sqlSessionProxy是在構(gòu)函數(shù)中初始化的,代碼如下:

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,  PersistenceExceptionTranslator exceptionTranslator) {  notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");  notNull(executorType, "Property 'executorType' is required");  this.sqlSessionFactory = sqlSessionFactory;  this.executorType = executorType;  this.exceptionTranslator = exceptionTranslator;  this.sqlSessionProxy = (SqlSession) newProxyInstance(   SqlSessionFactory.class.getClassLoader(),   new Class[] { SqlSession.class },   new SqlSessionInterceptor()); } 

而SqlSessionInterceptor()這東西都是private。 所以父類壓根就不會(huì)加載我寫(xiě)的那個(gè)SqlSessionInterceptor()。所以問(wèn)題就出在這,那好吧,博主又重寫(xiě)構(gòu)函數(shù)

public EjsSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {   super(getSqlSessionFactory(), executorType, exceptionTranslator);  } 

很明顯這段代碼是編譯不通過(guò)的,構(gòu)造函數(shù)中,怎么可能調(diào)用類實(shí)例方法列?  那怎么辦列? 又只有把父類的構(gòu)造函數(shù)copy過(guò)來(lái),那問(wèn)題又有了,這些成員屬性又沒(méi)有。那又只得把他們也搬過(guò)來(lái)。。  后來(lái),這個(gè)動(dòng)態(tài)數(shù)據(jù)數(shù)據(jù)源的功能,終于完成了。

--------------------------------------------------------------------------------------------------------------------分割線-----------------------------------------------------------------------------------------------------------整個(gè)完整的代碼如下:

1、重寫(xiě)SqlSessionTemplate (重寫(xiě)的過(guò)程已經(jīng)在上面分析過(guò)了)

public class EjsSqlSessionTemplate extends SqlSessionTemplate {  private final SqlSessionFactory sqlSessionFactory;  private final ExecutorType executorType;  private final SqlSession sqlSessionProxy;  private final PersistenceExceptionTranslator exceptionTranslator;  private Map<Object, SqlSessionFactory> targetSqlSessionFactory;  public void setTargetSqlSessionFactory(Map<Object, SqlSessionFactory> targetSqlSessionFactory) {   this.targetSqlSessionFactory = targetSqlSessionFactory;  }  public EjsSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {   this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());  }  public EjsSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {   this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration()     .getEnvironment().getDataSource(), true));  }  public EjsSqlSessionTemplate(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());  }  @Override  public SqlSessionFactory getSqlSessionFactory() {   SqlSessionFactory targetSqlSessionFactory = this.targetSqlSessionFactory.get(SqlSessionContextHolder.getDataSourceKey());   if (targetSqlSessionFactory != null) {    return targetSqlSessionFactory;   } else if ( this.sqlSessionFactory != null) {    return this.sqlSessionFactory;   }   throw new IllegalArgumentException("sqlSessionFactory or targetSqlSessionFactory must set one at least");  }  @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(      EjsSqlSessionTemplate.this.getSqlSessionFactory(),      EjsSqlSessionTemplate.this.executorType,      EjsSqlSessionTemplate.this.exceptionTranslator);    try {     Object result = method.invoke(sqlSession, args);     if (!isSqlSessionTransactional(sqlSession, EjsSqlSessionTemplate.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 (EjsSqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {      Throwable translated = EjsSqlSessionTemplate.this.exceptionTranslator        .translateExceptionIfPossible((PersistenceException) unwrapped);      if (translated != null) {       unwrapped = translated;      }     }     throw unwrapped;    } finally {     closeSqlSession(sqlSession, EjsSqlSessionTemplate.this.getSqlSessionFactory());    }   }  } } 

2。自定義了一個(gè)注解

/**  * 注解式數(shù)據(jù)源,用來(lái)進(jìn)行數(shù)據(jù)源切換  * User:Amos.zhou  * Date: 14-2-27  * Time: 下午2:34  */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ChooseDataSource {  String value() default ""; } 

3.定義一個(gè)AspectJ的切面(我習(xí)慣用AspectJ,因?yàn)閟pring AOP不支持cflow()這些語(yǔ)法),所以在編譯,打包的時(shí)候一定要用aspectJ的編譯器,不能直接用原生的JDK。有些方法就是我基于以前Hibernate,JDBC動(dòng)態(tài)數(shù)據(jù)源的時(shí)候改動(dòng)的。

/**  * <li>類描述:完成數(shù)據(jù)源的切換,抽類切面,具體項(xiàng)目繼承一下,不需要重寫(xiě)即可使用</li>  *  * @author: amos.zhou  * 2013-8-1 上午11:51:40  * @since v1.0  */ @Aspect public abstract class ChooseDataSourceAspect {  protected static final ThreadLocal<String> preDatasourceHolder = new ThreadLocal<String>();  @Pointcut("execution(public * *.*(..))")  public void allMethodPoint() {  }  @Pointcut("@within(com.ejushang.spider.annotation.ChooseDataSource) && allMethodPoint()")  public void allServiceMethod() {  }  /**   * 對(duì)所有注解有ChooseDataSource的類進(jìn)行攔截   */  @Pointcut("cflow(allServiceMethod()) && allServiceMethod()")  public void changeDatasourcePoint() {  }  /**   * 根據(jù)@ChooseDataSource的屬性值設(shè)置不同的dataSourceKey,以供DynamicDataSource   */  @Before("changeDatasourcePoint()")  public void changeDataSourceBeforeMethodExecution(JoinPoint jp) {   //拿到anotation中配置的數(shù)據(jù)源   String resultDS = determineDatasource(jp);   //沒(méi)有配置實(shí)用默認(rèn)數(shù)據(jù)源   if (resultDS == null) {    SqlSessionContextHolder.setSessionFactoryKey(null);    return;   }   preDatasourceHolder.set(SqlSessionContextHolder.getDataSourceKey());   //將數(shù)據(jù)源設(shè)置到數(shù)據(jù)源持有者   SqlSessionContextHolder.setSessionFactoryKey(resultDS);  }  /**   * <p>創(chuàng)建時(shí)間: 2013-8-20 上午9:48:44</p>   * 如果需要修改獲取數(shù)據(jù)源的邏輯,請(qǐng)重寫(xiě)此方法   *   * @param jp   * @return   */  @SuppressWarnings("rawtypes")  protected String determineDatasource(JoinPoint jp) {   String methodName = jp.getSignature().getName();   Class targetClass = jp.getSignature().getDeclaringType();   String dataSourceForTargetClass = resolveDataSourceFromClass(targetClass);   String dataSourceForTargetMethod = resolveDataSourceFromMethod(     targetClass, methodName);   String resultDS = determinateDataSource(dataSourceForTargetClass,     dataSourceForTargetMethod);   return resultDS;  }  /**   * 方法執(zhí)行完畢以后,數(shù)據(jù)源切換回之前的數(shù)據(jù)源。   * 比如foo()方法里面調(diào)用bar(),但是bar()另外一個(gè)數(shù)據(jù)源,   * bar()執(zhí)行時(shí),切換到自己數(shù)據(jù)源,執(zhí)行完以后,要切換到foo()所需要的數(shù)據(jù)源,以供   * foo()繼續(xù)執(zhí)行。   * <p>創(chuàng)建時(shí)間: 2013-8-16 下午4:27:06</p>   */  @After("changeDatasourcePoint()")  public void restoreDataSourceAfterMethodExecution() {   SqlSessionContextHolder.setSessionFactoryKey(preDatasourceHolder.get());   preDatasourceHolder.remove();  }  /**   * <li>創(chuàng)建時(shí)間: 2013-6-17 下午5:34:13</li> <li>創(chuàng)建人:amos.zhou</li> <li>方法描述 :</li>   *   * @param targetClass   * @param methodName   * @return   */  @SuppressWarnings("rawtypes")  private String resolveDataSourceFromMethod(Class targetClass,             String methodName) {   Method m = ReflectUtil.findUniqueMethod(targetClass, methodName);   if (m != null) {    ChooseDataSource choDs = m.getAnnotation(ChooseDataSource.class);    return resolveDataSourcename(choDs);   }   return null;  }  /**   * <li>創(chuàng)建時(shí)間: 2013-6-17 下午5:06:02</li>   * <li>創(chuàng)建人:amos.zhou</li>   * <li>方法描述 : 確定   * 最終數(shù)據(jù)源,如果方法上設(shè)置有數(shù)據(jù)源,則以方法上的為準(zhǔn),如果方法上沒(méi)有設(shè)置,則以類上的為準(zhǔn),如果類上沒(méi)有設(shè)置,則使用默認(rèn)數(shù)據(jù)源</li>   *   * @param classDS   * @param methodDS   * @return   */  private String determinateDataSource(String classDS, String methodDS) { //  if (null == classDS && null == methodDS) { //   return null; //  }   // 兩者必有一個(gè)不為null,如果兩者都為Null,也會(huì)返回Null   return methodDS == null ? classDS : methodDS;  }  /**   * <li>創(chuàng)建時(shí)間: 2013-6-17 下午4:33:03</li> <li>創(chuàng)建人:amos.zhou</li> <li>方法描述 : 類級(jí)別的 @ChooseDataSource   * 的解析</li>   *   * @param targetClass   * @return   */  @SuppressWarnings({"unchecked", "rawtypes"})  private String resolveDataSourceFromClass(Class targetClass) {   ChooseDataSource classAnnotation = (ChooseDataSource) targetClass     .getAnnotation(ChooseDataSource.class);   // 直接為整個(gè)類進(jìn)行設(shè)置   return null != classAnnotation ? resolveDataSourcename(classAnnotation)     : null;  }  /**   * <li>創(chuàng)建時(shí)間: 2013-6-17 下午4:31:42</li> <li>創(chuàng)建人:amos.zhou</li> <li>方法描述 :   * 組裝DataSource的名字</li>   *   * @param ds   * @return   */  private String resolveDataSourcename(ChooseDataSource ds) {   return ds == null ? null : ds.value();  } } 

那么以上3個(gè)類,就可以作為一個(gè)公共的組件打個(gè)包了。

那么項(xiàng)目中具體 怎么用列?

4.  在項(xiàng)目中定義一個(gè)具體的AspectJ切面

@Aspect public class OrderFetchAspect extends ChooseDataSourceAspect { }

如果你的根據(jù)你的需要重寫(xiě)方法,我這邊是不需要重寫(xiě)的,所以空切面就行了。

5.配置spring,在上面的分析過(guò)程中已經(jīng)貼出了,基本上就是每個(gè)數(shù)據(jù)庫(kù),一個(gè)dataSource,每個(gè)DataSource一個(gè)SqlSessionFactory。最后配一個(gè)SqlSessionTemplate,也就是我們自己重寫(xiě)的。再就是MapperScan了,大致如下(數(shù)據(jù)庫(kù)連接信息已去除,包名為杜撰):

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> </bean> <bean id="dataSourceTb" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> </bean> <!-- 事務(wù)管理 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  <property name="dataSource" ref="dataSource" /> </bean> <!-- 注解控制事務(wù) --> <tx:annotation-driven transaction-manager="transactionManager"/> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  <property name="dataSource" ref="dataSource"/>  <property name="configLocation" value="classpath:mybatis.xml" />  <property name="mapperLocations" value="classpath*:com/foo/bar/**/config/*mapper.xml" /> </bean> <bean id="sqlSessionFactoryTb" class="org.mybatis.spring.SqlSessionFactoryBean">  <property name="dataSource" ref="dataSourceTb"/>  <property name="configLocation" value="classpath:mybatis.xml" />  <property name="mapperLocations" value="classpath*:<span style="font-family: Arial, Helvetica, sans-serif;">com/foo/bar</span><span style="font-family: Arial, Helvetica, sans-serif;">/**/configtb/*mapper.xml" /></span> </bean> <bean id="sqlSessionTemplate" class="com.foo.bar.template.EjsSqlSessionTemplate">  <constructor-arg ref="sqlSessionFactory" />  <property name="targetSqlSessionFactory">   <map>    <entry value-ref="sqlSessionFactory" key="spider"/>    <entry value-ref="sqlSessionFactoryTb" key="sysinfo"/>   </map>  </property> </bean> <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">  <property name="basePackage" value="com.foo.bar.**.mapper*" />  <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/> </bean> 

6.具體應(yīng)用

@ChooseDataSource("spider") public class ShopServiceTest extends ErpTest {  private static final Logger log = LoggerFactory.getLogger(ShopServiceTest.class);  @Autowired  private IShopService shopService;  @Autowired  private IJdpTbTradeService jdpTbTradeService;  @Test  @Rollback(false)  public void testFindAllShop(){   List<Shop> shopList1 = shopService.findAllShop();   for(Shop shop : shopList1){    System.out.println(shop);   }   fromTestDB();  }  @ChooseDataSource("sysinfo")  private void fromTestDB(){   List<Shop> shopList = jdpTbTradeService.findAllShop();   for(Shop shop : shopList){    System.out.println(shop);   }  } } 

測(cè)試發(fā)現(xiàn) shopList1是從spider庫(kù)查出來(lái)的數(shù)據(jù),而fromDB則是從sysinfo中查出來(lái)的數(shù)據(jù)。 那么我們就大功告成。
要做到我以上功能,Spring AOP是做不到的,因?yàn)樗恢С諧flow(),這也就是我為什么要用AspectJ的原因。

-----------------------------------------------------------------------------------------------再次分割線-------------------------------------------------------------------------------------------------------------------

好了,功能我們已經(jīng)實(shí)現(xiàn)了,你有沒(méi)有覺(jué)得很麻煩,這一點(diǎn)也不Spring的風(fēng)格,Spring的各個(gè)地方擴(kuò)展都是很方便的。那么我們看看,在SqlSessionTemplate中的什么地方改動(dòng)一下,我們就可以很輕松的實(shí)現(xiàn)這個(gè)功能列?大家可以理解了,再回去看一下源碼。

其實(shí),只要將源碼中的那個(gè)SqlSessionInterceptor的這句話:

final SqlSession sqlSession = getSqlSession(    SqlSessionTemplate.this.sqlSessionFactory,    SqlSessionTemplate.this.executorType,    SqlSessionTemplate.this.exceptionTranslator); 

改為:

final SqlSession sqlSession = getSqlSession(      EjsSqlSessionTemplate.this.getSqlSessionFactory(),      EjsSqlSessionTemplate.this.executorType,     		 EjsSqlSessionTemplate.this.exceptionTranslator); 

保證 每次在產(chǎn)生Session代理的時(shí)候,傳進(jìn)去的參數(shù)都是調(diào)用getSqlSessionFactory()獲取,那么我們自定義的SqlSessionTemplate,只要重寫(xiě)getSqlSessionFactory(),加多一個(gè)以下2句話:

private Map<Object, SqlSessionFactory> targetSqlSessionFactory;  public void setTargetSqlSessionFactory(Map<Object, SqlSessionFactory> targetSqlSessionFactory) {   this.targetSqlSessionFactory = targetSqlSessionFactory;  } 

那么就完全可以實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換。  那么mybatis-spring的項(xiàng)目團(tuán)隊(duì)會(huì)這樣維護(hù)么? 我會(huì)以mail的方式與他們溝通。至于能否改進(jìn),我們不得而知了。

其實(shí)這也就引發(fā)一個(gè)關(guān)于面向?qū)ο笤O(shè)計(jì)時(shí)的思想,也是一直爭(zhēng)論得喋喋不休的一個(gè)問(wèn)題:

    在類的方法中,如果要用到類的屬性時(shí),是直接用this.filedName  來(lái)操作,還是用  getFiledName() 來(lái)進(jìn)行操作?

其實(shí)以前我也是偏向于直接用this.屬性來(lái)進(jìn)行操作的,但是經(jīng)歷過(guò)這次以后,我想我會(huì)偏向于后者。

以上所述是小編給大家介紹的關(guān)于Spring3 + Mybatis3整合時(shí)多數(shù)據(jù)源動(dòng)態(tài)切換的問(wèn)題,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)武林網(wǎng)網(wǎng)站的支持!

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 泰和县| 凌云县| 田林县| 莆田市| 林州市| 金华市| 彭水| 霍城县| 厦门市| 安康市| 铜山县| 司法| 双城市| 饶河县| 闵行区| 通州区| 方山县| 斗六市| 乐昌市| 武乡县| 合山市| 新乐市| 芜湖县| 松潘县| 长沙市| 泰兴市| 黎川县| 东安县| 峨边| 大城县| 福鼎市| 九江县| 姚安县| 丰镇市| 航空| 沾化县| 合水县| 东乡县| 恩平市| 安岳县| 易门县|