仍是动态数据源的那一套,先要继承java
AbstractRoutingDataSource
1spring
package com.statistics.util; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * 用户 :LX * 建立时间: 2018/7/4. 23:49 * 地点:广州 * 目的: 动态数据源,若是是作读写分离,能够用这个类来作读写的自动切换数据库,可是这里只是测试 * 结果: * 1 AbstractRoutingDataSource用来作动态数据源切换的类,要继承他才行 * 2 建立 DynamicDataSourceHolder 类,用来作操做数据源 * 3 编写 DynamicDataSourceInterceptor 类来自动切换数据源 * 4 在mybatis的配置文件中去设置 DynamicDataSourceInterceptor 拦截器 * 5 spring中对数据源进行配置 * 6 写好注解,哪些要拦截 */ public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceHolder.getDbType(); } }
而后建立数据库操做类sql
package com.statistics.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 用户 :LX * 建立时间: 2018/7/4. 23:52 * 地点:广州 * 目的: 动态数据源的操做 * 结果: */ public class DynamicDataSourceHolder { //日志对象 private static Logger logger = LoggerFactory.getLogger(DynamicDataSourceHolder.class); /** * 线程安全的本地线程类 */ private static ThreadLocal<String> contextHolder = new ThreadLocal<String>(); /** * 主数据库 */ public static final String DB_MASTER = "master"; /** * 从库 */ public static final String DB_SLAVE = "slave"; /** * 获取数据源 * @return */ public static String getDbType(){ String db = contextHolder.get(); logger.debug("getDbType方法中从线程安全的里面获取到:" + db); if (db == null){ db = DB_MASTER; } return db; } /** * 注入线程的数据源 * @param str */ public static void setDbType(String str){ logger.debug("所注入使用的数据源:" + str); contextHolder.set(str); } /** * 清理链接 */ public static void clearDBType(){ contextHolder.remove(); } }
最关键的地方就是下面的这个类数据库
package com.statistics.util; 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; /** * 用户 :LX * 建立时间: 2018/7/5. 0:02 * 地点:广州 * 目的: 数据源的拦截器,靠这个来进行切换读写时候的数据源 * 结果: * Interceptor 就是mybatis的 Interceptor,mybatis的拦截器 */ //@Intercepts标记了这是一个Interceptor,而后在@Intercepts中定义了两个@Signature,即两个拦截点。第一个@Signature咱们定义了该Interceptor将拦截Executor接口中参数类型 // 为MappedStatement、Object、RowBounds和ResultHandler的query方法;第二个@Signature咱们定义了该Interceptor将拦截StatementHandler中参数类型为Connection的prepare方法。 // 只要拦截了update和query便可,全部的增删改查都会封装在update和query中 @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 DynamicDataSourceInterceptor implements Interceptor { //日志对象 private static Logger logger = LoggerFactory.getLogger(DynamicDataSourceInterceptor.class); /** * 判断是插入仍是增长仍是删除之类的正则, u0020是空格 */ private static final String regex = ".*insert\\u0020.*|.*delete\\u0020.*|.update\\u0020.*"; /** * 咱们要操做的主要拦截方法,什么状况下去拦截,就看这个了 * @param invocation * @return * @throws Throwable */ @Override public Object intercept(Invocation invocation) throws Throwable { //判断当前是否有实际事务处于活动状态 true 是 boolean synchronizationActive = TransactionSynchronizationManager.isActualTransactionActive(); //获取sql的资源变量参数(增删改查的一些参数) Object[] objects = invocation.getArgs(); //MappedStatement 能够获取到究竟是增长仍是删除 仍是修改的操做 MappedStatement mappedStatement = (MappedStatement) objects[0]; //用来决定datasource的 String lookupKey = DynamicDataSourceHolder.DB_MASTER; if (synchronizationActive != true){ //当前的是有事务的====================Object[0]=org.apache.ibatis.mapping.MappedStatement@c028cc logger.debug("当前的是有事务的====================Object[0]=" + objects[0]); //读方法,说明是 select 查询操做 if (mappedStatement.getSqlCommandType().equals(SqlCommandType.SELECT)){ //若是selectKey 为自增id查询主键(select last_insert_id()方法),使用主库,这个查询是自增主键的一个查询 if (mappedStatement.getId().contains(SelectKeyGenerator.SELECT_KEY_SUFFIX)){ //使用主库 lookupKey = DynamicDataSourceHolder.DB_MASTER; } else { //获取到绑定的sql BoundSql boundSql = mappedStatement.getSqlSource().getBoundSql(objects[1]); String sqlstr = boundSql.getSql(); //toLowerCase方法用于把字符串转换为小写,replaceAll正则将全部的制表符转换为空格 String sql = sqlstr.toLowerCase(Locale.CHINA).replaceAll("[\\t\\n\\r]", " "); //sql是这个===================:select top 1 * from cipentinfo where regno=? logger.debug("sql是这个===================:" + sql); //使用sql去匹配正则,看他是不是增长、删除、修改的sql,若是是则使用主库 if (sql.matches(regex)){ lookupKey = DynamicDataSourceHolder.DB_MASTER; } else { //从读库(从库),注意,读写分离后必定不能将数据写到读库中,会形成很是麻烦的问题 lookupKey = DynamicDataSourceHolder.DB_SLAVE; } } } } else { //非事务管理的用主库 lookupKey = DynamicDataSourceHolder.DB_MASTER; } //设置方法[slave] ues [SELECT] Strategy,SqlCommanType[SELECT],sqlconmantype[{}]......................... cipentinfoNamespace.selectOneByRegNo logger.debug("设置方法[{}] ues [{}] Strategy,SqlCommanType[{}],sqlconmantype[{}]......................... " + mappedStatement.getId(), lookupKey, mappedStatement.getSqlCommandType().name(), mappedStatement.getSqlCommandType()); //最终决定使用的数据源 DynamicDataSourceHolder.setDbType(lookupKey); return invocation.proceed(); } /** * 返回封装好的对象,决定返回的是本体仍是编织好的代理 * @param target * @return */ @Override public Object plugin(Object target) { //Executor是mybatis的,全部的增删改查都会通过这个类 if (target instanceof Executor){ //若是是Executor 那就进行拦截 return Plugin.wrap(target, this); } else { //不然返回本体 return target; } } /** * 类初始化的时候作一些操做 * @param properties */ @Override public void setProperties(Properties properties) { } }
上面已经会根据语句自动进行选择数据库的 工做apache
而后在mybatis的配置文件中将拦截器配置进去安全
<?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> <!--mybatis的配置文件--> <plugins> <!--配置自定义的拦截器,这是mybatis自带的 --> <plugin interceptor="com.statistics.util.DynamicDataSourceInterceptor" /> </plugins> </configuration>
最后将spring配置多数据源那里注册bean(session
DynamicDataSource
)类mybatis
将全部的数据源管理起来,注意一点,咱们本身定义的数据源的名字必定要和spring配置这里的数据源的对应,不然找不到数据源就尴尬了。这一部分就省略了,不懂的百度app