在大多數情況下,一個功能全面的系統只使用單一數據源幾乎是不太可能的,所以配置多數據源是十分必要的,記錄一下去年某個項目配置多數據源的方式~
單一數據源的配置如下:
1. 配置datasource
2. 配置sessionFactory,屬性爲上一步配置的datasource和mapperLocation(xml文件所在的目錄)
多數據源配置:
要實現多數據源切換,重點在於擴展AbstractRoutingDataSource抽象類,這個類是Spring2.0之後增加的。
這個類相當於數據源DataSourcer的路由中介,可以實現在項目運行時根據設置的key值切換到對應的數據源DataSource上。
實現過程如下:
1. 編寫枚舉類,表示數據源的key
public enum DataSources {
SYS, API, APP, APWH, BICD
}
2. 編寫線程安全的數據源切換類DataSourceTypeManager
public class DataSourceTypeManager {
// ThreadLocal類是實現線程安全的關鍵,因爲數據操作大部分都是併發執行,所以必須要考慮線程安全
private static final ThreadLocal<DataSources> dataSourceTypes = new ThreadLocal<DataSources>() {
@Override
protected DataSources initialValue() {
return DataSources.SYS;
}
};
public static DataSources get() {
return dataSourceTypes.get();
}
public static void set(DataSources dataSourceType) {
dataSourceTypes.set(dataSourceType);
}
public static void reset() {
dataSourceTypes.set(DataSources.SYS);
}
}
3. 擴展類AbstractRoutingDataSource
public class ThreadLocalRountingDataSource extends AbstractRoutingDataSource {
// 關鍵,重寫獲取數據源key的方法
@Override
protected Object determineCurrentLookupKey() {
return DataSourceTypeManager.get();
}
}
4. mybatis配置文件
以上,多數據源配置完成,下面講講在實際進行數據操作是如何使用。
- 第一種方法屬於比較笨的方法,就是在操作數據之前,手動設置DataSourceTypeManager.set(),執行完數據庫操作之後重置;
public void insertData () {
// 設置數據源,枚舉值
DataSourceTypeManager.set(DataSources.SYS);
//....數據庫操作
// 清除設置的值
DataSourceTypeManager.reset();
}
- 第二種是之前項目使用的,通過擴展SqlSessionDaoSupport實現。
(1)新建SqlSessionDaoSupport的擴展類BaseDaoSupport,封裝一些CRUD方法。
public abstract class BaseDaoSupport extends SqlSessionDaoSupport {
private static boolean isShowSql;
public Connection getConnection() {
return getSqlSession().getConnection();
}
public Configuration getConfiguration() {
return getSqlSession().getConfiguration();
}
@Resource
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
super.setSqlSessionFactory(sqlSessionFactory);
}
public boolean isShowSql() {
if (isShowSql) {
}
return isShowSql;
}
public void showSql(String statement, Object parameter) {
if (isShowSql()) {
getConfiguration().getMappedStatement(getClass().getName() + "." + statement).getBoundSql(parameter)
.getSql();
}
}
/*********************** 封裝方法分割線 ***********************/
protected int _getInt(String statement, Object parameter) {
this.showSql(statement, parameter);
return getSqlSession().selectOne(getClass().getName() + "." + statement, parameter);
}
protected String _getString(String statement, Object parameter) {
this.showSql(statement, parameter);
return getSqlSession().selectOne(getClass().getName() + "." + statement, parameter);
}
protected double _getDouble(String statement, Object parameter) {
this.showSql(statement, parameter);
return getSqlSession().selectOne(getClass().getName() + "." + statement, parameter);
}
@SuppressWarnings("unchecked")
protected <T> T _selectOne(String statement, Object parameter) {
this.showSql(statement, parameter);
return (T) getSqlSession().selectOne(getClass().getName() + "." + statement, parameter);
}
protected <T> List<T> _selectList(String statement, Object parameter) {
this.showSql(statement, parameter);
return getSqlSession().selectList(getClass().getName() + "." + statement, parameter);
}
// protected <T> Pagination<T> _selectForPage(String statement, Object parameter) {
// this.showSql(statement, parameter);
// return null;
// }
protected int _insert(String statement, Object parameter) {
this.showSql(statement, parameter);
return getSqlSession().insert(getClass().getName() + "." + statement, parameter);
}
protected int _update(String statement, Object parameter) {
this.showSql(statement, parameter);
return getSqlSession().update(getClass().getName() + "." + statement, parameter);
}
protected int _delete(String statement, Object parameter) {
this.showSql(statement, parameter);
return getSqlSession().delete(getClass().getName() + "." + statement, parameter);
}
}
(2)每個數據源新建擴展類,重寫getSqlSession方法,設置數據源。
public class ApiDaoSupport extends BaseDaoSupport{
public SqlSession getSqlSession() {
// 設置相應的數據源
DataSourceTypeManager.set(DataSources.API);
return super.getSqlSession();
}
}
(3)數據庫操作類,對應Mapper文件的方法。
@Repository("bicdDao")
public class BicdDao extends ApiDaoSupport{
public List<Map<String, Object>> queryYieldPgAttention(String reportName, String site) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("site", site);
params.put("reportName", reportName);
return this._selectList("queryYieldPgAttention", params);
}
}
個人認爲這種方法還挺方便的,同一個數據庫的查詢寫在同一個Mapper文件中即可~代碼侵入性不高,但是在數據源很多的時候需要編寫很多的擴展類,也是很費體力的……
- 第三種:使用Spring AOP實現註解配置數據源
說實話,在開始學習Spring的時候並沒有理解AOP的意義,當結合實際業務使用的時候就發現了它的巧妙了,一個被公認爲偉大的東西一定是有它讓人信服的理由,Spring就是這樣一個存在,向這些框架作者致敬~
回到正題~
以下關於自定義註解的實現摘抄整理自:https://blog.csdn.net/twomr/article/details/79137056
(1)編寫自定義註解類
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetDataSource {
DataSourceKey dataSourceKey() default DataSourceKey.DB_MASTER;
}
(2)編寫數據源切換切面類:DynamicDataSourceAspect
@Aspect
@Order(-1)
@Component
public class DynamicDataSourceAspect {
private static final Logger LOG = Logger.getLogger(DynamicDataSourceAspect.class);
@Pointcut("execution(* com.apedad.example.service.*.list*(..))")
public void pointCut() {
}
/**
* 執行方法前更換數據源
*
* @param joinPoint 切點
* @param targetDataSource 動態數據源
*/
@Before("@annotation(targetDataSource)")
public void doBefore(JoinPoint joinPoint, TargetDataSource targetDataSource) {
DataSourceKey dataSourceKey = targetDataSource.dataSourceKey();
if (dataSourceKey == DataSourceKey.DB_OTHER) {
LOG.info(String.format("設置數據源爲 %s", DataSourceKey.DB_OTHER));
DynamicDataSourceContextHolder.set(DataSourceKey.DB_OTHER);
} else {
LOG.info(String.format("使用默認數據源 %s", DataSourceKey.DB_MASTER));
DynamicDataSourceContextHolder.set(DataSourceKey.DB_MASTER);
}
}
/**
* 執行方法後清除數據源設置
*
* @param joinPoint 切點
* @param targetDataSource 動態數據源
*/
@After("@annotation(targetDataSource)")
public void doAfter(JoinPoint joinPoint, TargetDataSource targetDataSource) {
LOG.info(String.format("當前數據源 %s 執行清理方法", targetDataSource.dataSourceKey()));
DynamicDataSourceContextHolder.clear();
}
@Before(value = "pointCut()")
public void doBeforeWithSlave(JoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
//獲取當前切點方法對象
Method method = methodSignature.getMethod();
if (method.getDeclaringClass().isInterface()) {//判斷是否爲藉口方法
try {
//獲取實際類型的方法對象
method = joinPoint.getTarget().getClass()
.getDeclaredMethod(joinPoint.getSignature().getName(), method.getParameterTypes());
} catch (NoSuchMethodException e) {
LOG.error("方法不存在!", e);
}
}
if (null == method.getAnnotation(TargetDataSource.class)) {
DynamicDataSourceContextHolder.setSlave();
}
}
}
(3)在需要切換數據源的service方法上加上註解就OK了~
//使用此註解來切換到想切換的數據源
@TargetDataSource(dataSourceKey = DataSourceKey.DB_OTHER)
@Override
public int insert(UserInfo userInfo) {
return userInfoMapper.insert(userInfo);
}
以上就是關於Mybatis實現多數據源的記錄~
好好學習,天天向上~^ ^