Spring?多數(shù)據(jù)源方法級(jí)別注解實(shí)現(xiàn)過程
Spring框架提供了多種數(shù)據(jù)源管理方式,其中多數(shù)據(jù)源管理是其中之一。多數(shù)據(jù)源管理允許應(yīng)用程序使用多個(gè)數(shù)據(jù)源,而不是只使用一個(gè)數(shù)據(jù)源,從而提高了應(yīng)用程序的靈活性和可靠性。
多數(shù)據(jù)源管理的主要目的是讓應(yīng)用程序能夠在不同的數(shù)據(jù)庫(kù)之間切換,以滿足不同的業(yè)務(wù)需求。例如,如果應(yīng)用程序需要訪問一個(gè)高可用性的數(shù)據(jù)庫(kù),但是該數(shù)據(jù)庫(kù)當(dāng)前不可用,那么應(yīng)用程序可以自動(dòng)切換到備用數(shù)據(jù)庫(kù),以保證業(yè)務(wù)的連續(xù)性。
在多數(shù)據(jù)源管理中,Spring框架通過使用JNDI(Java命名和目錄接口)來(lái)管理數(shù)據(jù)源。JNDI允許應(yīng)用程序在運(yùn)行時(shí)動(dòng)態(tài)查找數(shù)據(jù)源,并且可以使用不同的數(shù)據(jù)源來(lái)訪問不同的數(shù)據(jù)庫(kù)。
在多數(shù)據(jù)源管理中,還需要考慮數(shù)據(jù)源的配置和管理。這包括創(chuàng)建數(shù)據(jù)源、配置數(shù)據(jù)源、管理連接池和處理連接泄漏等問題。Spring框架提供了多種數(shù)據(jù)源配置和管理的工具和技術(shù),例如Spring JDBC Template、Spring Data JPA和Spring Boot等。
總之,多數(shù)據(jù)源管理是Spring框架中非常重要的一部分,它可以提高應(yīng)用程序的靈活性和可靠性,從而更好地滿足業(yè)務(wù)需求。
廢話少說(shuō),直接開干!
下面詳細(xì)地介紹一下多數(shù)據(jù)源的實(shí)現(xiàn)過程,同時(shí)實(shí)現(xiàn)一個(gè)方法級(jí)別的注解,便于靈活使用。
1, Pom配置
引入mysql,spring-boot-starter-jdbc
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> <version>2.4.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql-connector.version}</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis-plus.version}</version> </dependency>
2,配置數(shù)據(jù)源
可以在項(xiàng)目里,或者在apollo,nacos等配置中心上
#數(shù)據(jù)源master spring.dynamic.datasource.master.url = jdbc:mysql://127.0.0.1:3306/aaa?characterEncoding=utf-8&rewriteBatchedStatements=true&tinyInt1isBit=false&allowMultiQueries=true spring.dynamic.datasource.master.username = root spring.dynamic.datasource.master.password = xxxxxxxx spring.dynamic.datasource.master.driver-class-name = com.mysql.cj.jdbc.Driver spring.dynamic.datasource.master.type = com.zaxxer.hikari.HikariDataSource #數(shù)據(jù)源slave spring.dynamic.datasource.slave.url = jdbc:mysql://127.0.0.1:3306/bbb?characterEncoding=utf-8&rewriteBatchedStatements=true&tinyInt1isBit=false&allowMultiQueries=true spring.dynamic.datasource.slave.username = root spring.dynamic.datasource.slave.password = yyyyyyyy spring.dynamic.datasource.slave.driver-class-name = com.mysql.cj.jdbc.Driver spring.dynamic.datasource.slave.type = com.zaxxer.hikari.HikariDataSource
3,讀取數(shù)據(jù)源配置
3.1 先看整個(gè)項(xiàng)目結(jié)構(gòu)
3.2 新增讀取數(shù)據(jù)源配置類
import lombok.Data; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import java.util.Map; @Data @ConfigurationProperties(prefix = "spring.dynamic") public class DSProperties { private Map<String, DataSourceProperties> datasource; }
3.3 新增線程和數(shù)據(jù)源mapping的類
import com.xxx.gateway.common.Constant; import lombok.extern.slf4j.Slf4j; @Slf4j public class DynamicDataSourceHolder { /** * 保存動(dòng)態(tài)數(shù)據(jù)源名稱 */ private static final ThreadLocal<String> DYNAMIC_DATASOURCE_KEY = new ThreadLocal<>(); /** * 設(shè)置/切換數(shù)據(jù)源,決定當(dāng)前線程使用哪個(gè)數(shù)據(jù)源 */ public static void setDynamicDataSourceKey(String key){ log.info("數(shù)據(jù)源切換為:{}",key); DYNAMIC_DATASOURCE_KEY.set(key); } /** * 獲取動(dòng)態(tài)數(shù)據(jù)源名稱,默認(rèn)使用mater數(shù)據(jù)源 */ public static String getDynamicDataSourceKey(){ String key = DYNAMIC_DATASOURCE_KEY.get(); return key == null ? Constant.MASTER_DATASOURCE : key; } /** * 移除當(dāng)前數(shù)據(jù)源 */ public static void removeDynamicDataSourceKey(){ log.info("移除數(shù)據(jù)源:{}",DYNAMIC_DATASOURCE_KEY.get()); DYNAMIC_DATASOURCE_KEY.remove(); } }
3.4 繼承路由多數(shù)據(jù)源的抽象類,并實(shí)現(xiàn)determineCurrentLookupKey方法
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { String dataBaseType = DynamicDataSourceHolder.getDynamicDataSourceKey(); return dataBaseType; } }
4,根據(jù)配置重寫數(shù)據(jù)源和事務(wù)管理器
注意: 下面這個(gè)注解里的basePackages是你項(xiàng)目的路徑
@MapperScan(basePackages = {"com.xxx.gateway.mapper"},
sqlSessionFactoryRef = "SqlSessionFactory")
import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import java.util.HashMap; import java.util.Map; @Configuration @EnableConfigurationProperties(DSProperties.class) @MapperScan(basePackages = {"com.xxx.gateway.mapper"}, sqlSessionFactoryRef = "SqlSessionFactory") public class DynamicDataSourceConfig { @Bean(name = "dynamicDataSource") public DynamicDataSource DataSource(DSProperties dsProperties) { Map targetDataSource = new HashMap<>(8); dsProperties.getDatasource().forEach((k, v) -> { targetDataSource.put(k, v.initializeDataSourceBuilder().build()); }); DynamicDataSource dataSource = new DynamicDataSource(); dataSource.setTargetDataSources(targetDataSource); return dataSource; } /** * 創(chuàng)建動(dòng)態(tài)數(shù)據(jù)源的SqlSessionFactory,傳入的是動(dòng)態(tài)數(shù)據(jù)源 * @Primary這個(gè)注解很重要,如果項(xiàng)目中存在多個(gè)SqlSessionFactory,這個(gè)注解一定要加上 */ @Primary @Bean("SqlSessionFactory") public SqlSessionFactory sqlSessionFactoryBean(DynamicDataSource dynamicDataSource) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dynamicDataSource); org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); configuration.setMapUnderscoreToCamelCase(true); configuration.setDefaultFetchSize(100); configuration.setDefaultStatementTimeout(30); sqlSessionFactoryBean.setMapperLocations( new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml")); sqlSessionFactoryBean.setConfiguration(configuration); return sqlSessionFactoryBean.getObject(); } /** * 重寫事務(wù)管理器,管理動(dòng)態(tài)數(shù)據(jù)源 */ @Primary @Bean(value = "transactionManager") public PlatformTransactionManager annotationDrivenTransactionManager(DynamicDataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }
5,注解實(shí)現(xiàn)
5.1 新增注解定義
import java.lang.annotation.*; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DS { String value() default Constant.MASTER_DATASOURCE; }
5.2 新增注解實(shí)現(xiàn)
import com.xxx.gateway.config.DynamicDataSourceHolder; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import java.util.Objects; @Aspect @Order(1) @Component @Slf4j public class DsAspect { @Pointcut("@annotation(com.xgd.gateway.aspect.DS)") public void dsPointCut() { } //環(huán)繞通知 @Around("dsPointCut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable{ String key = getDefineAnnotation(joinPoint).value(); DynamicDataSourceHolder.setDynamicDataSourceKey(key); try { return joinPoint.proceed(); } finally { DynamicDataSourceHolder.removeDynamicDataSourceKey(); } } /** * 先判斷方法的注解,后判斷類的注解,以方法的注解為準(zhǔn) * @param joinPoint * @return */ private DS getDefineAnnotation(ProceedingJoinPoint joinPoint){ MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); DS dataSourceAnnotation = methodSignature.getMethod().getAnnotation(DS.class); if (Objects.nonNull(methodSignature)) { return dataSourceAnnotation; } else { Class<?> dsClass = joinPoint.getTarget().getClass(); return dsClass.getAnnotation(DS.class); } } }
6, 通過注解使用多數(shù)據(jù)源
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.xxx.gateway.aspect.DS; import com.xxx.gateway.domain.User; import org.apache.ibatis.annotations.Param; import java.util.List; public interface UserMapper extends BaseMapper<User> { public User queryByCode(@Param("code") String code); @DS(value="master") public User queryByCodeNoPass(@Param("code") String code); @DS(value="slave1") public List<String> queryBanks(); }
注意:
1,@DS注解里的value值是可以隨意的,只要和配置中心里spring.dynamic.datasource.后面的一致就可以。
2,如果方法上沒有配置數(shù)據(jù)源,那么就是走默認(rèn)的master數(shù)據(jù)源,所以項(xiàng)目里必須至少配置了master數(shù)據(jù)源。
3,各個(gè)數(shù)據(jù)源可以重復(fù),因?yàn)椴粫?huì)校驗(yàn)是否重復(fù)。
到此這篇關(guān)于Spring 多數(shù)據(jù)源方法級(jí)別注解實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Spring 多數(shù)據(jù)源內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
kafka生產(chǎn)者發(fā)送消息流程深入分析講解
本文將介紹kafka的一條消息的發(fā)送流程,從消息的發(fā)送到服務(wù)端的存儲(chǔ)。上文說(shuō)到kafak分為客戶端與服務(wù)端,要發(fā)送消息就涉及到了網(wǎng)絡(luò)通訊,kafka采用TCP協(xié)議進(jìn)行客戶端與服務(wù)端的通訊協(xié)議2023-03-03SpringBoot集成easy-rules規(guī)則引擎流程詳解
這篇文章主要介紹了SpringBoot集成easy-rules規(guī)則引擎流程,合理的使用規(guī)則引擎可以極大的減少代碼復(fù)雜度,提升代碼可維護(hù)性。業(yè)界知名的開源規(guī)則引擎有Drools,功能豐富,但也比較龐大2023-03-03解讀StringBuilder為何比String節(jié)省效率
StringBuilder比String節(jié)省效率的原因主要在于其可變性和性能開銷的降低,StringBuilder在內(nèi)部維護(hù)一個(gè)字符數(shù)組,可以直接在原有基礎(chǔ)上修改,避免了每次拼接時(shí)的額外復(fù)制操作2024-12-12Spring容器刷新obtainFreshBeanFactory示例詳解
這篇文章主要為大家介紹了Spring容器刷新obtainFreshBeanFactory示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03使用JDBC連接Mysql 8.0.11出現(xiàn)了各種錯(cuò)誤的解決
這篇文章主要介紹了使用JDBC連接Mysql 8.0.11出現(xiàn)了各種錯(cuò)誤的解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08SpringBoot3整合EasyExcel動(dòng)態(tài)實(shí)現(xiàn)表頭重命名
這篇文章主要為大家詳細(xì)介紹了SpringBoot3整合EasyExcel如何通過WriteHandler動(dòng)態(tài)實(shí)現(xiàn)表頭重命名,文中的示例代碼講解詳細(xì),有需要的可以了解下2025-03-03Java反射 JavaBean對(duì)象自動(dòng)生成插入,更新,刪除,查詢sql語(yǔ)句操作
這篇文章主要介紹了Java反射 JavaBean對(duì)象自動(dòng)生成插入,更新,刪除,查詢sql語(yǔ)句操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2020-08-08idea項(xiàng)目結(jié)構(gòu)中不顯示out文件夾的解決
本文通過圖片的方式詳細(xì)解釋操作步驟,使讀者能夠更直觀更方便地理解和執(zhí)行操作,同時(shí),文章末尾祝福讀者步步高升,一帆風(fēng)順,展現(xiàn)了作者的人情味和親和力,整體來(lái)說(shuō),這是一篇簡(jiǎn)單易懂、實(shí)用性強(qiáng)的操作指南2024-10-10Java中短路運(yùn)算符與邏輯運(yùn)算符示例詳解
這篇文章主要給大家介紹了關(guān)于Java中短路運(yùn)算符與邏輯運(yùn)算符的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01MybatisX無(wú)法自動(dòng)生成entity實(shí)體類的解決方法
本文主要介紹了MybatisX無(wú)法自動(dòng)生成entity實(shí)體類的解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06