仍是動態數據源的那一套,先要繼承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