mysql+spring+mybatis實現資料庫讀寫分離[程式碼配置]
阿新 • • 發佈:2018-10-31
場景:一個讀資料來源一個讀寫資料來源。
原理:藉助spring的【org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource】這個抽象類實現,看名字可以瞭解到是一個路由資料來源的東西,這個類中有一個方法
/** * 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會呼叫這個方法去找對應的資料來源。返回值即對應的資料來源的LookUpKey.那麼這個LookUpKey在哪定義的呢?看下面的dataBase.xml的配置
<!--資料來源 讀寫 --> <bean id="dataSourceRW" class="org.logicalcobwebs.proxool.ProxoolDataSource"> <property name="alias" value="ihotelRW"></property> <property name="delegateProperties"> <value>user=${jdbc.username},password=${jdbc.password} </value> </property> <property name="user" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <property name="driver" value="${jdbc.driverClassName}" /> <property name="driverUrl" value="${jdbc.url}" /> <property name="maximumConnectionCount" value="${jdbc.maximumConnectionCount}"></property> <property name="maximumActiveTime" value="${jdbc.maximumActiveTime}"></property> <property name="maximumConnectionLifetime" value="${jdbc.maximumConnectionLifetime}"></property> <property name="prototypeCount" value="${jdbc.prototypeCount}"></property> <property name="houseKeepingSleepTime" value="${jdbc.houseKeepingSleepTime}"></property> <property name="simultaneousBuildThrottle" value="${jdbc.simultaneousBuildThrottle}"></property> <property name="houseKeepingTestSql" value="${jdbc.houseKeepingTestSql}"></property> <property name="verbose" value="${jdbc.verbose}"></property> <property name="statistics" value="${jdbc.statistics}"></property> <property name="statisticsLogLevel" value="${jdbc.statisticsLogLevel}"></property> </bean> <!--資料來源 讀--> <bean id="dataSourceR" class="org.logicalcobwebs.proxool.ProxoolDataSource"> <property name="alias" value="ihotelR"></property> <property name="delegateProperties"> <value>user=${jdbc.r.username},password=${jdbc.r.password} </value> </property> <property name="user" value="${jdbc.r.username}" /> <property name="password" value="${jdbc.r.password}" /> <property name="driver" value="${jdbc.r.driverClassName}" /> <property name="driverUrl" value="${jdbc.r.url}" /> <property name="maximumConnectionCount" value="${jdbc.maximumConnectionCount}"></property> <property name="maximumActiveTime" value="${jdbc.maximumActiveTime}"></property> <property name="maximumConnectionLifetime" value="${jdbc.maximumConnectionLifetime}"></property> <property name="prototypeCount" value="${jdbc.prototypeCount}"></property> <property name="houseKeepingSleepTime" value="${jdbc.houseKeepingSleepTime}"></property> <property name="simultaneousBuildThrottle" value="${jdbc.simultaneousBuildThrottle}"></property> <property name="houseKeepingTestSql" value="${jdbc.houseKeepingTestSql}"></property> <property name="verbose" value="${jdbc.verbose}"></property> <property name="statistics" value="${jdbc.statistics}"></property> <property name="statisticsLogLevel" value="${jdbc.statisticsLogLevel}"></property> </bean> <!-- 動態資料來源 --> <bean id="dynamicDataSource" class="com.dao.datasource.DynamicDataSource"> <!-- 通過key-value關聯資料來源 --> <property name="targetDataSources"> <map> <entry value-ref="dataSourceRW" key="dataSourceKeyRW"></entry> <entry value-ref="dataSourceR" key="dataSourceKeyR"></entry> </map> </property> <property name="defaultTargetDataSource" ref="dataSourceRW" /> </bean> <!--mybatis與Spring整合 開始 --> <bean id="sqlSessionFactory" name="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="configLocation" value="classpath:conf/core/sqlMapConfig.xml" /> <property name="dataSource" ref="dynamicDataSource" /> </bean>
動態資料來源dynamicDataSource中的dataSourceKeyRW、dataSourceKeyR就是
protected abstract Object determineCurrentLookupKey();
這個方法要返回的值。那麼如何設定,讓這個方法的返回值是根據我們的需要返回dataSourceKeyRW、dataSourceKeyR呢?由於這個方法沒有入參,並且是spring自動呼叫的,因此考慮使用靜態變數儲存dataSource的key,在呼叫sql語句前設定靜態變數的值,然後在這個方法中得到靜態變數的值,返回。又考慮到多執行緒,同時可能會有很多請求,為避免執行緒之間相互干擾,考慮使用threadLocal。
先看儲存dataSourceKey的容器類。
public class DBContextHolder {
/**
* 執行緒threadlocal
*/
private static ThreadLocal<String> contextHolder = new ThreadLocal<>();
private String DB_TYPE_RW = "dataSourceKeyRW";
private String DB_TYPE_R = "dataSourceKeyR";
public String getDbType() {
String db = contextHolder.get();
if (db == null) {
db = DB_TYPE_RW;// 預設是讀寫庫
}
return db;
}
/**
*
* 設定本執行緒的dbtype
*
* @param str
* @see [相關類/方法](可選)
* @since [產品/模組版本](可選)
*/
public void setDbType(String str) {
contextHolder.set(str);
}
/**
* clearDBType
*
* @Title: clearDBType
* @Description: 清理連線型別
*/
public static void clearDBType() {
contextHolder.remove();
}
}
動態資料來源的實現類。
public class DynamicDataSource extends AbstractRoutingDataSource {
/*
* (non-Javadoc)
* @see javax.sql.CommonDataSource#getParentLogger()
*/
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// TODO Auto-generated method stub
return null;
}
/**
*
* override determineCurrentLookupKey
* <p>
* Title: determineCurrentLookupKey
* </p>
* <p>
* Description: 自動查詢datasource
* </p>
*
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
return DBContextHolder.getDbType();
}
}
在DAO層中設定資料庫型別。
/**
* 新增郵件
*
* @param sms
* @return
*/
public boolean insertEmail(Email email) {
//根據具體需要設定不同的資料庫
DBContextHolder.setDbType(DBContextHolder.DB_TYPE_RW);
//DBContextHolder.setDbType(DBContextHolder.DB_TYPE_R);
int result = this.getSqlSession().insert(STATEMENT + ".addEntity",
email);
return result == 1;
}
在本例中,我們是在DAO中指定資料庫,我們也可以根據需要在service或者controller中指定DB型別,需要記住的是setDbType是針對執行緒維度的。要考慮多執行緒的問題。