新聞中心
這篇文章主要介紹Spring Boot + Mybatis如何實現(xiàn)動態(tài)數(shù)據(jù)源,文中介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們一定要看完!
成都創(chuàng)新互聯(lián)專注于馬尾網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供馬尾營銷型網(wǎng)站建設(shè),馬尾網(wǎng)站制作、馬尾網(wǎng)頁設(shè)計、馬尾網(wǎng)站官網(wǎng)定制、小程序制作服務(wù),打造馬尾網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供馬尾網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
動態(tài)數(shù)據(jù)源
在很多具體應(yīng)用場景的時候,我們需要用到動態(tài)數(shù)據(jù)源的情況,比如多租戶的場景,系統(tǒng)登錄時需要根據(jù)用戶信息切換到用戶對應(yīng)的數(shù)據(jù)庫。又比如業(yè)務(wù)A要訪問A數(shù)據(jù)庫,業(yè)務(wù)B要訪問B數(shù)據(jù)庫等,都可以使用動態(tài)數(shù)據(jù)源方案進(jìn)行解決。接下來,我們就來講解如何實現(xiàn)動態(tài)數(shù)據(jù)源,以及在過程中剖析動態(tài)數(shù)據(jù)源背后的實現(xiàn)原理。
實現(xiàn)案例
本教程案例基于 Spring Boot + Mybatis + MySQL 實現(xiàn)。
數(shù)據(jù)庫設(shè)計
首先需要安裝好MySQL數(shù)據(jù)庫,新建數(shù)據(jù)庫 example,創(chuàng)建example表,用來測試數(shù)據(jù)源,SQL腳本如下:
CREATE TABLE `example` ( `pk` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵', `message` varchar(100) NOT NULL, `create_time` datetime NOT NULL COMMENT '創(chuàng)建時間', `modify_time` datetime DEFAULT NULL COMMENT '生效時間', PRIMARY KEY (`pk`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='測試用例表'
添加依賴
添加Spring Boot,Spring Aop,Mybatis,MySQL相關(guān)依賴。
pom.xml
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.mybatis.spring.boot mybatis-spring-boot-starter 1.3.1 org.springframework.boot spring-boot-starter-aop mysql mysql-connector-java 5.1.8
自定義配置文件
新建自定義配置文件resource/config/mysql/db.properties,添加數(shù)據(jù)源:
#數(shù)據(jù)庫設(shè)置 spring.datasource.example.jdbc-url=jdbc:mysql://localhost:3306/example?characterEncoding=UTF-8 spring.datasource.example.username=root spring.datasource.example.password=123456 spring.datasource.example.driver-class-name=com.mysql.jdbc.Driver
啟動類
啟動類添加 exclude = {DataSourceAutoConfiguration.class}, 以禁用數(shù)據(jù)源默認(rèn)自動配置。
數(shù)據(jù)源默認(rèn)自動配置會讀取 spring.datasource.* 的屬性創(chuàng)建數(shù)據(jù)源,所以要禁用以進(jìn)行定制。
DynamicDatasourceApplication.java:
package com.main.example.dynamic.datasource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) public class DynamicDatasourceApplication { public static void main(String[] args) { SpringApplication.run(DynamicDatasourceApplication.class, args); } }
數(shù)據(jù)源配置類
創(chuàng)建一個數(shù)據(jù)源配置類,主要做以下幾件事情:
1. 配置 dao,model(bean),xml mapper文件的掃描路徑。
2. 注入數(shù)據(jù)源配置屬性,創(chuàng)建數(shù)據(jù)源。
3. 創(chuàng)建一個動態(tài)數(shù)據(jù)源,裝入數(shù)據(jù)源。
4. 將動態(tài)數(shù)據(jù)源設(shè)置到SQL會話工廠和事務(wù)管理器。
如此,當(dāng)進(jìn)行數(shù)據(jù)庫操作時,就會通過我們創(chuàng)建的動態(tài)數(shù)據(jù)源去獲取要操作的數(shù)據(jù)源了。
DbSourceConfig.java:
package com.main.example.config.dao; import com.main.example.common.DataEnum; import com.main.example.common.DynamicDataSource; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; //數(shù)據(jù)庫配置統(tǒng)一在config/mysql/db.properties中 @Configuration @PropertySource(value = "classpath:config/mysql/db.properties") public class DbSourceConfig { private String typeAliasesPackage = "com.main.example.bean.**.*"; @Bean(name = "exampleDataSource") @ConfigurationProperties(prefix = "spring.datasource.example") public DataSource exampleDataSource() { return DataSourceBuilder.create().build(); } /* * 動態(tài)數(shù)據(jù)源 * dbMap中存放數(shù)據(jù)源名稱與數(shù)據(jù)源實例,數(shù)據(jù)源名稱存于DataEnum.DbSource中 * setDefaultTargetDataSource方法設(shè)置默認(rèn)數(shù)據(jù)源 */ @Bean(name = "dynamicDataSource") public DataSource dynamicDataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); //配置多數(shù)據(jù)源 Map
動態(tài)數(shù)據(jù)源類
我們上一步把這個動態(tài)數(shù)據(jù)源設(shè)置到了SQL會話工廠和事務(wù)管理器,這樣在操作數(shù)據(jù)庫時就會通過動態(tài)數(shù)據(jù)源類來獲取要操作的數(shù)據(jù)源了。
動態(tài)數(shù)據(jù)源類集成了Spring提供的AbstractRoutingDataSource類,AbstractRoutingDataSource 中獲取數(shù)據(jù)源的方法就是 determineTargetDataSource,而此方法又通過 determineCurrentLookupKey 方法獲取查詢數(shù)據(jù)源的key。
所以如果我們需要動態(tài)切換數(shù)據(jù)源,就可以通過以下兩種方式定制:
1. 覆寫 determineCurrentLookupKey 方法
通過覆寫 determineCurrentLookupKey 方法,從一個自定義的 DbSourceContext.getDbSource() 獲取數(shù)據(jù)源key值,這樣在我們想動態(tài)切換數(shù)據(jù)源的時候,只要通過 DbSourceContext.setDbSource(key) 的方式就可以動態(tài)改變數(shù)據(jù)源了。這種方式要求在獲取數(shù)據(jù)源之前,要先初始化各個數(shù)據(jù)源到 DbSourceContext 中,我們案例就是采用這種方式實現(xiàn)的,所以要將數(shù)據(jù)源都事先初始化到DynamicDataSource 中。
2. 可以通過覆寫 determineTargetDataSource,因為數(shù)據(jù)源就是在這個方法創(chuàng)建并返回的,所以這種方式就比較自由了,支持到任何你希望的地方讀取數(shù)據(jù)源信息,只要最終返回一個 DataSource 的實現(xiàn)類即可。比如你可以到數(shù)據(jù)庫、本地文件、網(wǎng)絡(luò)接口等方式讀取到數(shù)據(jù)源信息然后返回相應(yīng)的數(shù)據(jù)源對象就可以了。
DynamicDataSource.java:
package com.main.example.common; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DbSourceContext.getDbSource(); } }
數(shù)據(jù)源上下文
動態(tài)數(shù)據(jù)源的切換主要是通過調(diào)用這個類的方法來完成的。在任何想要進(jìn)行切換數(shù)據(jù)源的時候都可以通過調(diào)用這個類的方法實現(xiàn)切換。比如系統(tǒng)登錄時,根據(jù)用戶信息調(diào)用這個類的數(shù)據(jù)源切換方法切換到用戶對應(yīng)的數(shù)據(jù)庫。完整代碼如下:
DbSourceContext.java:
package com.main.example.common; import org.apache.log4j.Logger; public class DbSourceContext { private static Logger logger = Logger.getLogger(DbSourceContext.class); private static final ThreadLocaldbContext = new ThreadLocal (); public static void setDbSource(String source) { logger.debug("set source ====>" + source); dbContext.set(source); } public static String getDbSource() { logger.debug("get source ====>" + dbContext.get()); return dbContext.get(); } public static void clearDbSource() { dbContext.remove(); } }
注解式數(shù)據(jù)源
到這里,在任何想要動態(tài)切換數(shù)據(jù)源的時候,只要調(diào)用DbSourceContext.setDbSource(key) 就可以完成了。
接下來我們實現(xiàn)通過注解的方式來進(jìn)行數(shù)據(jù)源的切換,原理就是添加注解(如@DbSource(value="example")),然后實現(xiàn)注解切面進(jìn)行數(shù)據(jù)源切換。
創(chuàng)建一個動態(tài)數(shù)據(jù)源注解,擁有一個value值,用于標(biāo)識要切換的數(shù)據(jù)源的key。
DbSource.java:
package com.main.example.config.dao; import java.lang.annotation.*; /** * 動態(tài)數(shù)據(jù)源注解 * @author * @date April 12, 2019 */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DbSource { /** * 數(shù)據(jù)源key值 * @return */ String value(); }
創(chuàng)建一個AOP切面,攔截帶 @DataSource 注解的方法,在方法執(zhí)行前切換至目標(biāo)數(shù)據(jù)源,執(zhí)行完成后恢復(fù)到默認(rèn)數(shù)據(jù)源。
DynamicDataSourceAspect.java:
package com.main.example.config.dao; import com.main.example.common.DbSourceContext; import org.apache.log4j.Logger; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; /** * 動態(tài)數(shù)據(jù)源切換處理器 * @author linzhibao * @date April 12, 2019 */ @Aspect @Order(-1) // 該切面應(yīng)當(dāng)先于 @Transactional 執(zhí)行 @Component public class DynamicDataSourceAspect { private static Logger logger = Logger.getLogger(DynamicDataSourceAspect.class); /** * 切換數(shù)據(jù)源 * @param point * @param dbSource */ //@Before("@annotation(dbSource)") 注解在對應(yīng)方法,攔截有@DbSource的方法 //注解在類對象,攔截有@DbSource類下所有的方法 @Before("@within(dbSource)") public void switchDataSource(JoinPoint point, DbSource dbSource) { // 切換數(shù)據(jù)源 DbSourceContext.setDbSource(dbSource.value()); } /** * 重置數(shù)據(jù)源 * @param point * @param dbSource */ //注解在類對象,攔截有@DbSource類下所有的方法 @After("@within(dbSource)") public void restoreDataSource(JoinPoint point, DbSource dbSource) { // 將數(shù)據(jù)源置為默認(rèn)數(shù)據(jù)源 DbSourceContext.clearDbSource(); } }
到這里,動態(tài)數(shù)據(jù)源相關(guān)的處理代碼就完成了。
編寫用戶業(yè)務(wù)代碼
接下來編寫用戶查詢業(yè)務(wù)代碼,用來進(jìn)行測試,Dao層只需添加一個查詢接口即可。
ExampleDao.java:
package com.main.example.dao; import com.main.example.common.DataEnum; import com.main.example.config.dao.DbSource; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.List; @Component("exampleDao") //切換數(shù)據(jù)源注解,以DataEnum.DbSource中的值為準(zhǔn) @DbSource("example") public class ExampleDao extends DaoBase { private static final String MAPPER_NAME_SPACE = "com.main.example.dao.ExampleMapper"; public ListselectAllMessages() { return selectList(MAPPER_NAME_SPACE, "selectAllMessages"); } }
Controler代碼:
TestExampleDao.java:
package com.main.example.dao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; @RestController public class TestExampleDao { @Autowired ExampleDao exampleDao; @RequestMapping(value = "/test/example") public ListselectAllMessages() { try { List ldata = exampleDao.selectAllMessages(); if(ldata == null){System.out.println("*********it is null.***********");return null;} for(String d : ldata) { System.out.println(d); } return ldata; }catch(Exception e) { e.printStackTrace(); } return new ArrayList<>(); } }
ExampleMapper.xml代碼:
測試效果
啟動系統(tǒng),訪問 http://localhost:80/test/example">http://localhost:80/test/example,分別測試兩個接口,成功返回數(shù)據(jù)。
可能遇到的問題
1.報錯:java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName
原因:
spring boot從1.X升級到2.X版本之后,一些配置及用法有了變化,如果不小心就會碰到“jdbcUrl is required with driverClassName.”的錯誤
解決方法:
在1.0 配置數(shù)據(jù)源的過程中主要是寫成:spring.datasource.url 和spring.datasource.driverClassName。
而在2.0升級之后需要變更成:spring.datasource.jdbc-url和spring.datasource.driver-class-name即可解決!
2.自定義配置文件
自定義配置文件需要在指定配置類上加上@PropertySource標(biāo)簽,例如:
@PropertySource(value = "classpath:config/mysql/db.properties")
若是作用于配置類中的方法,則在方法上加上@ConfigurationProperties,例如:
@ConfigurationProperties(prefix = "spring.datasource.example")
配置項前綴為spring.datasource.example
若是作用于配置類上,則在類上加上@ConfigurationProperties(同上),并且在啟動類上加上@EnableConfigurationProperties(XXX.class)
3.多數(shù)據(jù)源
需要在啟動類上取消自動裝載數(shù)據(jù)源,如:
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
springboot是什么
springboot一種全新的編程規(guī)范,其設(shè)計目的是用來簡化新Spring應(yīng)用的初始搭建以及開發(fā)過程,SpringBoot也是一個服務(wù)于框架的框架,服務(wù)范圍是簡化配置文件。
以上是“Spring Boot + Mybatis如何實現(xiàn)動態(tài)數(shù)據(jù)源”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!
網(wǎng)頁名稱:SpringBoot+Mybatis如何實現(xiàn)動態(tài)數(shù)據(jù)源
轉(zhuǎn)載源于:http://www.dlmjj.cn/article/gojeii.html