用于以后方便查看,借用:https://blog.csdn.net/weixin_43882034/article/details/84996443
1.创建 DyncmicDataSourceHolder 类
package cn.ljs.dyncmic; import com.alibaba.dubbo.common.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 在读写分离的实现上, * Spring为我们提供了路由数据源的AbstractRoutingDataSource抽象类, * 我们只需编写一个新的类继承它, * 即可在不改变原有的代码的基础上实现读写分离 */ public class DyncmicDataSourceHolder { private static Logger logger= LoggerFactory.getLogger(DyncmicDataSourceHolder.class); /** * 保证线程安全,使用线程threadlocal */ private static ThreadLocal<String> contextHolder=new ThreadLocal<String>(); public static final String DB_MASTER="master"; public static final String DB_SLAVE="slave"; /** * @return java.lang.String * @description 获取连接类型 */ public static String getDbType(){ String db=contextHolder.get(); if (StringUtils.isBlank(db)) { db=DB_MASTER; } return db; } /** * 设置数据源类型 * @param str */ public static void setDbType(String str){ logger.debug("使用数据源类型---"+str); System.err.println("使用数据源类型---"+str); contextHolder.set(str); } /** * 清理连接类型 */ public static void clearDaType(){ contextHolder.remove(); } }
2.创建 DynamicDataSource,继承AbstractRoutingDataSource
package cn.ljs.dyncmic; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DyncmicDataSourceHolder.getDbType(); } }
3 创建DyncmicDataSourceInterceptor,mybatis拦截器
package cn.ljs.dyncmic; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.keygen.SelectKeyGenerator; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.plugin.*; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.transaction.support.TransactionSynchronizationManager; import java.util.Locale; import java.util.Properties; /** * 创建DyncmicDataSourceInterceptor,mybatis拦截器 */ @Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})}) public class DyncmicDataSourceInterceptor implements Interceptor { private static Logger logger = LoggerFactory.getLogger(DyncmicDataSourceInterceptor.class); // 写操作的正则表达式 private static final String REGEX = ".*insert\u0020.*||.*delete\u0020|.*update\u0020.*"; @Override public Object intercept(Invocation invocation) throws Throwable { // 判断方法是否是被事务管理的 boolean synchronizationAction = TransactionSynchronizationManager.isActualTransactionActive(); //获取sql 中的参数 Object[] objects = invocation.getArgs(); MappedStatement mappedStatement = (MappedStatement) objects[0]; //数据源key String lookupKey = DyncmicDataSourceHolder.DB_MASTER; if (!synchronizationAction) { // 读方法 if (mappedStatement.getSqlCommandType().equals(SqlCommandType.SELECT)) { //selectKey为自增id查询主键(SELECT LAST_INSERT_ID)方法 if (mappedStatement.getId().contains(SelectKeyGenerator.SELECT_KEY_SUFFIX)) { lookupKey = DyncmicDataSourceHolder.DB_MASTER; } else { //获取sql 语句 BoundSql boundSql = mappedStatement.getSqlSource().getBoundSql(objects[1]); String sql = boundSql.getSql().toLowerCase(Locale.CHINA).replace("\t\n\r", " "); if (sql.matches(REGEX)) {//insert、update、delete lookupKey = DyncmicDataSourceHolder.DB_MASTER; } else {//select lookupKey = DyncmicDataSourceHolder.DB_SLAVE; } } } } else { lookupKey = DyncmicDataSourceHolder.DB_MASTER; } logger.debug("设置方法[{}] use [{}] Strategy,SqlCommandType [{}]...", mappedStatement.getId(), lookupKey, mappedStatement.getSqlCommandType().name()); DyncmicDataSourceHolder.setDbType(lookupKey); return invocation.proceed(); } @Override public Object plugin(Object target) { //Executor表示含有增删改查的操作 的对象 if (target instanceof Executor) { //有增删改查的操作,就调用拦截方法 return Plugin.wrap(target, this); } else { //无增删改查的操作。不做处理 return target; } } @Override public void setProperties(Properties properties) { } }
4.修改mybatis-config,引入拦截器
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!-- 打印查询语句 --> <!--<setting name="logImpl" value="STDOUT_LOGGING" />--> <!--解决,查询返回结果含null没有对应字段值问题--> <setting name="callSettersOnNulls" value="true"/> </settings> <!-- 设置别名 --> <typeAliases> <package name="cn.ljs.entity" /> </typeAliases> <plugins> <plugin interceptor="cn.ljs.dyncmic.DyncmicDataSourceInterceptor"></plugin> </plugins> </configuration>
5,修改mybatis配置文件,
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 配置base数据源 --> <!-- Druid --> <bean id="masterDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 基本属性 url、user、password --> <!-- 本地 --> <property name="url" value="jdbc:mysql://localhost:3306/new_cgpt?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true" /> <property name="username" value="root" /> <property name="password" value="root" /> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="0" /> <property name="minIdle" value="0" /> <property name="maxActive" value="100" /> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="60000" /> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="60000" /> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="300000" /> <property name="validationQuery" value="select 1 from dual" /> <property name="testWhileIdle" value="true" /> <property name="testOnBorrow" value="false" /> <property name="testOnReturn" value="false" /> <!-- 打开PSCache,并且指定每个连接上PSCache的大小 --> <property name="poolPreparedStatements" value="true" /> <property name="maxPoolPreparedStatementPerConnectionSize" value="20" /> <!-- 对于长时间不使用的连接强制关闭 --> <property name="removeAbandoned" value="true" /> </bean> <bean id="slaveDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 本地 --> <property name="url" value="jdbc:mysql://localhost:3306/cgpt?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true" /> <property name="username" value="root" /> <property name="password" value="root" /> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="0" /> <property name="minIdle" value="0" /> <property name="maxActive" value="100" /> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="60000" /> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="60000" /> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="300000" /> <property name="validationQuery" value="select 1 from dual" /> <property name="testWhileIdle" value="true" /> <property name="testOnBorrow" value="false" /> <property name="testOnReturn" value="false" /> <!-- 打开PSCache,并且指定每个连接上PSCache的大小 --> <property name="poolPreparedStatements" value="true" /> <property name="maxPoolPreparedStatementPerConnectionSize" value="20" /> <!-- 对于长时间不使用的连接强制关闭 --> <property name="removeAbandoned" value="true" /> </bean> <!--配置动态数据源--> <bean id="dynamicDataSource" class="cn.ljs.dyncmic.DynamicDataSource"> <property name="targetDataSources"> <map> <entry key="master" value-ref="masterDataSource"/> <entry key="slave" value-ref="slaveDataSource"/> </map> </property> </bean> <!--由于在运行时决定使用哪个数据源,所以使用懒加载--> <bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy"> <property name="targetDataSource"> <ref bean="dynamicDataSource"/> </property> </bean> <!-- 注册SqlSessionFactoryBean --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <!-- 自动扫描mappers.xml文件 --> <property name="mapperLocations" value="classpath:cn/ljs/mapper/*.xml" /> <property name="configLocation" value="classpath:mybatis-config.xml"></property> </bean> <!-- DAO接口所在包名,Spring会自动查找其下的类 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="cn.ljs.dao" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean> </beans>