ssm讀寫分離多資料來源

夢與現實-保潔發表於2018-09-26

實現讀寫分離的方法
第一種:使用讀寫分離中間外掛,實現讀寫分離,一般大公司會自己開發自己的中介軟體,中小型公司更多的是使用程式實現讀寫分離,中介軟體就不多介紹了。
第二種:就是在程式的讀寫分離,實現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的第一篇文章,水平比較低,大家見諒!

相關文章