激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|

服務器之家 - 編程語言 - JAVA教程 - 關于Spring3 + Mybatis3整合時多數據源動態切換的問題

關于Spring3 + Mybatis3整合時多數據源動態切換的問題

2020-09-16 15:17兔子黨-大胡子 JAVA教程

這篇文章主要介紹了關于Spring3 + Mybatis3整合時多數據源動態切換的問題,需要的朋友可以參考下

以前的項目經歷中,基本上都是spring + hibernate + Spring JDBC這種組合用的多。至于MyBatis,也就這個項目才開始試用,閑話不多說,進入正題。

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

所以當項目中碰到這個問題,我幾乎想都沒有想,就采用了這種做法,但是一測試,一點反應都沒有。當時覺得不可能,于是斷點,加log調試,發現determineCurrentLookupKey()根本沒有調用。 

為什么列? 這不可能啊。靜下心來,仔細想想,才想到一個關鍵的問題: Mybatis整合Spring,而不是Spring整合的Mybatis! 直覺告訴我,問題就出在這里。

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

好吧,這個類的作用列,就是產生sessionProxy。關鍵代碼如下

?
1
2
3
4
final SqlSession sqlSession = getSqlSession(
 SqlSessionTemplate.this.sqlSessionFactory,
 SqlSessionTemplate.this.executorType,
 SqlSessionTemplate.this.exceptionTranslator);

這個sqlSessionFactory 我們就很眼熟啦,是我們在spring配置文件中配了的,是吧,也就是說這東西是直接從我們配置文件中讀進來,但這東西,就關聯了Datasource。所以就想到,如果能把這東西,做到動態,那么數據源切換,也就動態了。

于是第一反應就是寫了一個類,然后在里面定義一個Map,用來存放多個SqlSessionFactory,并采用Setter方法進行屬性注入。

?
1
2
3
4
5
6
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的配置文件就變成了這樣:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
<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>

那么這個思想是那里來的列? 當然就是借鑒了Spring的動態數據源的思想啦,對比一下Spring動態數據源的配置,看看是不是差不多?

然后重寫了個關鍵的方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
  * 重寫得到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就很簡單,就是一個ThreadLocal的思想

?
1
2
3
4
5
6
7
8
9
10
11
12
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("當前線程Thread:"+Thread.currentThread().getName()+" 當前的數據源 key is "+ key);
  return key;
 }
}

博主信心滿滿就開始測試了。。結果發現不行,切換不過來,始終都是綁定的是構造函數中的那個默認的sqlSessionFactory,當時因為看了一天源碼,頭也有點暈。其實為什么列?

看看我們產生sessionProxy的地方代碼,他的sqlSessionFactory是直接從構造函數來拿的。而構造函數中的sqlSessionFactory在spring容器啟動時,就已經初始化好了,這點也可以從我們Spring配置文件中得到證實。

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

?
1
2
3
4
final SqlSession sqlSession = getSqlSession(   
EjsSqlSessionTemplate.this.getSqlSessionFactory(),   
EjsSqlSessionTemplate.this.getExecutorType(),    
EjsSqlSessionTemplate.this.getPersistenceExceptionTranslator());

再試,發現還是不行,再找原因,又回歸到了剛才那個問題。因為我沒有重寫SqlSessionTemplate的構造函數,而sqlSessionProxy是在構函數中初始化的,代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
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。 所以父類壓根就不會加載我寫的那個SqlSessionInterceptor()。所以問題就出在這,那好吧,博主又重寫構函數

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

很明顯這段代碼是編譯不通過的,構造函數中,怎么可能調用類實例方法列?  那怎么辦列? 又只有把父類的構造函數copy過來,那問題又有了,這些成員屬性又沒有。那又只得把他們也搬過來。。  后來,這個動態數據數據源的功能,終于完成了。

--------------------------------------------------------------------------------------------------------------------分割線-----------------------------------------------------------------------------------------------------------整個完整的代碼如下:

1、重寫SqlSessionTemplate (重寫的過程已經在上面分析過了)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
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。自定義了一個注解

?
1
2
3
4
5
6
7
8
9
10
11
12
/**
 * 注解式數據源,用來進行數據源切換
 * 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.定義一個AspectJ的切面(我習慣用AspectJ,因為spring AOP不支持cflow()這些語法),所以在編譯,打包的時候一定要用aspectJ的編譯器,不能直接用原生的JDK。有些方法就是我基于以前Hibernate,JDBC動態數據源的時候改動的。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/**
 * <li>類描述:完成數據源的切換,抽類切面,具體項目繼承一下,不需要重寫即可使用</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() {
 }
 /**
  * 對所有注解有ChooseDataSource的類進行攔截
  */
 @Pointcut("cflow(allServiceMethod()) && allServiceMethod()")
 public void changeDatasourcePoint() {
 }
 /**
  * 根據@ChooseDataSource的屬性值設置不同的dataSourceKey,以供DynamicDataSource
  */
 @Before("changeDatasourcePoint()")
 public void changeDataSourceBeforeMethodExecution(JoinPoint jp) {
  //拿到anotation中配置的數據源
  String resultDS = determineDatasource(jp);
  //沒有配置實用默認數據源
  if (resultDS == null) {
   SqlSessionContextHolder.setSessionFactoryKey(null);
   return;
  }
  preDatasourceHolder.set(SqlSessionContextHolder.getDataSourceKey());
  //將數據源設置到數據源持有者
  SqlSessionContextHolder.setSessionFactoryKey(resultDS);
 }
 /**
  * <p>創建時間: 2013-8-20 上午9:48:44</p>
  * 如果需要修改獲取數據源的邏輯,請重寫此方法
  *
  * @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;
 }
 /**
  * 方法執行完畢以后,數據源切換回之前的數據源。
  * 比如foo()方法里面調用bar(),但是bar()另外一個數據源,
  * bar()執行時,切換到自己數據源,執行完以后,要切換到foo()所需要的數據源,以供
  * foo()繼續執行。
  * <p>創建時間: 2013-8-16 下午4:27:06</p>
  */
 @After("changeDatasourcePoint()")
 public void restoreDataSourceAfterMethodExecution() {
  SqlSessionContextHolder.setSessionFactoryKey(preDatasourceHolder.get());
  preDatasourceHolder.remove();
 }
 /**
  * <li>創建時間: 2013-6-17 下午5:34:13</li> <li>創建人: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>創建時間: 2013-6-17 下午5:06:02</li>
  * <li>創建人:amos.zhou</li>
  * <li>方法描述 : 確定
  * 最終數據源,如果方法上設置有數據源,則以方法上的為準,如果方法上沒有設置,則以類上的為準,如果類上沒有設置,則使用默認數據源</li>
  *
  * @param classDS
  * @param methodDS
  * @return
  */
 private String determinateDataSource(String classDS, String methodDS) {
//  if (null == classDS && null == methodDS) {
//   return null;
//  }
  // 兩者必有一個不為null,如果兩者都為Null,也會返回Null
  return methodDS == null ? classDS : methodDS;
 }
 /**
  * <li>創建時間: 2013-6-17 下午4:33:03</li> <li>創建人:amos.zhou</li> <li>方法描述 : 類級別的 @ChooseDataSource
  * 的解析</li>
  *
  * @param targetClass
  * @return
  */
 @SuppressWarnings({"unchecked", "rawtypes"})
 private String resolveDataSourceFromClass(Class targetClass) {
  ChooseDataSource classAnnotation = (ChooseDataSource) targetClass
    .getAnnotation(ChooseDataSource.class);
  // 直接為整個類進行設置
  return null != classAnnotation ? resolveDataSourcename(classAnnotation)
    : null;
 }
 /**
  * <li>創建時間: 2013-6-17 下午4:31:42</li> <li>創建人:amos.zhou</li> <li>方法描述 :
  * 組裝DataSource的名字</li>
  *
  * @param ds
  * @return
  */
 private String resolveDataSourcename(ChooseDataSource ds) {
  return ds == null ? null : ds.value();
 }
}

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

那么項目中具體 怎么用列?

4.  在項目中定義一個具體的AspectJ切面

?
1
2
3
@Aspect
public class OrderFetchAspect extends ChooseDataSourceAspect {
}

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<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>
<!-- 事務管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
 <property name="dataSource" ref="dataSource" />
</bean>
<!-- 注解控制事務 -->
<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.具體應用

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@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);
  }
 }
}

測試發現 shopList1是從spider庫查出來的數據,而fromDB則是從sysinfo中查出來的數據。 那么我們就大功告成。
要做到我以上功能,Spring AOP是做不到的,因為他不支持Cflow(),這也就是我為什么要用AspectJ的原因。

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

好了,功能我們已經實現了,你有沒有覺得很麻煩,這一點也不Spring的風格,Spring的各個地方擴展都是很方便的。那么我們看看,在SqlSessionTemplate中的什么地方改動一下,我們就可以很輕松的實現這個功能列?大家可以理解了,再回去看一下源碼。

其實,只要將源碼中的那個SqlSessionInterceptor的這句話:

?
1
2
3
4
final SqlSession sqlSession = getSqlSession(
   SqlSessionTemplate.this.sqlSessionFactory,
   SqlSessionTemplate.this.executorType,
   SqlSessionTemplate.this.exceptionTranslator);

改為:

?
1
2
3
4
final SqlSession sqlSession = getSqlSession(
     EjsSqlSessionTemplate.this.getSqlSessionFactory(),
     EjsSqlSessionTemplate.this.executorType,    
         EjsSqlSessionTemplate.this.exceptionTranslator);

保證 每次在產生Session代理的時候,傳進去的參數都是調用getSqlSessionFactory()獲取,那么我們自定義的SqlSessionTemplate,只要重寫getSqlSessionFactory(),加多一個以下2句話:

?
1
2
3
4
private Map<Object, SqlSessionFactory> targetSqlSessionFactory;
 public void setTargetSqlSessionFactory(Map<Object, SqlSessionFactory> targetSqlSessionFactory) {
  this.targetSqlSessionFactory = targetSqlSessionFactory;
 }

那么就完全可以實現動態數據源切換。  那么mybatis-spring的項目團隊會這樣維護么? 我會以mail的方式與他們溝通。至于能否改進,我們不得而知了。

其實這也就引發一個關于面向對象設計時的思想,也是一直爭論得喋喋不休的一個問題:

    在類的方法中,如果要用到類的屬性時,是直接用this.filedName  來操作,還是用  getFiledName() 來進行操作?

其實以前我也是偏向于直接用this.屬性來進行操作的,但是經歷過這次以后,我想我會偏向于后者。

以上所述是小編給大家介紹的關于Spring3 + Mybatis3整合時多數據源動態切換的問題,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!

原文鏈接:http://blog.csdn.net/zl3450341/article/details/20150687

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 成年免费在线视频 | 欧美a级大胆视频 | 色阁阁69婷婷 | 久久草在线视频 | 狠狠干精品视频 | 亚洲国产高清视频 | 国产91免费看 | 欧美成人三级大全 | 国产一级伦理片 | 日韩电影视频 | 中文字幕精品亚洲 | 亚洲一区二区三区在线免费观看 | 日韩精品网站在线观看 | 中文字幕国 | 国产精品91久久久 | 久久99深爱久久99精品 | 欧美精品一区二区久久 | 亚洲第九十九页 | 欧美片一区二区 | 成人三级免费电影 | 99国产精品国产免费观看 | 亚洲情久久| 日韩视频在线观看免费视频 | 久久久精品网 | 国产一区视频在线免费观看 | 久久久久无码国产精品一区 | 一本色道久久综合亚洲精品图片 | 香蕉成人在线观看 | 黄网站在线免费看 | 久久国产精品无码网站 | 91精品影视 | 日韩视频在线视频 | 成人福利在线 | 久久91精品视频 | 久草在线看片 | 人与xxxxhdxxxhdxx 日韩黄a | 久久骚 | 久久99国产精品免费网站 | 午夜视频在线观 | 欧美成人午夜影院 | 午夜一级|