从零开发分布式数据库中间件 二、构建MyBatis的读写分离数据库中间件
在上一节 从零开发分布式数据库中间件 一、读写分离的数据库中间件 中,我们讲了如何通过ThreadLocal来指定每次访问的数据源,并通过jdbc的连接方式来切换数据源,那么这一节我们使用我们常用的数据库持久层框架MyBatis来实现数据库读写分离。
一、数据源代理:
此类与上一节相似,即可以指定当前线程访问的数据源。
- 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的作用:
- <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类:
- 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的读写分离数据库中间件的更多相关文章
- MySQL中间件之ProxySQL(10):读写分离方法论
返回ProxySQL系列文章:http://www.cnblogs.com/f-ck-need-u/p/7586194.html 1.不同类型的读写分离 数据库中间件最基本的功能就是实现读写分离,Pr ...
- ASP.NET开发实战——(十二)ASP.NET MVC 与数据库之Entity Framework Migrations
在开发数据库应用程序的时候,经常会遇到某些表需要添加字段或者修改类型.新增表等需求,而对于EF Code First来说关注的只有实体类,当需求变更时只需要添加新的实体类或者在实体类中添加.删除.修改 ...
- 第十二章 LNMP架构之分离数据库
一.课程回顾 1.搭建LNMP环境 1.配置官方源2.yum安装依赖3.yum安装nginx4.配置nginx5.创建用户6.启动并加入开机自启7.上传安装包8.解压安装包9.卸载旧版本PHP10. ...
- mycat数据库集群系列之mycat读写分离安装配置
最近在梳理数据库集群的相关操作,现在花点时间整理一下关于mysql数据库集群的操作总结,恰好你又在看这一块,供一份参考.本次系列终结大概包括以下内容:多数据库安装.mycat部署安装.数据库之读写分离 ...
- mysql数据库的主从同步,实现读写分离 g
https://blog.csdn.net/qq_15092079/article/details/81672920 前言 1 分别在两台centos 7系统上安装mysql 5.7 2 master ...
- mysql数据库的主从同步,实现读写分离
大型网站为了软解大量的并发访问,除了在网站实现分布式负载均衡,远远不够.到了数据业务层.数据访问层,如果还是传统的数据结构,或者只是单单靠一台服务器来处理如此多的数据库连接操作,数据库必然会崩溃,特别 ...
- Excel开发之旅(二)----数据的读写
1.要实现数据的读写,首先,我们需要添加引用: using Excel=Microsoft.Office.Interop.Excel; 直接在项目中添加即可. 2.给3个按钮添加响应事件,工程代码截图 ...
- 【MS SQL】数据库维护计划之数据库备份(二)
原文:[MS SQL]数据库维护计划之数据库备份(二) 上篇[MS SQL]数据库维护计划之数据库备份(一) 说了数据库备份的一些概念后,这篇以HRP_KQYY数据库备份为例,进行备份计划设置. 考虑 ...
- C# 动态创建SQL数据库(二) 在.net core web项目中生成二维码 后台Post/Get 请求接口 方式 WebForm 页面ajax 请求后台页面 方法 实现输入框小数多 自动进位展示,编辑时实际值不变 快速掌握Gif动态图实现代码 C#处理和对接HTTP接口请求
C# 动态创建SQL数据库(二) 使用Entity Framework 创建数据库与表 前面文章有说到使用SQL语句动态创建数据库与数据表,这次直接使用Entriy Framwork 的ORM对象关 ...
随机推荐
- office2007下载地址
thunder://QUFodHRwOi8vNDYuZHVvdGUub3JnOjgwODAvb2ZmaWNlMjAwN3Byby56aXBaWg==thunder://QUFodHRwOi8vNTEu ...
- SQL 系统表
http://www.cnblogs.com/asdcer/archive/2007/05/14/746377.aspx
- Locked Treasure
题意: 有一宝箱,宝箱上有一些锁,有n个人,每个人分管一些钥匙(每个锁对应无数个钥匙), 现给出n,m 求问至少需要多少锁才能存在一种钥匙分配方案使得: 1.至少m个人才可以打开锁. 2.任选m个人一 ...
- web.xml报错Cannot resolve class 'StrutsPrepareAndExecuteFilter' (idea创建SSH项目)
原因: xwork-core.jar包已经合并到struts2-core.jar下,并且点开jar包,发现没有 org.apache.struts2.dispatcher.ng.filter.Stru ...
- HDU-2616
Kill the monster Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- 【Data Structure & Algorithm】字符串全排列
字符串全排列 题目:输入一个字符串,打印出该字符串的所有排列.例如输入字符串abc,则输出由字符a.b.c所能排列出来的所有字符串abc.acb.bac.bca.cab.cba. 分析:考察对递归的理 ...
- C# 选择文件路径,选择文件
// 选择文件: private string SelectPath() { string path = string.Empty; var openFileDialog = new Microsof ...
- hdoj5289【RMQ+二分】【未完待续】
思路: 对当前值查找最近满足位置: 利用RMQ求出区间最大最小值,再枚举右端点,二分区间找到满足要求的最大区间累加
- Some JPR highlights (JPR 2019 March)
Journal Name:Journal of Proteome Research Issue:2019 March Shared by: Weining Zhao 1. Acetylome: ...
- sed 删除指定行
参考:http://blog.sina.com.cn/s/blog_4ba5b45e0102e7l2.html