从零开发分布式数据库中间件 二、构建MyBatis的读写分离数据库中间件
2017-12-28 16:29
465 查看
从零开发分布式数据库中间件 二、构建MyBatis的读写分离数据库中间件
在上一节 从零开发分布式数据库中间件 一、读写分离的数据库中间件 中,我们讲了如何通过ThreadLocal来指定每次访问的数据源,并通过jdbc的连接方式来切换数据源,那么这一节我们使用我们常用的数据库持久层框架MyBatis来实现数据库读写分离。一、数据源代理:
此类与上一节相似,即可以指定当前线程访问的数据源。
[java] view plain copy print?package com.happyheng.datasource;
/**
* 数据源代理设置
* Created by happyheng on 17/1/15.
*/
public class DataSourceProxy {
private static ThreadLocal<DataSourceEnum> threadLocal = new ThreadLocal<>();
public enum DataSourceEnum {
MASTER,
SLAVE
}
/**
* 为当前线程设置数据源
*/
public static void setDataSource(DataSourceEnum sourceEnum) {
threadLocal.set(sourceEnum);
}
public static DataSourceEnum getDataSource() {
return threadLocal.get();
}
}
package com.happyheng.datasource;/** * 数据源代理设置 * Created by happyheng on 17/1/15. */public class DataSourceProxy { private static ThreadLocal<DataSourceEnum> threadLocal = new ThreadLocal<>(); public enum DataSourceEnum { MASTER, SLAVE } /** * 为当前线程设置数据源 */ public static void setDataSource(DataSourceEnum sourceEnum) { threadLocal.set(sourceEnum); } public static DataSourceEnum getDataSource() { return threadLocal.get(); }}
二、数据源Map:
首先我们需要将我们的读写数据源都写入到配置文件中,并设置到继承了AbstractRoutingDataSource抽象类的子类中,接下来我们会讲解AbstractRoutingDataSource的作用:
[java] view plain copy print?<bean id="masterDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${master.driver}" />
<property name="url" value="${master.dburl}" />
<property name="username" value="${master.user}" />
<property name="password" value="${master.password}" />
</bean>
<bean id="slaveDataSource1" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${slave1.driver}" />
<property name="url" value="${slave1.dburl}" />
<property name="username" value="${slave1.user}" />
<property name="password" value="${slave1.password}" />
</bean>
<bean id="slaveDataSource2" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${slave2.driver}" />
<property name="url" value="${slave2.dburl}" />
<property name="username" value="${slave2.user}" />
<property name="password" value="${slave2.password}" />
</bean>
<bean id="dataSource" class="com.happyheng.datasource.OptionalDataSource" >
<!-- 通过key-value的形式来关联数据源 -->
<property name="targetDataSources">
<map>
<entry key="masterDataSource" value-ref="masterDataSource" />
<entry key="slaveDataSource1" value-ref="slaveDataSource1" />
<entry key="slaveDataSource2" value-ref="slaveDataSource2" />
</map>
</property>
<property name="defaultTargetDataSource" ref="masterDataSource" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath*:mybatis/**/*Mapper.xml"/>
</bean>
<bean id="masterDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${master.driver}" /> <property name="url" value="${master.dburl}" /> <property name="username" value="${master.user}" /> <property name="password" value="${master.password}" /> </bean> <bean id="slaveDataSource1" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${slave1.driver}" /> <property name="url" value="${slave1.dburl}" /> <property name="username" value="${slave1.user}" /> <property name="password" value="${slave1.password}" /> </bean> <bean id="slaveDataSource2" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${slave2.driver}" /> <property name="url" value="${slave2.dburl}" /> <property name="username" value="${slave2.user}" /> <property name="password" value="${slave2.password}" /> </bean> <bean id="dataSource" class="com.happyheng.datasource.OptionalDataSource" > <!-- 通过key-value的形式来关联数据源 --> <property name="targetDataSources"> <map> <entry key="masterDataSource" value-ref="masterDataSource" /> <entry key="slaveDataSource1" value-ref="slaveDataSource1" /> <entry key="slaveDataSource2" value-ref="slaveDataSource2" /> </map> </property> <property name="defaultTargetDataSource" ref="masterDataSource" /> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="mapperLocations" value="classpath*:mybatis/**/*Mapper.xml"/> </bean>
二、AbstractRoutingDataSource数据源路由类:
在MyBatis中,需从SqlSessionFactory中获取dao文件,而SqlSessionFactory即需要数据源,因为我们需要根据不同的情况来选定数据源,所以不能写死一个数据源,而是应该将数据源写入到AbstractRoutingDataSource中的map中,AbstractRoutingDataSource即能够根据不同的情况指定访问的数据源。
还有需要注意的是,AbstractRoutingDataSource是一个抽象类,需要实现其determineCurrentLookupKey方法,来指定每次访问数据库的数据源。
下为继承了AbstractRoutingDataSource的OptionalDataSource类:
[java] view plain copy print?package com.happyheng.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* Created by happyheng on 17/1/10.
*/
public class OptionalDataSource extends AbstractRoutingDataSource {
// 数据源
private String masterDataSource = "masterDataSource";
private String[] slaveDataSource = {"slaveDataSource1", "slaveDataSource2"};
@Override
protected Object determineCurrentLookupKey() {
DataSourceProxy.DataSourceEnum dataSourceEnum = DataSourceProxy.getDataSource();
if (dataSourceEnum == DataSourceProxy.DataSourceEnum.SLAVE) {
double random = Math.random();
int randomIndex = (int)(random * slaveDataSource.length);
System.out.println("访问的是从数据库" + (randomIndex + 1));
return slaveDataSource[randomIndex];
} else {
System.out.println("访问的是主数据库");
return masterDataSource;
}
}
}
package com.happyheng.datasource;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/** * Created by happyheng on 17/1/10. */public class OptionalDataSource extends AbstractRoutingDataSource { // 数据源 private String masterDataSource = "masterDataSource"; private String[] slaveDataSource = {"slaveDataSource1", "slaveDataSource2"}; @Override protected Object determineCurrentLookupKey() { DataSourceProxy.DataSourceEnum dataSourceEnum = DataSourceProxy.getDataSource(); if (dataSourceEnum == DataSourceProxy.DataSourceEnum.SLAVE) { double random = Math.random(); int randomIndex = (int)(random * slaveDataSource.length); System.out.println("访问的是从数据库" + (randomIndex + 1)); return slaveDataSource[randomIndex]; } else { System.out.println("访问的是主数据库"); return masterDataSource; } }}
首先,我们将一主两从的数据源都写入到OptionalDataSource的map中,而每次MyBatis访问数据库时,都会调用此类的determineCurrentLookupKey()来获取数据源map中的key,从而得到对应的数据源。
可以看出,当我们发现是访问从数据库时,使用随机法来获取从数据库数据源,当发现是访问主数据库时,直接访问主数据库数据源。
4、此项目已在github上开源,可以完整实现MyBatis的数据库读写分离,地址为:github。如果觉得不错,那么就star一下来鼓励我吧。
相关文章推荐
- 从零开发分布式数据库中间件 二、构建MyBatis的读写分离数据库中间件
- 从零开发分布式数据库中间件 二、构建MyBatis的读写分离数据库中间件
- 从零开发分布式数据库中间件 一、读写分离的数据库中间件
- 从零开发分布式数据库中间件 一、读写分离的数据库中间件(转)
- 从零开发分布式数据库中间件 一、读写分离的数据库中间件
- MyCat:开源分布式数据库中间件之数据库分片和读写分离配置
- 数据库的主从配置 读写分离 中间件之-------Amoeba
- 框架学习系列 mybatis 第6篇 mybatis入门程序之需求开发3向数据库中插入数据1
- mysql 主从数据库备份与读写分离中间件比较(TDDL、Amoeba、Cobar、MyCat)
- MyCat 读写分离 数据库分库分表 中间件 安装部署,及简单使用
- J2EE项目系列(四)--SSM框架构建积分系统和基本商品检索系统(Spring+SpringMVC+MyBatis+Lucene+Redis+MAVEN)(2)建立商品数据库和Lucene的搭建
- 【方正中间件】用平台如何进行连远程服务器开发(JAVA版本/数据库SQLServer)
- 使用mysql 中间件 Atlas 实现数据库读写分离
- 30分钟构建专属数据库中间件
- MyCat 读写分离 数据库分库分表 中间件 安装部署,及简单使用
- mysql+spring+mybatis实现数据库读写分离[代码配置] .
- 数据库中间件 Sharding-JDBC 源码分析 —— JDBC实现与读写分离
- Mycat开发实践---数据库中间件架构分析
- Oracle安全实战——开发完全的数据库与中间件环境
- spring+mybatis利用interceptor(plugin)兑现数据库读写分离