SpringBoot多數(shù)據(jù)源切換實(shí)現(xiàn)代碼(Mybaitis)
前言
但是在實(shí)際業(yè)務(wù)場景中,數(shù)據(jù)量迅速增長,一個(gè)庫一個(gè)表已經(jīng)滿足不了我們的需求的時(shí)候,我們就會(huì)考慮分庫分表的操作,在springboot中如何實(shí)現(xiàn)多數(shù)據(jù)源,動(dòng)態(tài)數(shù)據(jù)源切換,讀寫分離等操作。 當(dāng)你看到這篇文件那么你幸運(yùn)了,下面直接提供終極通用版代碼
如果是非Mybaitis的那么可以進(jìn)行參照,原理都差不多
配置文件(YML)
spring: datasource: default-db-key: voidme multi-db: - voidme: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root url: jdbc:mysql://192.168.42.153:3306/voidme?characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&useSSL=false - xcdef: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root url: jdbc:mysql://192.168.42.153:3306/xcdef?characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&useSSL=false mybatis: #1.classpath:只會(huì)到你的classes路徑中查找找文件。 #2.classpath*:不僅會(huì)到classes路徑,還包括jar文件中(classes路徑)進(jìn)行查找。 mapper-locations: classpath*:/mapper/**/*Mapper.xml # mapper映射文件位置 type-aliases-package: com.**.entity # 實(shí)體類所在的位置 configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #用于控制臺(tái)打印sql語句 map-underscore-to-camel-case: true #開啟將帶有下劃線的表字段 映射為駝峰格式的實(shí)體類屬性
核心代碼
DynamicDataSource
這個(gè)類用于獲取數(shù)據(jù)源的(核心)
package com.dynamicdatadource.dynamic; import org.springframework.beans.factory.annotation.Value; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Value("${spring.datasource.default-db-key}") private String defaultDbKey; @Override protected Object determineCurrentLookupKey() { String currentDb = DynamicDataSourceService.currentDb(); if (currentDb == null) { return defaultDbKey; } return currentDb; } }
DynamicDataSourceService
這個(gè)類是數(shù)據(jù)源切換工具,我們做了線程隔離了所以不用擔(dān)心多線程數(shù)據(jù)源會(huì)混亂的問題
package com.dynamicdatadource.dynamic; import com.application.ApplicationContextProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.jdbc.DataSourceBuilder; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; public class DynamicDataSourceService { private static final Logger log = LoggerFactory.getLogger(DynamicDataSourceService.class); private static final Map<Object, Object> dataSources = new HashMap<>(); private static final ThreadLocal<String> dbKeys = ThreadLocal.withInitial(() -> null); /** * 動(dòng)態(tài)添加一個(gè)數(shù)據(jù)源 * * @param name 數(shù)據(jù)源的key * @param dataSource 數(shù)據(jù)源對象 */ public static void addDataSource(String name, DataSource dataSource) { DynamicDataSource dynamicDataSource = ApplicationContextProvider.getApplicationContext().getBean(DynamicDataSource.class); dataSources.put(name, dataSource); dynamicDataSource.setTargetDataSources(dataSources); dynamicDataSource.afterPropertiesSet(); log.info("添加了數(shù)據(jù)源:{}",name); } /** * @param name 數(shù)據(jù)源的key * @param driverClassName 驅(qū)動(dòng) * @param url 數(shù)據(jù)庫連接地址 * @param username 數(shù)據(jù)庫賬戶 * @param password 數(shù)據(jù)庫密碼 */ public static void addDataSource(String name, String driverClassName,String url,String username,String password) { DataSourceBuilder<?> builder = DataSourceBuilder.create(); builder.driverClassName(driverClassName); builder.username(username); builder.password(password); builder.url(url); addDataSource(name,builder.build()); log.info("添加了數(shù)據(jù)源:{}",name); } /** * 切換數(shù)據(jù)源 */ public static void switchDb(String dbKey) { dbKeys.set(dbKey); } /** * 重置數(shù)據(jù)源(切換為默認(rèn)的數(shù)據(jù)源) */ public static void resetDb() { dbKeys.remove(); } /** * 獲取當(dāng)前數(shù)據(jù)源的key */ public static String currentDb() { return dbKeys.get(); } }
DynamicDataSourceConfig
將數(shù)據(jù)源配置到springboot中和初始化Mybaitis配置
package com.dynamicdatadource.dynamic; import lombok.Data; import org.apache.ibatis.logging.Log; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import java.io.IOException; import java.util.HashMap; import java.util.Map; @Configuration @ConfigurationProperties(prefix = "mybatis") @Data public class DynamicDataSourceConfig { private String mapperLocations; private String typeAliasesPackage; @Data public class MybatisConfiguration{ private String logImpl; private boolean mapUnderscoreToCamelCase; } private MybatisConfiguration configuration=new MybatisConfiguration(); /** * 動(dòng)態(tài)數(shù)據(jù)源 */ @Bean public DynamicDataSource dynamicDataSource() { DynamicDataSource dataSource = new DynamicDataSource(); Map<Object, Object> targetDataSources = new HashMap<>(); dataSource.setTargetDataSources(targetDataSources); return dataSource; } /** * 會(huì)話工廠Mybaitis */ @Bean public SqlSessionFactoryBean sqlSessionFactoryBean() throws IOException, ClassNotFoundException { org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); configuration.setMapUnderscoreToCamelCase(this.configuration.isMapUnderscoreToCamelCase()); //開啟駝峰命名 configuration.setLogImpl((Class<? extends Log>) Class.forName(this.configuration.getLogImpl())); //控制臺(tái)打印sql日志 SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dynamicDataSource()); sqlSessionFactoryBean.setConfiguration(configuration); PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); sqlSessionFactoryBean.setMapperLocations(resolver.getResources(mapperLocations)); sqlSessionFactoryBean.setTypeAliasesPackage(typeAliasesPackage); return sqlSessionFactoryBean; } /** * 事務(wù)管理器 */ @Bean public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dynamicDataSource()); } }
加載YML數(shù)據(jù)庫配置類
package com.dynamicdatadource.config; import com.dynamicdatadource.dynamic.DynamicDataSourceService; import lombok.Data; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.sql.DataSource; import java.util.List; import java.util.Map; import java.util.Set; @Component @Data @ConfigurationProperties(prefix = "spring.datasource") public class YmlDataSourceProvider { private List<Map<String, DataSourceProperties>> multiDb; private DataSource buildDataSource(DataSourceProperties prop) { DataSourceBuilder<?> builder = DataSourceBuilder.create(); builder.driverClassName(prop.getDriverClassName()); builder.username(prop.getUsername()); builder.password(prop.getPassword()); builder.url(prop.getUrl()); return builder.build(); } public void initDataSource() { multiDb.forEach(map -> { Set<String> keys = map.keySet(); keys.forEach(key -> { DataSourceProperties properties = map.get(key); DataSource dataSource = buildDataSource(properties); DynamicDataSourceService.addDataSource(key, dataSource); }); }); } //在構(gòu)造函數(shù)之后執(zhí)行 @PostConstruct public void init() { initDataSource(); } }
aop切換
package com.dynamicdatadource.aop; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD,ElementType.TYPE})//作用:方法和類 @Retention(RetentionPolicy.RUNTIME) public @interface DynamicDataSourceAnno { String key() default ""; }
package com.dynamicdatadource.aop; import com.dynamicdatadource.dynamic.DynamicDataSourceService; import org.apache.commons.lang.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; // 用于單獨(dú)的請求或者類進(jìn)行切換數(shù)據(jù)庫 @Aspect @Component public class DynamicDataSourceAspect { @Pointcut("@annotation(com.dynamicdatadource.aop.DynamicDataSourceAnno)") public void dynamicDataSourceAnno() { } @Around("dynamicDataSourceAnno()") public Object DynamicDataSourceAspectAroundAnno(ProceedingJoinPoint joinPoint) { Object object = null; try { MethodSignature signature = (MethodSignature)joinPoint.getSignature(); DynamicDataSourceAnno dynamicDataSourceAnno = signature.getMethod().getAnnotation(DynamicDataSourceAnno.class); String key = dynamicDataSourceAnno.key(); if (StringUtils.isNotBlank(key)) { //切換為指定數(shù)據(jù)庫 DynamicDataSourceService.switchDb(key); } object = joinPoint.proceed(); } catch (Throwable e) { e.printStackTrace(); }finally { //還原為默認(rèn)配置 DynamicDataSourceService.resetDb(); } return object; } // 還可以擴(kuò)展包路徑切換 }
效果
運(yùn)行程序之后,就會(huì)將數(shù)據(jù)源加入到數(shù)據(jù)源列表中了
擴(kuò)展
MysqlDataSourceInitialize
從數(shù)據(jù)庫中將配置信息查詢出來,然后動(dòng)態(tài)添加到數(shù)據(jù)源列表中
package com.dao.config; import com.dao.DatasourceDao; import com.dynamicdatadource.aop.DynamicDataSourceAnno; import com.dynamicdatadource.dynamic.DynamicDataSourceService; import com.entity.DataSourceEneity; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.sql.DataSource; import java.util.List; //從數(shù)據(jù)庫中查詢出全部的數(shù)據(jù)源,添加到數(shù)據(jù)源容器中 /** * 表結(jié)構(gòu)如下: * * CREATE TABLE `t_datasource` ( * `id` int(11) NOT NULL, * `key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '綁定的key,用于數(shù)據(jù)源的切換', * `url` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '數(shù)據(jù)庫連接地址', * `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '數(shù)據(jù)庫用戶名', * `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '數(shù)據(jù)庫密碼', * `driverClassName` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '數(shù)據(jù)庫驅(qū)動(dòng)', * `type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '數(shù)據(jù)庫類型: mysql ,oracle,..', * `state` int(2) NOT NULL COMMENT '是否可用: 1可用 ,2不可用', * PRIMARY KEY (`id`), * UNIQUE KEY `key` (`key`) * ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; * * 上表要放入到默認(rèn)數(shù)據(jù)源中的數(shù)據(jù)庫里才行 */ @Component public class MysqlDataSourceInitialize implements ApplicationRunner { @Autowired private DatasourceDao datasourceDao; //項(xiàng)目啟動(dòng)后執(zhí)行初始化數(shù)據(jù)源 @Override public void run(ApplicationArguments args) throws Exception { try { List<DataSourceEneity> dataSources = datasourceDao.getDataSources(); for (DataSourceEneity dataSource : dataSources) { DynamicDataSourceService.addDataSource(dataSource.getKey(),dataSource.getDataSource()); } } catch (Exception e) { e.printStackTrace(); } } }
DataSourceEneity實(shí)體類
@Data public class DataSourceEneity { private int id; private String key; private String url; private String username; private String password; private String driverClassName; private String type; private int state; public DataSource getDataSource() { DataSourceBuilder<?> builder = DataSourceBuilder.create(); builder.driverClassName(driverClassName); builder.username(username); builder.password(password); builder.url(url); return builder.build(); } }
總結(jié)
到此這篇關(guān)于SpringBoot多數(shù)據(jù)源切換實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)SpringBoot多數(shù)據(jù)源切換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- springboot dynamic多數(shù)據(jù)源demo以及常見切換、事務(wù)的問題
- Springboot實(shí)現(xiàn)多數(shù)據(jù)源切換詳情
- SpringBoot多數(shù)據(jù)源配置并通過注解實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源
- SpringBoot基于AbstractRoutingDataSource實(shí)現(xiàn)多數(shù)據(jù)源動(dòng)態(tài)切換
- SpringBoot實(shí)現(xiàn)多數(shù)據(jù)源的切換實(shí)踐
- SpringBoot?+DynamicDataSource切換多數(shù)據(jù)源的全過程
- springboot中mybatis多數(shù)據(jù)源動(dòng)態(tài)切換實(shí)現(xiàn)
- Springboot如何設(shè)置多數(shù)據(jù)源,隨時(shí)切換
相關(guān)文章
java實(shí)現(xiàn)字符串反轉(zhuǎn)案例
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)字符串反轉(zhuǎn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-09-09淺談Java中浮點(diǎn)型數(shù)據(jù)保留兩位小數(shù)的四種方法
今天在進(jìn)行開發(fā)的過程中遇到了一個(gè)小問題,是關(guān)于如何將double類型的數(shù)據(jù)保留兩位小數(shù)。具有一定的參考價(jià)值,本文就詳細(xì)的介紹一下2021-09-09使用Mybatis Generator結(jié)合Ant腳本快速自動(dòng)生成Model、Mapper等文件的方法
這篇文章主要介紹了使用Mybatis Generator結(jié)合Ant腳本快速自動(dòng)生成Model、Mapper等文件的方法的相關(guān)資料,需要的朋友可以參考下2016-06-06SpringMVC訪問靜態(tài)資源的三種方式小結(jié)
這篇文章主要介紹了SpringMVC訪問靜態(tài)資源的三種方式小結(jié),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-02-02手動(dòng)編譯并運(yùn)行Java項(xiàng)目實(shí)現(xiàn)過程解析
這篇文章主要介紹了手動(dòng)編譯并運(yùn)行Java項(xiàng)目實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10利用Springboot實(shí)現(xiàn)Jwt認(rèn)證的示例代碼
這篇文章主要介紹了利用Springboot實(shí)現(xiàn)Jwt認(rèn)證的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12