前言
最近因为工作的需要,要搭建的一个项目需要实现数据源的读写分离,在这里将代码进行分享,有需要的朋友们可以参考学习。
首先是配置数据源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<!--读数据源配置-->
<bean id= "readDataSource" class = "com.alibaba.druid.pool.DruidDataSource" init-method= "init" destroy-method= "close" >
//配置省略
</bean>
<!--写数据源配置-->
<bean id= "writeDataSource" class = "com.alibaba.druid.pool.DruidDataSource" init-method= "init" destroy-method= "close" >
//配置省略
</bean>
<!-- 动态数据源 -->
<bean id = "dataSource" class = "com.potato.common.bean.DynamicDataSource" >
<!-- 已配置的数据源 -->
<property name= "targetDataSources" >
<map>
<entry key= "READ" value-ref= "readDataSource" />
<entry key= "WRITE" value-ref= "writeDataSource" />
</map>
</property>
<!-- 默认的数据源 -->
<property name= "defaultTargetDataSource" ref= "writeDataSource" />
</bean>
|
数据源是如何切换的呢?通过动态数据源的配置我们知道原来是通过key来进行切换,这里要使用到org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
,我们可以编写自己的动态数据源类DynamicDataSource
来继承它。
1
2
3
4
5
6
|
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getType();
}
}
|
还需要一个存放key的地方DataSourceContextHolder
为保证切换时线程安全我们使用ThreadLocal
来保存我们的key。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public class DataSourceContextHolder {
private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceContextHolder. class );
public static final String DATA_SOURCE_WRITE = "WRITE" ;
public static final String DATA_SOURCE_READ = "READ" ;
// 线程本地环境
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
// 设置数据源类型
public static void setType(String type) {
if (LOGGER.isDebugEnabled())
LOGGER.debug( "==============切换数据源,类型:" +type+ "================" );
contextHolder.set(type);
}
// 获取数据源类型
public static String getType() {
return (contextHolder.get());
}
// 清除数据源类型
public static void clearType() {
contextHolder.remove();
}
}
|
好了,我们可以通过操作DataSourceContextHolder
来实现数据源动态的切换了。小伙伴们可能会说了,难道每次调用方法都要手动选择要切换的数据源类型?当然不是啦,Spring AOP登场。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@Component
@Aspect
public class DynamicDataSourceAspect {
@Pointcut ( "execution (* com.potato.orm.mapper.*.select*(..)) || execution (* com.potato.orm.mapper.*.count*(..)) " )
public void readMethodPointcut() {}
@Pointcut ( "execution (* com.potato.orm.mapper.*.insert*(..)) || execution (* com.potato.orm.mapper.*.update*(..)) || execution (* com.potato.orm.mapper.*.delete*(..))" )
public void writeMethodPointcut() {}
@Before ( "readMethodPointcut()" )
public void switchReadDataSource(){
//System.out.println("============切换到读数据源===========");
DataSourceContextHolder.setType(DataSourceContextHolder.DATA_SOURCE_READ);
}
@Before ( "writeMethodPointcut()" )
public void switchWriteDataSource(){
//System.out.println("=============切换到写数据源==========");
DataSourceContextHolder.setType(DataSourceContextHolder.DATA_SOURCE_WRITE);
}
}
|
总结
好啦,以上就是这篇文章的全部内容了,在访问Mapper(本项目使用的是MyBatis啦,相当于是DAO)中查询方法时会切换到读数据源,增、删、改方法会切换到写数据源。希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。
原文链接:http://www.jianshu.com/p/077395e7e66f