一、创建基于ThreadLocal的动态数据源容器,保证数据源的线程安全性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package com.bounter.mybatis.extension;
/**
* 基于ThreadLocal实现的动态数据源容器,保证DynamicDataSource的线程安全性
* @author simon
*
*/
public class DynamicDataSourceHolder {
private static final ThreadLocal<String> dataSourceHolder = new ThreadLocal<>();
public static void setDataSource(String dataSourceKey) {
dataSourceHolder.set(dataSourceKey);
}
public static String getDataSource() {
return dataSourceHolder.get();
}
public static void clearDataSource() {
dataSourceHolder.remove();
}
}
|
二、定义Spring动态数据源扩展类,用来实现Master、Slave数据源动态切换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package com.bounter.mybatis.extension;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 自定义的Spring 动态数据源扩展类,用来实现Master、Slave数据源动态切换
* @author simon
*
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
//使用DynamicDataSourceHolder保证线程安全
return DynamicDataSourceHolder.getDataSource();
}
}
|
三、配置Master、Slave数据源
1. db.properties配置Master、Slave数据信息
1
2
3
4
5
6
7
8
9
10
11
|
# Master DB
db.master.url=jdbc:mysql://192.168.168.110:3306/bounter?useUnicode= true &characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowMultiQueries= true &serverTimezone=PRC&useSSL= false
db.master.username=bounter
# AES encrypt,Base64 encode
db.master. password =ZNhnEjauk3pecZxxS84ofA==
# Slave DB
db.slave.url=jdbc:mysql://192.168.168.111:3306/ database ?useUnicode= true &characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowMultiQueries= true &serverTimezone=PRC&useSSL= false
db.slave.username=bounter
# AES encrypt,Base64 encode
db.slave. password =jFYmt2f57RHhzItYDhWiSA==
|
2. Spring 配置文件配置Master、Slave连接池,动态数据源
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
|
<!-- Master数据源 -->
<bean id= "masterDataSource" class = "com.alibaba.druid.pool.DruidDataSource"
init-method= "init" destroy-method= "close" >
<!-- 基本属性 url、user、password -->
<property name= "url" value= "${db.master.url}" />
<property name= "username" value= "${db.master.username}" />
<property name= "password" value= "${db.master.password}" />
<!-- 配置初始化大小、最小、最大 -->
<property name= "initialSize" value= "20" />
<property name= "minIdle" value= "1" />
<property name= "maxActive" value= "40" />
<!-- 配置获取连接等待超时的时间 -->
<property name= "maxWait" value= "60000" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name= "timeBetweenEvictionRunsMillis" value= "60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name= "minEvictableIdleTimeMillis" value= "300000" />
<property name= "validationQuery" value= "SELECT 'x'" />
<property name= "testWhileIdle" value= "true" />
<property name= "testOnBorrow" value= "false" />
<property name= "testOnReturn" value= "false" />
<!-- 配置监控统计拦截的filters -->
<property name= "filters" value= "stat" />
</bean>
<!-- Slave数据源 -->
<bean id= "slaveDataSource" class = "com.alibaba.druid.pool.DruidDataSource"
init-method= "init" destroy-method= "close" >
<!-- 基本属性 url、user、password -->
<property name= "url" value= "${db.slave.url}" />
<property name= "username" value= "${db.slave.username}" />
<property name= "password" value= "${db.slave.password}" />
<!-- 配置初始化大小、最小、最大 -->
<property name= "initialSize" value= "20" />
<property name= "minIdle" value= "1" />
<property name= "maxActive" value= "40" />
<!-- 配置获取连接等待超时的时间 -->
<property name= "maxWait" value= "60000" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name= "timeBetweenEvictionRunsMillis" value= "60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name= "minEvictableIdleTimeMillis" value= "300000" />
<property name= "validationQuery" value= "SELECT 'x'" />
<property name= "testWhileIdle" value= "true" />
<property name= "testOnBorrow" value= "false" />
<property name= "testOnReturn" value= "false" />
<!-- 配置监控统计拦截的filters -->
<property name= "filters" value= "stat" />
</bean>
<!-- 自定义动态数据源 -->
<bean id= "dataSource" class = "com.bounter.mybatis.extension.DynamicDataSource" >
<property name= "targetDataSources" >
<map key-type= "java.lang.String" >
<!-- 配置读写数据源 -->
<entry value-ref= "masterDataSource" key= "write" ></entry>
<entry value-ref= "slaveDataSource" key= "read" ></entry>
</map>
</property>
<property name= "defaultTargetDataSource" ref= "masterDataSource" ></property>
</bean>
|
四、创建数据源切面,通过AOP实现根据Dao层方法前缀动态选取读、写数据源
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
|
package com.bounter.mybatis.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import com.bounter.mybatis.extension.DynamicDataSourceHolder;
/**
* 数据源切面,通过dao方法前缀决定访问读、写数据源
* @author simon
*
*/
@Component
@Aspect
@EnableAspectJAutoProxy (proxyTargetClass = true )
public class DataSourceAspect {
//读库数据源key
private static final String DATASOURCE_KEY_READ = "read" ;
//查询方法清单
String[] queryMethods = { "find" , "get" , "query" , "count" , "select" };
/**
* dao层方法执行前选择数据源
* @param point
*/
@Before ( "execution(* com.bounter.mybatis.dao..*.*(..))" )
public void before(JoinPoint point) {
// 获取到当前执行的方法名
String methodName = point.getSignature().getName();
//匹配查询方法
for (String queryMethod : queryMethods) {
if (methodName.startsWith(queryMethod)) {
//查询方法设置数据源为读库
DynamicDataSourceHolder.setDataSource(DATASOURCE_KEY_READ);
break ;
}
}
}
/**
* dao层方法执行完后清空数据源选择
* @param point
*/
@After ( "execution(* com.bounter.mybatis.dao..*.*(..))" )
public void after(JoinPoint point) {
DynamicDataSourceHolder.clearDataSource();
}
}
|
github源码地址:https://github.com/13babybear/bounter-mybatis
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。