一、摘要
這篇文章將介紹Spring整合Mybatis 如何完成SqlSessionFactory的動(dòng)態(tài)切換的。并且會(huì)簡(jiǎn)單的介紹下MyBatis整合Spring中的官方的相關(guān)代碼。
Spring整合MyBatis切換SqlSessionFactory有兩種方法
第一、 繼承SqlSessionDaoSupport,重寫(xiě)獲取SqlSessionFactory的方法。
第二、繼承SqlSessionTemplate 重寫(xiě)getSqlSessionFactory、getConfiguration和SqlSessionInterceptor這個(gè)攔截器。其中最為關(guān)鍵還是繼承SqlSessionTemplate 并重寫(xiě)里面的方法。
而Spring整合MyBatis也有兩種方式,一種是配置MapperFactoryBean,另一種則是利用MapperScannerConfigurer進(jìn)行掃描接口或包完成對(duì)象的自動(dòng)創(chuàng)建。相對(duì)來(lái)說(shuō)后者更方便些。
MapperFactoryBean繼承了SqlSessionDaoSupport也就是動(dòng)態(tài)切換SqlSessionFactory的第一種方法,我們需要重寫(xiě)和實(shí)現(xiàn)SqlSessionDaoSupport方法,或者是繼承MapperFactoryBean來(lái)重寫(xiě)覆蓋相關(guān)方法。如果利用MapperScannerConfigurer的配置整合來(lái)切換SqlSessionFactory,那么我們就需要繼承SqlSessionTemplate,重寫(xiě)上面提到的方法。在整合的配置中很多地方都是可以注入SqlSessionTemplate代替SqlSessionFactory的注入的。因?yàn)镾qlSessionTemplate的創(chuàng)建也是需要注入SqlSessionFactory的。
二、實(shí)現(xiàn)代碼
1、繼承SqlSessionTemplate 重寫(xiě)getSqlSessionFactory、getConfiguration和SqlSessionInterceptor
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
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
|
package com.hoo.framework.mybatis.support; 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 重寫(xiě)相關(guān)方法 * @author hoojo * @createDate 2013-10-18 下午03:07:46 * @file CustomSqlSessionTemplate.java * @package com.hoo.framework.mybatis.support * @project SHMB * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ 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()); } } } } |
重寫(xiě)后的getSqlSessionFactory方法會(huì)從我們配置的SqlSessionFactory集合targetSqlSessionFactorys或默認(rèn)的defaultTargetSqlSessionFactory中獲取Session對(duì)象。而改寫(xiě)的SqlSessionInterceptor 是這個(gè)MyBatis整合Spring的關(guān)鍵,所有的SqlSessionFactory對(duì)象的session都將在這里完成創(chuàng)建、提交、關(guān)閉等操作。所以我們改寫(xiě)這里的代碼,在這里獲取getSqlSessionFactory的時(shí)候,從多個(gè)SqlSessionFactory中獲取我們?cè)O(shè)置的那個(gè)即可。
上面添加了targetSqlSessionFactorys、defaultTargetSqlSessionFactory兩個(gè)屬性來(lái)配置多個(gè)SqlSessionFactory對(duì)象和默認(rèn)的SqlSessionFactory對(duì)象。
CustomerContextHolder 設(shè)置SqlSessionFactory的類型
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
|
package com.hoo.framework.mybatis.support; /** * <b>function:</b> 多數(shù)據(jù)源 * @author hoojo * @createDate 2013-9-27 上午11:36:57 * @file CustomerContextHolder.java * @package com.hoo.framework.spring.support * @project SHMB * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public abstract class CustomerContextHolder { public final static String SESSION_FACTORY_MYSQL = "mysql" ; public final static String SESSION_FACTORY_ORACLE = "oracle" ; 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(); } } |
2、配置相關(guān)的文件applicationContext-session-factory.xml
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
|
<?xml version= "1.0" encoding= "UTF-8" ?> <beans xmlns= "http://www.springframework.org/schema/beans" xmlns:aop= "http://www.springframework.org/schema/aop" xmlns:tx= "http://www.springframework.org/schema/tx" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" 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 "> <!-- 配置c3p0數(shù)據(jù)源 --> <bean id= "dataSourceOracle" class = "com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method= "close" > <property name= "driverClass" value= "${datasource.driver}" /> <property name= "jdbcUrl" value= "${datasource.url}" /> <property name= "user" value= "${datasource.username}" /> <property name= "password" value= "${datasource.password}" /> <property name= "acquireIncrement" value= "${c3p0.acquireIncrement}" /> <property name= "initialPoolSize" value= "${c3p0.initialPoolSize}" /> <property name= "minPoolSize" value= "${c3p0.minPoolSize}" /> <property name= "maxPoolSize" value= "${c3p0.maxPoolSize}" /> <property name= "maxIdleTime" value= "${c3p0.maxIdleTime}" /> <property name= "idleConnectionTestPeriod" value= "${c3p0.idleConnectionTestPeriod}" /> <property name= "maxStatements" value= "${c3p0.maxStatements}" /> <property name= "numHelperThreads" value= "${c3p0.numHelperThreads}" /> <property name= "preferredTestQuery" value= "${c3p0.preferredTestQuery}" /> <property name= "testConnectionOnCheckout" value= "${c3p0.testConnectionOnCheckout}" /> </bean> <bean id= "dataSourceMySQL" class = "com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method= "close" > <property name= "driverClass" value= "com.mysql.jdbc.Driver" /> <property name= "jdbcUrl" value= "jdbc:mysql://172.31.108.178:3306/world?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull" /> <property name= "user" value= "root" /> <property name= "password" value= "jp2011" /> <property name= "acquireIncrement" value= "${c3p0.acquireIncrement}" /> <property name= "initialPoolSize" value= "${c3p0.initialPoolSize}" /> <property name= "minPoolSize" value= "${c3p0.minPoolSize}" /> <property name= "maxPoolSize" value= "${c3p0.maxPoolSize}" /> <property name= "maxIdleTime" value= "${c3p0.maxIdleTime}" /> <property name= "idleConnectionTestPeriod" value= "${c3p0.idleConnectionTestPeriod}" /> <property name= "maxStatements" value= "${c3p0.maxStatements}" /> <property name= "numHelperThreads" value= "${c3p0.numHelperThreads}" /> <property name= "preferredTestQuery" value= "${c3p0.preferredTestQuery}" /> <property name= "testConnectionOnCheckout" value= "${c3p0.testConnectionOnCheckout}" /> </bean> <!-- 配置SqlSessionFactoryBean --> <bean id= "oracleSqlSessionFactory" class = "org.mybatis.spring.SqlSessionFactoryBean" > <property name= "dataSource" ref= "dataSourceOracle" /> <property name= "configLocation" value= "classpath:mybatis.xml" /> <!-- mapper和resultmap配置路徑 --> <property name= "mapperLocations" > <list> <!-- 表示在com.hoo目錄下的任意包下的resultmap包目錄中,以-resultmap.xml或-mapper.xml結(jié)尾所有文件 --> <value>classpath:com/hoo/framework/mybatis/mybatis-common.xml</value> <value>classpath:com/hoo /**/ resultmap /*-resultmap.xml</value> <value>classpath:com/hoo/**/mapper/*-mapper.xml</value> <value>classpath:com/hoo/**/mapper/**/*-mapper.xml</value> </list> </property> </bean> <!-- 配置SqlSessionFactoryBean --> <bean id="mysqlSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSourceMySQL"/> <property name="configLocation" value="classpath:mybatis.xml"/> <!-- mapper和resultmap配置路徑 --> <property name="mapperLocations"> <list> <!-- 表示在com.hoo目錄下的任意包下的resultmap包目錄中,以-resultmap.xml或-mapper.xml結(jié)尾所有文件 (oracle和mysql掃描的配置和路徑不一樣,如果是公共的都掃描 這里要區(qū)分下,不然就報(bào)錯(cuò) 找不到對(duì)應(yīng)的表、視圖)--> <value>classpath:com/hoo/framework/mybatis/mybatis-common.xml</value> <value>classpath:com/hoo/**/resultmap/*-mysql-resultmap.xml</value> <value>classpath:com/hoo/**/mapper/*-mysql-mapper.xml</value> <value>classpath:com/hoo/**/mapper/**/*-mysql-mapper.xml</value> <value>classpath:com/hoo/**/mapper/**/ multiple-datasource-mapper.xml</value> </list> </property> </bean> <!-- 配置自定義的SqlSessionTemplate模板,注入相關(guān)配置 --> <bean id= "sqlSessionTemplate" class = "com.hoo.framework.mybatis.support.CustomSqlSessionTemplate" > <constructor-arg ref= "oracleSqlSessionFactory" /> <property name= "targetSqlSessionFactorys" > <map> <entry value-ref= "oracleSqlSessionFactory" key= "oracle" /> <entry value-ref= "mysqlSqlSessionFactory" key= "mysql" /> </map> </property> </bean> <!-- 通過(guò)掃描的模式,掃描目錄在com/hoo/任意目錄下的mapper目錄下,所有的mapper都需要繼承SqlMapper接口的接口 --> <bean class = "org.mybatis.spring.mapper.MapperScannerConfigurer" > <property name= "basePackage" value= "com.hoo.**.mapper" /> <!-- 注意注入sqlSessionTemplate --> <property name= "sqlSessionTemplateBeanName" value= "sqlSessionTemplate" /> <property name= "markerInterface" value= "com.hoo.framework.mybatis.SqlMapper" /> </bean> </beans> |
上面的配置關(guān)鍵是在MapperScannerConfigurer中注入sqlSessionTemplate,這個(gè)要注意。當(dāng)我們配置了多個(gè)SqlSessionFactoryBean的時(shí)候,就需要為MapperScannerConfigurer指定一個(gè)sqlSessionFactoryBeanName或是sqlSessionTemplateBeanName。一般情況下注入了sqlSessionTemplateBeanName對(duì)象,那sqlSessionFactory也就有值了。如果單獨(dú)的注入了sqlSessionFactory那么程序會(huì)創(chuàng)建一個(gè)sqlSessionTemplate對(duì)象。我們可以看看代碼SqlSessionFactoryDaoSupport對(duì)象的代碼。如果你不喜歡使用掃描的方式,也可以注入sqlSessionTemplate或繼承sqlSessionTemplate完成數(shù)據(jù)庫(kù)操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public abstract class SqlSessionDaoSupport extends DaoSupport { private SqlSession sqlSession; private boolean externalSqlSession; public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { if (! this .externalSqlSession) { this .sqlSession = new SqlSessionTemplate(sqlSessionFactory); } } public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { this .sqlSession = sqlSessionTemplate; this .externalSqlSession = true ; } ...... |
這段代碼很明顯,如果注入了sqlSessionTemplate上面的注入也就不會(huì)執(zhí)行了。如果沒(méi)有注入sqlSessionTemplate,那么會(huì)自動(dòng)new一個(gè)sqlSessionTemplate對(duì)象。
3、編寫(xiě)相關(guān)測(cè)試接口和實(shí)現(xiàn)的mapper.xml
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
|
package com.hoo.server.datasource.mapper; import java.util.List; import java.util.Map; import com.hoo.framework.mybatis.SqlMapper; /** * <b>function:</b> MyBatis 多數(shù)據(jù)源 測(cè)試查詢接口 * @author hoojo * @createDate 2013-10-10 下午04:18:08 * @file MultipleDataSourceMapper.java * @package com.hoo.server.datasource.mapper * @project SHMB * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public interface MultipleDataSourceMapper extends SqlMapper { public List<Map<String, Object>> execute4MySQL() throws Exception; public List<Map<String, Object>> execute4Oracle() throws Exception; } multiple-datasource-mapper.xml <?xml version= "1.0" encoding= "UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace= "com.hoo.server.datasource.mapper.MultipleDataSourceMapper" > <select id= "execute4Oracle" resultType= "map" > <![CDATA[ SELECT * FROM deviceInfo_tab t where rownum < 10 ]]> </select> <select id= "execute4MySQL" resultType= "map" > <![CDATA[ SELECT * FROM city limit 2 ]]> </select> </mapper> |
上面分別查詢oracle和mysql兩個(gè)數(shù)據(jù)庫(kù)中的table
4、測(cè)試代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
@Autowired @Qualifier ( "multipleDataSourceMapper" ) private MultipleDataSourceMapper mapper; @Test public void testMapper() { CustomerContextHolder.setContextType(CustomerContextHolder.SESSION_FACTORY_MYSQL); try { trace(mapper.execute4MySQL()); } catch (Exception e1) { e1.printStackTrace(); } CustomerContextHolder.setContextType(CustomerContextHolder.SESSION_FACTORY_ORACLE); try { trace(mapper.execute4Oracle()); } catch (Exception e) { e.printStackTrace(); } } |
運(yùn)行后發(fā)現(xiàn)能夠順利查詢出數(shù)據(jù)。
如果你是重寫(xiě)SqlSessionDaoSupport,那么方法如下
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
|
package com.hoo.framework.mybatis.support; import java.util.Map; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionUtils; import org.mybatis.spring.support.SqlSessionDaoSupport; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; /** * <b>function:</b> MyBatis 動(dòng)態(tài)SqlSessionFactory * @author hoojo * @createDate 2013-10-14 下午02:32:19 * @file DynamicSqlSessionDaoSupport.java * @package com.hoo.framework.mybatis.support * @project SHMB * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public class DynamicSqlSessionDaoSupport extends SqlSessionDaoSupport implements ApplicationContextAware { private ApplicationContext applicationContext; private Map<Object, SqlSessionFactory> targetSqlSessionFactorys; private SqlSessionFactory defaultTargetSqlSessionFactory; private SqlSession sqlSession; @Override public final SqlSession getSqlSession() { SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(CustomerContextHolder.getContextType()); if (targetSqlSessionFactory != null ) { setSqlSessionFactory(targetSqlSessionFactory); } else if (defaultTargetSqlSessionFactory != null ) { setSqlSessionFactory(defaultTargetSqlSessionFactory); targetSqlSessionFactory = defaultTargetSqlSessionFactory; } else { targetSqlSessionFactory = (SqlSessionFactory) applicationContext.getBean(CustomerContextHolder.getContextType()); setSqlSessionFactory(targetSqlSessionFactory); } this .sqlSession = SqlSessionUtils.getSqlSession(targetSqlSessionFactory); return this .sqlSession; } @Override protected void checkDaoConfig() { //Assert.notNull(getSqlSession(), "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required"); } public void setTargetSqlSessionFactorys(Map<Object, SqlSessionFactory> targetSqlSessionFactorys) { this .targetSqlSessionFactorys = targetSqlSessionFactorys; } public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) { this .defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this .applicationContext = applicationContext; } } |
主要重寫(xiě)getSqlSession方法,上面獲取SqlSessionFactory的方法。
重寫(xiě)好了后就可以配置這個(gè)對(duì)象,配置代碼如下
1
2
3
4
5
6
7
|
//每一個(gè)DAO由繼承SqlSessionDaoSupport全部改為DynamicSqlSessionDaoSupport public class UserMapperDaoImpl extends DynamicSqlSessionDaoSupport implements UserDao { public int addUser(User user) { return this .getSqlSession().insert( "com.hoo.user.dao.UserDao.addUser" , user); } } |
在上面的配置文件中加入配置
1
2
3
4
5
6
7
8
9
10
|
<bean id= "baseDao" class = "com.hoo.framework.mybatis.support.DynamicSqlSessionDaoSupport" abstract = "true" lazy-init= "true" > <property name= "targetSqlSessionFactorys" > <map> <entry value-ref= "oracleSqlSessionFactory" key= "oracle" /> <entry value-ref= "mysqlSqlSessionFactory" key= "mysql" /> </map> </property> <property name= "defaultTargetSqlSessionFactory" ref= "oracleSqlSessionFactory" /> </bean> <bean id= "userMapperDao" class = "com.hoo.user.dao.impl.UserMapperDaoImpl" parent= "baseDao" /> |
就這樣也可以利用DynamicSqlSessionDaoSupport來(lái)完成動(dòng)態(tài)切換sqlSessionFactory對(duì)象,只需用在注入userMapperDao調(diào)用方法的時(shí)候設(shè)置下CustomerContextHolder的contextType即可。
三、總結(jié)
為了實(shí)現(xiàn)這個(gè)功能看了mybatis-spring-1.2.0.jar這個(gè)包的部分源代碼,代碼內(nèi)容不是很多。所以看了下主要的代碼,下面做些簡(jiǎn)單的介紹。
MapperScannerConfigurer這個(gè)類就是我們要掃描的Mapper接口的類,也就是basePackage中繼承markerInterface配置的接口。可以看看ClassPathBeanDefinitionScanner、ClassPathMapperScanner中的doScan這個(gè)方法。它會(huì)掃描basePackage這個(gè)包下所有接口,在ClassPathScanningCandidateComponentProvider中有這個(gè)方法findCandidateComponents,它會(huì)找到所有的BeanDefinition。
最重要的一點(diǎn)是ClassPathMapperScanner中的doScan這個(gè)方法它會(huì)給這些接口創(chuàng)建一個(gè)MapperFactoryBean。并且會(huì)檢查sqlSessionFactory和sqlSessionTemplate對(duì)象的注入情況。
image 所以我們配置掃描的方式也就相當(dāng)于我們?cè)谂渲梦募薪o每一個(gè)Mapper配置一個(gè)MapperFactoryBean一樣。而這個(gè)MapperFactoryBean又繼承SqlSessionDaoSupport。所以當(dāng)初我想重寫(xiě)MapperScannerConfigurer中的postProcessBeanDefinitionRegistry方法,然后重寫(xiě)方法中的ClassPathMapperScanner中的doScan方法,將definition.setBeanClass(MapperFactoryBean.class);改成自己定義的MapperFactoryBean。最后以失敗告終,因?yàn)檫@里是Spring裝載掃描對(duì)象的時(shí)候都已經(jīng)為這些對(duì)象創(chuàng)建好了代理、設(shè)置好了mapperInterface和注入需要的類。所以在調(diào)用相關(guān)操作數(shù)據(jù)庫(kù)的API方法的時(shí)候,設(shè)置對(duì)應(yīng)的SqlSessionFactory也是無(wú)效的。
輾轉(zhuǎn)反側(cè)我看到了SqlSessionTemplate這個(gè)類,它的功能相當(dāng)于SqlSessionDaoSupport的實(shí)現(xiàn)類MapperFactoryBean。最為關(guān)鍵的是SqlSessionTemplate有一個(gè)攔截器SqlSessionInterceptor,它復(fù)制所有SqlSession的創(chuàng)建、提交、關(guān)閉,而且是在每個(gè)方法之前。這點(diǎn)在上面也提到過(guò)了!所以我們只需要在SqlSessionInterceptor方法中獲取SqlSessionFactory的時(shí)候,在這之前調(diào)用下CustomerContextHolder.setContextType方法即可完成數(shù)據(jù)庫(kù)的SqlSessionFactory的切換。而在MapperScannerConfigurer提供了注入SqlSessionFactory和sqlSessionTemplate的方法,如果注入了SqlSessionFactory系統(tǒng)將會(huì)new一個(gè)sqlSessionTemplate,而注入了sqlSessionTemplate就不會(huì)創(chuàng)建其他對(duì)象(見(jiàn)下面代碼)。所以我們配置一個(gè)sqlSessionTemplate并注入到MapperScannerConfigurer中,程序?qū)?huì)使用這個(gè)sqlSessionTemplate。本文最后的實(shí)現(xiàn)方式就是這樣完成的。
1
2
3
4
5
6
7
8
9
|
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { if (! this .externalSqlSession) { this .sqlSession = new SqlSessionTemplate(sqlSessionFactory); } } public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { this .sqlSession = sqlSessionTemplate; this .externalSqlSession = true ; } |
以上所述是小編給大家介紹的Spring3 整合MyBatis3 配置多數(shù)據(jù)源動(dòng)態(tài)選擇SqlSessionFactory詳細(xì)教程,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)服務(wù)器之家網(wǎng)站的支持!
原文鏈接:http://www.blogjava.net/hoojo/archive/2013/10/22/405488.html