日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

SpringBoot多数据源(主从数据源)配置

發(fā)布時(shí)間:2023/12/3 javascript 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringBoot多数据源(主从数据源)配置 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

🎶前言

學(xué)習(xí)springboot配置多數(shù)據(jù)源,先回顧一下springboot配置單數(shù)據(jù)源的方式
SpringBoot配置mybatis-mysql數(shù)據(jù)源

🔠主從數(shù)據(jù)源搭建

項(xiàng)目依賴

本次記錄多數(shù)據(jù)源配置主要是通過druid + mybatis plus + aop的形式實(shí)現(xiàn)的,mybatis plus是一個(gè)很方便的數(shù)據(jù)庫操作框架,自己也有實(shí)現(xiàn)多數(shù)據(jù)源的jar包,這里沒有使用她封裝的方法,主要是學(xué)習(xí)所以是自行實(shí)現(xiàn)了一遍簡(jiǎn)單的多數(shù)據(jù)源配置和動(dòng)態(tài)切換數(shù)據(jù)源。

<!-- mybatis-plus多數(shù)據(jù)源配置jar --> <dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId> </dependency>

使用到的依賴

<dependencies><!-- druid數(shù)據(jù)連接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId></dependency><!-- mybatis plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId></dependency><!-- spring-aop --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><!-- springboot配置依賴 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><!-- mysql驅(qū)動(dòng) --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency> </dependencies>

Yml文件配置數(shù)據(jù)源

這里可以看到數(shù)據(jù)源配置屬性路徑并非spring.datasource, 這里主要是想通過學(xué)習(xí)spring-boot配置文件自動(dòng)裝配, 來獲取配置并初始化數(shù)據(jù)源。

utmost:# 主從數(shù)據(jù)源配置datasource:dynamic:master:driverClassName: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://192.168.92.10:3306/utmost_01?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8userName: rootpassword: rootslave:enabled: true # 是否啟用從數(shù)據(jù)源driverClassName: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://192.168.92.10:3306/utmost_02?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8userName: rootpassword: root @Getter @Setter @ConfigurationProperties(prefix = UtmostDataSourceProperties.PREFIX) public class UtmostDataSourceProperties {/*** 配置前綴*/public static final String PREFIX = "utmost.datasource.dynamic";/*** master數(shù)據(jù)源配置前綴*/public static final String MASTER_PREFIX = "utmost.datasource.dynamic.master";/*** slave數(shù)據(jù)源配置前綴*/public static final String SLAVE_PREFIX = "utmost.datasource.dynamic.slave";/*** 設(shè)置默認(rèn)數(shù)據(jù)庫, 默認(rèn)master*/public String primary = "master";/*** 設(shè)置啟用數(shù)據(jù)源, 默認(rèn)true*/public boolean enabled = true;/*** 主數(shù)據(jù)源*/public SingleDataSourceProperty master;/*** 從數(shù)據(jù)源*/public SingleDataSourceProperty slave; } @Data @Accessors(chain = true) public class SingleDataSourceProperty {/*** JDBC driver*/private String driverClassName;/*** JDBC 數(shù)據(jù)庫地址*/private String url;/*** JDBC 用戶名*/private String userName;/*** JDBC 用戶密碼*/private String password; }

怎么通過UtmostDataSourceProperties類來獲取屬性, 主要通過@ConfigurationProperties注解實(shí)現(xiàn), 前面依賴中引入了spring-boot-configuration-processor依賴, 在使用yml配置數(shù)據(jù)源時(shí)就會(huì)出現(xiàn)一定的提示作用。這是因?yàn)樵诖虬幾g的時(shí)候會(huì)生成一個(gè)spring-configuration-metadata.json文件,這里就不贅述了,先了解到這是springboot幫助我們生成的作用于提示的文件就可以了。

mybatis-plus配置

# mybatis-plus 配置 mybatis-plus:configuration:# 開啟駝峰map-underscore-to-camel-case: true# 關(guān)閉一級(jí)緩存local-cache-scope: statement# 關(guān)閉二級(jí)緩存cache-enabled: false# sql xml文件映射路徑mapper-locations: classpath*:/mapper/*.xml# MyBaits 別名包掃描路徑,通過該屬性可以給包中的類注冊(cè)別名type-aliases-package: com.zy.utmost.entity

數(shù)據(jù)源裝配

數(shù)據(jù)源配置類

@Slf4j @Configuration @AllArgsConstructor @EnableConfigurationProperties(UtmostDataSourceProperties.class) @ConditionalOnProperty(prefix = UtmostDataSourceProperties.PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true) public class UtmostDataSourceAutoConfiguration {private final UtmostDataSourceProperties utmostDataSourceProperties;/*** 主數(shù)據(jù)源(這里配置寫的繁瑣一點(diǎn), 可根據(jù)個(gè)人喜好進(jìn)行簡(jiǎn)化.)** @return DataSource*/@Bean(name = "masterDataSource")@ConfigurationProperties(UtmostDataSourceProperties.MASTER_PREFIX)public DataSource masterDataSource() {SingleDataSourceProperty master = utmostDataSourceProperties.master;DruidDataSource druidDataSource = new DruidDataSource();druidDataSource.setName(DataSourceConstants.MASTER);druidDataSource.setDriverClassName(master.getDriverClassName());druidDataSource.setUrl(master.getUrl());druidDataSource.setUsername(master.getUserName());druidDataSource.setPassword(master.getPassword());return druidDataSource;}/*** 從 數(shù)據(jù)源(這里配置寫的繁瑣一點(diǎn), 可根據(jù)個(gè)人喜好進(jìn)行簡(jiǎn)化.)** @return DataSource*/@Bean(name = "slaveDataSource")@ConfigurationProperties(UtmostDataSourceProperties.SLAVE_PREFIX)@ConditionalOnProperty(prefix = UtmostDataSourceProperties.SLAVE_PREFIX, name = "enabled", havingValue = "true")public DataSource slaveDataSource() {SingleDataSourceProperty slave = utmostDataSourceProperties.slave;DruidDataSource druidDataSource = new DruidDataSource();druidDataSource.setName(DataSourceConstants.SLAVE);druidDataSource.setDriverClassName(slave.getDriverClassName());druidDataSource.setUrl(slave.getUrl());druidDataSource.setUsername(slave.getUserName());druidDataSource.setPassword(slave.getPassword());return druidDataSource;}/*** 動(dòng)態(tài)數(shù)據(jù)源** @return DynamicDataSource*/@Bean@Primarypublic DynamicDataSource dynamicDataSource() {Map<Object, Object> targetMap = new HashMap<>(2);targetMap.put(DataSourceConstants.MASTER, masterDataSource());targetMap.put(DataSourceConstants.SLAVE, slaveDataSource());DynamicDataSource dynamicDataSource = new DynamicDataSource(masterDataSource(), targetMap);log.info("動(dòng)態(tài)數(shù)據(jù)源裝配完成...");return dynamicDataSource;} }

動(dòng)態(tài)數(shù)據(jù)源實(shí)現(xiàn)類

通過繼承重寫AbstractRoutingDataSource 數(shù)據(jù)源路由來實(shí)現(xiàn)數(shù)據(jù)源動(dòng)態(tài)切換的功能.

public class DynamicDataSource extends AbstractRoutingDataSource {/*** 使用線程切換數(shù)據(jù)源*/private static ThreadLocal<String> contextHandler = new ThreadLocal<>();/*** 數(shù)據(jù)源key集合*/private static List<Object> dataSourceKeys = new ArrayList<>();/*** 配置數(shù)據(jù)源** @param defaultDataSource 主數(shù)據(jù)源* @param targetDataSourceMap 其他數(shù)據(jù)源集合*/public DynamicDataSource(DataSource defaultDataSource, Map<Object, Object> targetDataSourceMap) {super.setDefaultTargetDataSource(defaultDataSource);super.setTargetDataSources(targetDataSourceMap);super.afterPropertiesSet();// 初始化所有數(shù)據(jù)源的keyaddAllDataSourceKeys(targetDataSourceMap.keySet());}public static void setDataSourceKeys(String key) {contextHandler.set(key);}public static ThreadLocal<String> getDataSourceKeys() {return contextHandler;}public static void removeDataSourceKeys() {contextHandler.remove();}public static boolean containsDataSourceKeys(String key) {return dataSourceKeys.contains(key);}public static boolean addAllDataSourceKeys(Collection<? extends Object> keys) {return dataSourceKeys.addAll(keys);}@Overrideprotected Object determineCurrentLookupKey() {return contextHandler.get();} }

數(shù)據(jù)源切換注解

/*** 注解命名主要是為了好記所以直接使用了DataSource, 在使用時(shí)會(huì)發(fā)現(xiàn)有很* 多類都是以DataSource命名, 使用時(shí)需要注意.* @author yanzy*/ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DataSource {String value() default ""; }

數(shù)據(jù)源切換實(shí)現(xiàn)

/*** 實(shí)現(xiàn)注解織入切換數(shù)據(jù)源** @author yanzy* @date 2021/6/10 23:38* @since v1.0*/ @Aspect @Order(-10) @Component public class DataSourceAspect {private Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);/*** - @within掃描類注解, @annotation掃描方法上注解* 定義切面*/@Pointcut("@annotation(com.zy.utmost.annotation.DataSource) || @within(com.zy.utmost.annotation.DataSource)")public void annotationPointCut() {}/*** 前置事件 (方法前切換數(shù)據(jù)源)* @param point 織入點(diǎn)* @param dataSource 注解實(shí)例*/@Before("annotationPointCut() && @annotation(dataSource))")public void beforeMethod(JoinPoint point, DataSource dataSource) {switchDataSource(point, dataSource);}/*** 前置事件 (類前切換數(shù)據(jù)源)* @param point 織入點(diǎn)* @param dataSource 注解實(shí)例*/@Before("annotationPointCut() && @within(dataSource))")public void beforeClass(JoinPoint point, DataSource dataSource) {switchDataSource(point, dataSource);}private void switchDataSource(JoinPoint point,DataSource dataSource) {String key = dataSource.value();if (!DynamicDataSource.containsDataSourceKeys(key)) {logger.debug("數(shù)據(jù)源切換失敗: [{}] - 數(shù)據(jù)源不存在, 自動(dòng)使用默認(rèn)數(shù)據(jù)源.", key);} else {DynamicDataSource.setDataSourceKeys(key);logger.debug("數(shù)據(jù)源切換成功: [{}] - 已切換至 - [{}] - 數(shù)據(jù)源.", point.getSignature().getName(), key);}}/*** 后置增強(qiáng) (方法/類執(zhí)行完畢后,將數(shù)據(jù)源切回默認(rèn))** @param point 織入點(diǎn)*/@After("annotationPointCut()")public void after(JoinPoint point) {if (null != DynamicDataSource.getDataSourceKeys()) {DynamicDataSource.removeDataSourceKeys();logger.debug("數(shù)據(jù)源切換成功: 切換為主數(shù)據(jù)源.");}} }

搭建完測(cè)試

測(cè)試使用的crud類就不附上代碼了, 直接使用spring-boot-test來給數(shù)據(jù)庫插入數(shù)據(jù), 驗(yàn)證一下數(shù)據(jù)源是否可以正常切換.

數(shù)據(jù)庫

首先這里使用了兩個(gè)數(shù)據(jù)庫, 都創(chuàng)建了utmost這個(gè)數(shù)據(jù)庫實(shí)例并創(chuàng)建有sys_user表.

service實(shí)現(xiàn)類(方法增加注解切換數(shù)據(jù)源)

@Service public class SysUserServiceImpl implements SysUserService {@Resourceprivate SysUserMapper sysUserMapper;// 主庫插入@Overridepublic Integer insertUserMaster(SysUser sysUser) {return sysUserMapper.insert(sysUser);}// 從庫插入@Override@DataSource(value = DataSourceConstants.SLAVE)public Integer insertUserSlave(SysUser sysUser) {return sysUserMapper.insert(sysUser);} }

📣測(cè)試類

@SpringBootTest public class SysUserTableTest {@Autowiredprivate SysUserService sysUserService;@Testpublic void insertByMaster() {SysUser sysUser = new SysUser();sysUser.setUserName("admin");sysUser.setLoginName("admin");sysUser.setPassword("admin");sysUserService.insertUserMaster(sysUser);}@Testpublic void insertBySlave() {SysUser sysUser = new SysUser();sysUser.setUserName("admin");sysUser.setLoginName("admin");sysUser.setPassword("admin");sysUserService.insertUserSlave(sysUser);} }

測(cè)試主庫插入, 不標(biāo)注注解時(shí), 默認(rèn)使用主庫.

測(cè)試從庫插入, 調(diào)用使用了切換數(shù)據(jù)源注解得到方法

類標(biāo)注注解測(cè)試

@Service @DataSource(value = DataSourceConstants.SLAVE) public class SysUserServiceImpl implements SysUserService {@Resourceprivate SysUserMapper sysUserMapper;@Overridepublic Integer insertUserMaster(SysUser sysUser) {return sysUserMapper.insert(sysUser);}@Overridepublic Integer insertUserSlave(SysUser sysUser) {return sysUserMapper.insert(sysUser);} }

調(diào)用insertUserMaster, 驗(yàn)證數(shù)據(jù)源是否切換成功.

💯總結(jié)

好啦~ 以上主要記錄一下自己在學(xué)習(xí)過程中是如何配置多數(shù)據(jù)源的(嚴(yán)格來說代碼中的寫法應(yīng)該是主從數(shù)據(jù)源 嘿嘿)。
一般主從數(shù)據(jù)源主要是為了做讀寫分離的,后面學(xué)習(xí)總結(jié)完讀寫分離操作后在進(jìn)行分享記錄啦~~

總結(jié)

以上是生活随笔為你收集整理的SpringBoot多数据源(主从数据源)配置的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。