Spring + Mybatis 项目实现动态切换数据源实例详解

2025-05-29 0 92

项目背景:项目开发中数据库使用了读写分离,所有查询语句走从库,除此之外走主库。

最简单的办法其实就是建两个包,把之前数据源那一套配置copy一份,指向另外的包,但是这样扩展很有限,所有采用下面的办法。

参考了两篇文章如下:

https://www.zzvips.com/article/104411.html

https://www.zzvips.com/article/104412.html

这两篇文章都对原理进行了分析,下面只写自己的实现过程其他不再叙述。

实现思路是:

第一步,实现动态切换数据源:配置两个DataSource,配置两个SqlSessionFactory指向两个不同的DataSource,两个SqlSessionFactory都用一个SqlSessionTemplate,同时重写Mybatis提供的SqlSessionTemplate类,最后配置Mybatis自动扫描。

第二步,利用aop切面,拦截dao层所有方法,因为dao层方法命名的特点,比如所有查询sql都是select开头,或者get开头等等,拦截这些方法,并把当前数据源切换至从库。

spring中配置如下:

主库数据源配置:

?

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>

从库数据源配置:

?

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>

主库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>

从库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>

两个SqlSessionFactory使用同一个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自动扫描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();

}

}

最后就是写切面去对dao所有方法进行处理了,代码很简单如下:

?

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方法查询走从库

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 项目实现动态切换数据源实例详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对快网idc网站的支持!

原文链接:http://www.cnblogs.com/FlyHeLanMan/p/6744171.html

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 Spring + Mybatis 项目实现动态切换数据源实例详解 https://www.kuaiidc.com/117153.html

相关文章

发表评论
暂无评论