Spring动态数据源实现读写分离详解
程序员文章站
2023-12-10 09:08:34
一、创建基于threadlocal的动态数据源容器,保证数据源的线程安全性
package com.bounter.mybatis.extension;
/...
一、创建基于threadlocal的动态数据源容器,保证数据源的线程安全性
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数据源动态切换
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数据信息
# 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连接池,动态数据源
<!-- 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层方法前缀动态选取读、写数据源
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源码地址:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。