springboot 多数据源mybatis的两种整合方法
轉載自??springboot-mybatis多數據源的兩種整合方法
簡介:
隨著并發量的不斷增加,顯然單個數據庫已經承受不了高并發帶來的壓力。一個項目使用多個數據庫(無論是主從復制- - 讀寫分離還是分布式數據庫結構)的重要性變得越來越明顯。傳統項目中(個人對傳統項目的理解就是所有的業務模塊都在一個tomcat中完成,多個相同的tomcat集群也可認為是傳統項目)整合多數據源有兩種方法:分包和AOP。
版本:
springboot:1.5.9.RELEASE?
mariadb:5.7
一、分包方式實現
1、在application.properties中配置兩個數據庫:
## test1 database spring.datasource.test1.url=jdbc:mysql://localhost:3307/multipledatasource1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false spring.datasource.test1.username=root spring.datasource.test1.password=root spring.datasource.test1.driver-class-name=com.mysql.cj.jdbc.Driver ## test2 database spring.datasource.test2.url=jdbc:mysql://localhost:3307/multipledatasource2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false spring.datasource.test2.username=root spring.datasource.test2.password=root spring.datasource.test2.driver-class-name=com.mysql.cj.jdbc.Driver
2、建立連個數據源的配置文件
springbooot中的參數可以參考上一篇博客(不定期更新中):https://blog.csdn.net/tuesdayma/article/details/81029539
第一個配置文件:
//表示這個類為一個配置類 @Configuration // 配置mybatis的接口類放的地方 @MapperScan(basePackages = "com.mzd.multipledatasources.mapper.test01", sqlSessionFactoryRef = "test1SqlSessionFactory") public class DataSourceConfig1 {// 將這個對象放入Spring容器中@Bean(name = "test1DataSource")// 表示這個數據源是默認數據源@Primary// 讀取application.properties中的配置參數映射成為一個對象// prefix表示參數的前綴@ConfigurationProperties(prefix = "spring.datasource.test1")public DataSource getDateSource1() {return DataSourceBuilder.create().build();}@Bean(name = "test1SqlSessionFactory")// 表示這個數據源是默認數據源@Primary// @Qualifier表示查找Spring容器中名字為test1DataSource的對象public SqlSessionFactory test1SqlSessionFactory(@Qualifier("test1DataSource") DataSource datasource)throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(datasource);bean.setMapperLocations(// 設置mybatis的xml所在位置new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/test01/*.xml"));return bean.getObject();}@Bean("test1SqlSessionTemplate")// 表示這個數據源是默認數據源@Primarypublic SqlSessionTemplate test1sqlsessiontemplate(@Qualifier("test1SqlSessionFactory") SqlSessionFactory sessionfactory) {return new SqlSessionTemplate(sessionfactory);} }第二個配置文件:
@Configuration @MapperScan(basePackages = "com.mzd.multipledatasources.mapper.test02", sqlSessionFactoryRef = "test2SqlSessionFactory") public class DataSourceConfig2 {@Bean(name = "test2DataSource")@ConfigurationProperties(prefix = "spring.datasource.test2")public DataSource getDateSource2() {return DataSourceBuilder.create().build();}@Bean(name = "test2SqlSessionFactory")public SqlSessionFactory test2SqlSessionFactory(@Qualifier("test2DataSource") DataSource datasource)throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(datasource);bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/test02/*.xml"));return bean.getObject();}@Bean("test2SqlSessionTemplate")public SqlSessionTemplate test2sqlsessiontemplate(@Qualifier("test2SqlSessionFactory") SqlSessionFactory sessionfactory) {return new SqlSessionTemplate(sessionfactory);} }注意:
1、@Primary這個注解必須要加,因為不加的話spring將分不清楚那個為主數據源(默認數據源)
2、mapper的接口、xml形式以及dao層都需要兩個分開,目錄如圖:?
?
3、bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(“XXXX”));mapper的xml形式文件位置必須要配置,不然將報錯:no statement (這種錯誤也可能是mapper的xml中,namespace與項目的路徑不一致導致的,具體看情況吧,注意一下就行,問題不大的)
4、在service層中根據不同的業務注入不同的dao層。
5、如果是主從復制- -讀寫分離:比如test01中負責增刪改,test02中負責查詢。但是需要注意的是負責增刪改的數據庫必須是主庫(master)
6、如果是分布式結構的話,不同模塊操作各自的數據庫就好,test01包下全是test01業務,test02全是test02業務,但是如果test01中摻雜著test02的編輯操作,這時候將會產生事務問題:即test01中的事務是沒法控制test02的事務的,這個問題在之后的博客中會解決。
?
二、AOP實現
簡介: 用這種方式實現多數據源的前提必須要清楚兩個知識點:AOP原理和AbstractRoutingDataSource抽象類。
1、AOP:這個東西。。。不切當的說就是相當于攔截器,只要滿足要求的都會被攔截過來,然后進行一些列的操作。具體需要自己去體會。。。
2、AbstractRoutingDataSource:這個類是實現多數據源的關鍵,他的作用就是動態切換數據源,實質:有多少個數據源就存多少個數據源在targetDataSources(是AbstractRoutingDataSource的一個map類型的屬性,其中value為每個數據源,key表示每個數據源的名字)這個屬性中,然后根據determineCurrentLookupKey()這個方法獲取當前數據源在map中的key值,然后determineTargetDataSource()方法中動態獲取當前數據源,如果當前數據源不存并且默認數據源也不存在就拋出異常。
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {//多數據源map集合private Map<Object, Object> targetDataSources;//默認數據源private Object defaultTargetDataSource;//其實就是targetDataSources,后面的afterPropertiesSet()方法會將targetDataSources賦值給resolvedDataSourcesprivate Map<Object, DataSource> resolvedDataSources;private DataSource resolvedDefaultDataSource;public void setTargetDataSources(Map<Object, Object> targetDataSources) {this.targetDataSources = targetDataSources;}protected DataSource determineTargetDataSource() {Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");Object lookupKey = this.determineCurrentLookupKey();DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);if (dataSource == null && (this.lenientFallback || lookupKey == null)) {dataSource = this.resolvedDefaultDataSource;}if (dataSource == null) {throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");} else {return dataSource;}}protected abstract Object determineCurrentLookupKey(); }
具體實現:
1、定義一個動態數據源:繼承AbstractRoutingDataSource 抽象類,并重寫determineCurrentLookupKey()方法
public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {DataSourceType.DataBaseType dataBaseType = DataSourceType.getDataBaseType();return dataBaseType;}}2、創建一個切換數據源類型的類: ThreadLocal這個知識點可以參考我的博客:https://blog.csdn.net/tuesdayma/article/details/74841657 就是為了線程的安全性,每個線程之間不會相互影響。
public class DataSourceType {public enum DataBaseType {TEST01, TEST02}// 使用ThreadLocal保證線程安全private static final ThreadLocal<DataBaseType> TYPE = new ThreadLocal<DataBaseType>();// 往當前線程里設置數據源類型public static void setDataBaseType(DataBaseType dataBaseType) {if (dataBaseType == null) {throw new NullPointerException();}System.err.println("[將當前數據源改為]:" + dataBaseType);TYPE.set(dataBaseType);}// 獲取數據源類型public static DataBaseType getDataBaseType() {DataBaseType dataBaseType = TYPE.get() == null ? DataBaseType.TEST01 : TYPE.get();System.err.println("[獲取當前數據源的類型為]:" + dataBaseType);return dataBaseType;}// 清空數據類型public static void clearDataBaseType() {TYPE.remove();}}
3、定義多個數據源:怎么定義就不多說了,和方法一是一樣的,主要是將定義好的多個數據源放在動態數據源中。
4、定義AOP:就是不同業務切換不同數據庫的入口。如果覺得execution太長不愿意寫,就可以定義一個注解來實現。可參考于我的博客:https://blog.csdn.net/tuesdayma/article/details/79704238
整體目錄如圖:?
源代碼:https://github.com/mzd123/springboot-multipledatasources/tree/master
?
總結
以上是生活随笔為你收集整理的springboot 多数据源mybatis的两种整合方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SpringBoot2.1.9 多数据源
- 下一篇: 解决idea启动项目报错:Unable