首先,設置一個攔截器,攔截數據庫操作,判斷類型,動態選擇數據源
DynamicDataSourceinterceptor.java
package com.mlr.dao.split;
import java.util.Locale;
import java.util.Properties;
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.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
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;
@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);
private static final String REGEX = ".*insert\\u0020.*|.*delete\\0020.*|.*update\\u0020.*";
public java.lang.Object intercept(Invocation invocation) throws Throwable {
boolean synchronizationActive = TransactionSynchronizationManager.isSynchronizationActive();
//對于不是事務管理的操作
Object[] objects = invocation.getArgs();
String lookupKey = DynamicDataSourceHolder.DB_MASTER;
//獲取操作類型
MappedStatement mappedStatement = (MappedStatement) objects[0];
if (synchronizationActive != true) {
if (mappedStatement.getSqlCommandType().equals(SqlCommandType.SELECT)) {
if (mappedStatement.getId().contains(SelectKeyGenerator.SELECT_KEY_SUFFIX)) {
lookupKey = DynamicDataSourceHolder.DB_MASTER;
} else {
BoundSql boundSql = mappedStatement.getSqlSource().getBoundSql(objects[1]);
String sql = boundSql.getSql().toLowerCase(Locale.CHINA).replaceAll("[\\t\\n\\r]", "");
//符合正則用主庫,不符合用從庫
if (sql.matches(REGEX)) {
lookupKey = DynamicDataSourceHolder.DB_MASTER;
} else {
lookupKey = DynamicDataSourceHolder.DB_SLAVE;
}
}
}
}
//對于事務管理都用主庫
else{
lookupKey = DynamicDataSourceHolder.DB_MASTER;
}
logger.debug("設置方法[{}],use[{}]Strategy,SqlCommandType[{}]...",mappedStatement.getId(),lookupKey,mappedStatement.getSqlCommandType().name());
DynamicDataSourceHolder.setDBType(lookupKey);
return invocation.proceed();
}
/**
* Executor支持增刪改查操作,當操作時候攔截下來判斷用哪個數據源
*/
public java.lang.Object plugin(java.lang.Object target) {
if (target instanceof Executor) {
return Plugin.wrap(target, this);
} else {
return target;
}
}
public void setProperties(Properties properties) {
}
}
DynamicDataSourceHolder.java
package com.mlr.dao.split;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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();
if (db == null) {
db = DB_MASTER;
}
return db;
}
/**
* 設置連接類型
* @param str
*/
public static void setDBType(String str) {
logger.debug("所使用的數據源為" + str);
contextHolder.set(str);
}
/**
* 清理連接類型
* @param str
*/
public static void clearDBType(String str) {
contextHolder.remove();
}
}
DynamicDataSource .java
package com.mlr.dao.split;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource{
protected Object determineCurrentLookupKey() {
return DynamicDataSourceHolder.getDBType();
}
}
然后配置mybatis-config.xml中添加攔截器
<plugins>
<plugin interceptor="com.mlr.dao.split.DynamicDataSourceinterceptor"></plugin>
</plugins>
在spring-dao.xml中配置數據源
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置整合mybatis過程 -->
<!-- 1.配置數據庫相關參數properties的屬性:${url} -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 2.數據庫連接池 -->
<bean id="abstractDataSource" abstract="true" destroy-method="close"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 配置連接池屬性 -->
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- c3p0連接池的私有屬性 -->
<property name="maxPoolSize" value="30"/>
<property name="minPoolSize" value="10"/>
<!-- 關閉連接后不自動commit -->
<property name="autoCommitOnClose" value="false"/>
<!-- 獲取連接超時時間 -->
<property name="checkoutTimeout" value="10000"/>
<!-- 當獲取連接失敗重試次數 -->
<property name="acquireRetryAttempts" value="2"/>
</bean>
<!--主庫數據源-->
<bean id="master" parent="abstractDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.master.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--從庫數據源-->
<bean id="slave" parent="abstractDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.slave.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置動態數據源,這里targetDataSource就是路由數據源所對應名稱-->
<bean id="dynamicDataSource" class="com.mlr.dao.split.DynamicDataSource">
<property name="targetDataSources">
<map>
<entry value-ref="master" key="master"></entry>
<entry key="master" value-ref="master"></entry>
</map>
</property>
</bean>
<!--懶加載,程序運行后判斷sql-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
<property name="targetDataSource">
<ref bean="dynamicDataSource"></ref>
</property>
</bean>
<!-- 3.配置SqlSessionFactory對象 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入數據庫連接池 -->
<property name="dataSource" ref="dataSource"/>
<!-- 配置MyBaties全局配置文件:mybatis-config.xml -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- 掃描entity包 使用別名 -->
<property name="typeAliasesPackage" value="com.mlr.entity"/>
<!-- 掃描sql配置文件:mapper需要的xml文件 -->
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<!-- 4.配置掃描Dao接口包,動態實現Dao接口,注入到spring容器中 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 注入sqlSessionFactory -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!-- 給出需要掃描Dao接口包 -->
<property name="basePackage" value="com.mlr.dao"/>
</bean>
</beans>