Mybatis配置多數據源

        在大多數情況下,一個功能全面的系統只使用單一數據源幾乎是不太可能的,所以配置多數據源是十分必要的,記錄一下去年某個項目配置多數據源的方式~

單一數據源的配置如下:

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實現多數據源的記錄~

好好學習,天天向上~^ ^

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章