实现读写分离的方法
第一种:使用读写分离中间插件,实现读写分离,一般大公司会自己开发自己的中间件,中小型公司更多的是使用程序实现读写分离,中间件就不多介绍了。
第二种:就是在程序的读写分离,实现AbstractRoutingDataSource类的determineCurrentLookupKey方法
原理:借助spring的【org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource】这个抽象类实现。
每次去连数据库的时候,spring会调用determineCurrentLookupKey();这个方法去找对应的数据源。返回值即对应的数据源
/**
* Determine the current lookup key. This will typically be
* implemented to check a thread-bound transaction context.
* <p>Allows for arbitrary keys. The returned key needs
* to match the stored lookup key type, as resolved by the
* {@link #resolveSpecifiedLookupKey} method.
*/
protected abstract Object determineCurrentLookupKey();
第一步,配置spring-dao.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
<!-- 加载数据库配置文件和其他配置文件 -->
<context:property-placeholder location="classpath:database.properties"/>
<!-- 数据库连接池
<bean id="dataSource" class="org.apache.tomcat.dbcp.dbcp.BasicDataSource"
destroy-method="close">
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="minIdle" value="20"></property>
</bean> -->
<!-- 数据库连接池com.alibaba.druid.pool.DruidDataSource -->
<bean id="masterdataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="maxActive" value="100"></property>
<property name="minIdle" value="20"></property>
</bean>
<!-- 读库 -->
<bean id="slavedataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="${jdbc.r.url}"></property>
<property name="username" value="${jdbc.r.username}"></property>
<property name="password" value="${jdbc.r.password}"></property>
<property name="driverClassName" value="${jdbc.r.driver}"></property>
<property name="maxActive" value="100"></property>
<property name="minIdle" value="20"></property>
</bean>
<!-- 动态数据源 -->
<bean id="dynamicDataSource" class="com.tongzuwang.datasource.DynamicDataSource">
<!-- 通过key-value关联数据源 -->
<property name="targetDataSources">
<map>
<entry value-ref="masterdataSource" key="masterdataSource"></entry>
<entry value-ref="slavedataSource" key="slavedataSource"></entry>
</map>
</property>
<property name="defaultTargetDataSource" ref="masterdataSource" />
</bean>
<!-- 整合mybatis: SqlsessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据源 -->
<property name="dataSource" ref="dynamicDataSource"></property>
<!-- mybatis配置文件 -->
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property>
</bean>
<!-- 注入DAO对象:配置mapper MapperFactoryBean:用于生成mapper代理对象 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 配置扫描包的路径,如果要扫描多个包,中间使用逗号隔开 -->
<property name="basePackage" value="com.tongzuwang.able"></property>
<!-- 使用sqlSessionFactoryBeanName -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
</beans>
第二步,先继承AbstractRoutingDataSource 类实现determineCurrentLookupKey方法。
package com.tongzuwang.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
*
* override determineCurrentLookupKey
* Title: determineCurrentLookupKey
* Description: 自动查找datasource
*
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
return DBContextHolder.getDbType();
}
}
这个方法要返回的值。那么如何设置,让这个方法的返回值是根据我们的需要返回dataSource由于这个方法没有入参,并且是spring自动调用的,因此考虑使用静态变量存储dataSource的key,在调用sql语句前设置静态变量的值,然后在这个方法中得到静态变量的值,返回。又考虑到多线程,同时可能会有很多请求,为避免线程之间相互干扰,考虑使用threadLocal。
先看存储dataSourceKey的容器类
package com.tongzuwang.datasource;
public class DBContextHolder {
/**
* 线程threadlocal
*/
private static ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static String master = "masterdataSource";
public static String slave = "slavedataSource";
public static String getDbType() {
String db = contextHolder.get();
if (db == null) {
db = master;// 默认是读写库
}
return db;
}
/**
*
* 设置本线程的dbtype
*
* @param str
* @see [相关类/方法](可选)
* @since [产品/模块版本](可选)
*/
public static void setDbType(String str) {
contextHolder.set(str);
}
/**
* clearDBType
*
* @Title: clearDBType
* @Description: 清理连接类型
*/
public static void clearDBType() {
contextHolder.remove();
}
}
第三部,在访问数据之前使用DBContextHolder的静态方法,设置数据源,默认是主库
DBContextHolder .setDbType(DBContextHolder .master);//设置数据源为主库
这是,我在CSDN的第一篇文章,水平比较低,大家见谅!