項(xiàng)目背景:項(xiàng)目開發(fā)中數(shù)據(jù)庫(kù)使用了讀寫分離,所有查詢語句走從庫(kù),除此之外走主庫(kù)。
最簡(jiǎn)單的辦法其實(shí)就是建兩個(gè)包,把之前數(shù)據(jù)源那一套配置copy一份,指向另外的包,但是這樣擴(kuò)展很有限,所有采用下面的辦法。
參考了兩篇文章如下:
http://www.ythuaji.com.cn/article/104411.html
http://www.ythuaji.com.cn/article/104412.html
這兩篇文章都對(duì)原理進(jìn)行了分析,下面只寫自己的實(shí)現(xiàn)過程其他不再敘述。
實(shí)現(xiàn)思路是:
第一步,實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源:配置兩個(gè)DataSource,配置兩個(gè)SqlSessionFactory指向兩個(gè)不同的DataSource,兩個(gè)SqlSessionFactory都用一個(gè)SqlSessionTemplate,同時(shí)重寫Mybatis提供的SqlSessionTemplate類,最后配置Mybatis自動(dòng)掃描。
第二步,利用aop切面,攔截dao層所有方法,因?yàn)閐ao層方法命名的特點(diǎn),比如所有查詢sql都是select開頭,或者get開頭等等,攔截這些方法,并把當(dāng)前數(shù)據(jù)源切換至從庫(kù)。
spring中配置如下:
主庫(kù)數(shù)據(jù)源配置:
1
2
3
4
5
|
<bean id= "masterDataSource" class = "com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method= "close" > 2 <property name= "driverClass" value= "${master_mysql_jdbc_driver}" /> <property name= "jdbcUrl" value= "${master_mysql_jdbc_url}" /> <property name= "user" value= "${master_mysql_jdbc_user}" /> <property name= "password" value= "${master_mysql_jdbc_password}" /> </bean> |
從庫(kù)數(shù)據(jù)源配置:
1
2
3
4
5
6
|
<bean id= "masterDataSource" class = "com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method= "close" > <property name= "driverClass" value= "${slave_mysql_jdbc_driver}" /> <property name= "jdbcUrl" value= "${slave_mysql_jdbc_url}" /> <property name= "user" value= "${slave_mysql_jdbc_user}" /> <property name= "password" value= "${slave_mysql_jdbc_password}" /> </bean> |
主庫(kù)SqlSessionFactory配置:
1
2
3
4
|
<bean id= "masterSqlSessionFactory" class = "org.mybatis.spring.SqlSessionFactoryBean" > <property name= "dataSource" ref= "masterDataSource" /> <property name= "mapperLocations" value= "classpath:com/sincetimes/slg/dao/*.xml" /> </bean> |
從庫(kù)SqlSessionFactory配置:
1
2
3
4
|
<bean id= "slaveSqlSessionFactory" class = "org.mybatis.spring.SqlSessionFactoryBean" > <property name= "dataSource" ref= "slaveDataSource" /> <property name= "mapperLocations" value= "classpath:com/sincetimes/slg/dao/*.xml" /> </bean> |
兩個(gè)SqlSessionFactory使用同一個(gè)SqlSessionTemplate配置:
1
2
3
4
5
6
7
8
9
|
<bean id= "MasterAndSlaveSqlSessionTemplate" class = "com.sincetimes.slg.framework.core.DynamicSqlSessionTemplate" > <constructor-arg index= "0" ref= "masterSqlSessionFactory" /> <property name= "targetSqlSessionFactorys" > <map> <entry value-ref= "masterSqlSessionFactory" key= "master" /> <entry value-ref= "slaveSqlSessionFactory" key= "slave" /> </map> </property> </bean> |
配置Mybatis自動(dòng)掃描dao
1
2
3
4
|
<bean class = "org.mybatis.spring.mapper.MapperScannerConfigurer" > <property name= "basePackage" value= "com.sincetimes.slg.dao" /> <property name= "sqlSessionTemplateBeanName" value= "MasterAndSlaveSqlSessionTemplate" /> </bean> |
自己重寫了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
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.sincetimes.slg.framework.core; 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; import com.sincetimes.slg.framework.util.SqlSessionContentHolder; /** * * TODO 重寫SqlSessionTemplate * @author ccg * @version 1.0 * Created 2017年4月21日 下午3:15:15 */ public class DynamicSqlSessionTemplate 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 Map<Object, SqlSessionFactory> getTargetSqlSessionFactorys(){ return targetSqlSessionFactorys; } public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) { this .defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory; } public DynamicSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { this (sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType()); } public DynamicSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) { this (sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration() .getEnvironment().getDataSource(), true )); } public DynamicSqlSessionTemplate(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(SqlSessionContentHolder.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( DynamicSqlSessionTemplate. this .getSqlSessionFactory(), DynamicSqlSessionTemplate. this .executorType, DynamicSqlSessionTemplate. this .exceptionTranslator); try { Object result = method.invoke(sqlSession, args); if (!isSqlSessionTransactional(sqlSession, DynamicSqlSessionTemplate. 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 (DynamicSqlSessionTemplate. this .exceptionTranslator != null && unwrapped instanceof PersistenceException) { Throwable translated = DynamicSqlSessionTemplate. this .exceptionTranslator .translateExceptionIfPossible((PersistenceException) unwrapped); if (translated != null ) { unwrapped = translated; } } throw unwrapped; } finally { closeSqlSession(sqlSession, DynamicSqlSessionTemplate. this .getSqlSessionFactory()); } } } } |
SqlSessionContentHolder類代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package com.sincetimes.slg.framework.util; public abstract class SqlSessionContentHolder { public final static String SESSION_FACTORY_MASTER = "master" ; public final static String SESSION_FACTORY_SLAVE = "slave" ; 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(); } } |
最后就是寫切面去對(duì)dao所有方法進(jìn)行處理了,代碼很簡(jiǎn)單如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package com.sincetimes.slg.framework.core; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import com.sincetimes.slg.framework.util.SqlSessionContentHolder; @Aspect public class DynamicDataSourceAspect { @Pointcut ( "execution( * com.sincetimes.slg.dao.*.*(..))" ) public void pointCut(){ } @Before ( "pointCut()" ) public void before(JoinPoint jp){ String methodName = jp.getSignature().getName(); //dao方法查詢走從庫(kù) if (methodName.startsWith( "query" ) || methodName.startsWith( "get" ) || methodName.startsWith( "count" ) || methodName.startsWith( "list" )){ SqlSessionContentHolder.setContextType(SqlSessionContentHolder.SESSION_FACTORY_SLAVE); } else { SqlSessionContentHolder.setContextType(SqlSessionContentHolder.SESSION_FACTORY_MASTER); } } } |
以上所述是小編給大家介紹的Spring + Mybatis 項(xiàng)目實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源實(shí)例詳解,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)服務(wù)器之家網(wǎng)站的支持!
原文鏈接:http://www.cnblogs.com/FlyHeLanMan/p/6744171.html