在許多應(yīng)用程序中,可能需要使用多個數(shù)據(jù)庫或數(shù)據(jù)源來處理不同的業(yè)務(wù)需求。Spring Boot提供了簡便的方式來配置和使用多數(shù)據(jù)源,使開發(fā)人員能夠輕松處理多個數(shù)據(jù)庫連接。如果你的項(xiàng)目中可能需要隨時切換數(shù)據(jù)源的話,那我這篇文章可能能幫助到你
??:這里對于pom文件中坐標(biāo)的引入我就不多贅言了
配置文件
1??:properties文件中
# 數(shù)據(jù)源配置
spring.datasource.mysql.primary.url=jdbc:mysql://127.0.0.1:3351/tally_book?characterEncoding=utf8&serverTimezone=UTC
spring.datasource.mysql.primary.username=root
spring.datasource.mysql.primary.password=123456
spring.datasource.mysql.primary.driver-class-name=com.mysql.cj.jdbc.Driver
# 數(shù)據(jù)源配置
spring.datasource.mysql.slave1.url=jdbc:mysql://127.0.0.1:3351/dingding_mid?characterEncoding=utf8&serverTimezone=UTC
spring.datasource.mysql.slave1.username=root
spring.datasource.mysql.slave1.password=123456
spring.datasource.mysql.slave1.driver-class-name=com.mysql.cj.jdbc.Driver
上面的配置文件中我只寫了兩個源,而且都是mysql 的,primary和slave1就是區(qū)分
2??:配置類實(shí)現(xiàn)多數(shù)據(jù)源配置
package com.todoitbo.tallybookdasmart.config;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.todoitbo.tallybookdasmart.multiDataSource.DataSourceType;
import com.todoitbo.tallybookdasmart.multiDataSource.DynamicDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.*;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* @author xiaobo
* @date 2023/5/19
*/
@Configuration
@Slf4j
public class MultiDataSourceConfig {
@Bean
public PlatformTransactionManager platformTransactionManager(DataSource dynamicDataSource) {
return new DataSourceTransactionManager(dynamicDataSource);
}
@Bean
@Primary
@DependsOn("primaryDataSource")
public DataSource dynamicDataSource(@Qualifier(DataSourceType.PRIMARY) DataSource primaryDataSource,
@Qualifier(DataSourceType.SECOND) DataSource secondDataSource) {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 1.設(shè)置默認(rèn)數(shù)據(jù)源
dynamicDataSource.setDefaultTargetDataSource(primaryDataSource);
// 2.配置多數(shù)據(jù)源
Map<Object, Object> map = new HashMap<>();
map.put(DataSourceType.PRIMARY, primaryDataSource);
map.put(DataSourceType.SECOND, secondDataSource);
// 3.存放數(shù)據(jù)源集
dynamicDataSource.setTargetDataSources(map);
return dynamicDataSource;
}
@Bean(name = DataSourceType.PRIMARY)
@ConfigurationProperties(prefix = "spring.datasource.mysql.primary")
public DataSource primaryDataSource() {
log.info("主數(shù)據(jù)庫連接池創(chuàng)建中.......");
return DruidDataSourceBuilder.create().build();
}
@Bean(name = DataSourceType.SECOND)
@ConfigurationProperties(prefix = "spring.datasource.mysql.slave1")
public DataSource secondDataSource() {
log.info("second數(shù)據(jù)庫連接池創(chuàng)建中.......");
return DruidDataSourceBuilder.create().build();
}
}
3??:自定義注解實(shí)現(xiàn),可使用自定義注解來切換數(shù)據(jù)源
package com.todoitbo.tallybookdasmart.multiDataSource;
import java.lang.annotation.*;
/**
* description: 自定義注解,標(biāo)記數(shù)據(jù)源
*
* @author bo
* @version 1.0
* @date 2023/5/19 08:45
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface DataSource {
String value() default DataSourceType.PRIMARY;
}
4??:定義一個切面類
這段代碼是一個切面類DataSourceAspect
,用于在方法調(diào)用前后切換數(shù)據(jù)源。以下是代碼的解釋:
-
@Aspect
:指定該類為切面類,用于定義切面的切入點(diǎn)和增強(qiáng)邏輯。 -
@Order(value=1)
:指定切面的執(zhí)行順序,數(shù)值越小優(yōu)先級越高。 -
@Component
:將該切面類聲明為Spring的組件,使其可以被自動掃描并裝配到Spring容器中。 -
@Pointcut(value = "execution(* com.todoitbo.tallybookdasmart.service.*.*(..)) || execution(* com.todoitbo.tallybookdasmart.*.*(..))")
:定義切入點(diǎn)表達(dá)式,指定需要切入的目標(biāo)方法。 -
@Around("dataSourcePointCut()")
:定義環(huán)繞通知,表示在目標(biāo)方法執(zhí)行前后執(zhí)行切面邏輯。 -
public Object around(ProceedingJoinPoint joinPoint) throws Throwable
:環(huán)繞通知方法,包含切面邏輯。 - 在方法中通過反射獲取目標(biāo)方法的注解信息,判斷是否存在
@DataSource
注解,并獲取注解中設(shè)置的數(shù)據(jù)源名稱。 - 調(diào)用
DataSourceContextHolder.setDataSource(dataSource)
方法,將獲取到的數(shù)據(jù)源名稱設(shè)置到當(dāng)前線程的上下文中。 - 調(diào)用
joinPoint.proceed()
方法,繼續(xù)執(zhí)行目標(biāo)方法。 - 在
finally
塊中調(diào)用DataSourceContextHolder.clearDataSourceType()
方法,清除當(dāng)前線程中存儲的數(shù)據(jù)源信息。
package com.todoitbo.tallybookdasmart.multiDataSource;
import lombok.extern.slf4j.Slf4j;
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.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* @author xiaobo
*/
@Aspect
@Order(value=1)
@Component
@Slf4j
public class DataSourceAspect {
/** 定義切入點(diǎn)表達(dá)式*/
@Pointcut(value = "execution(* com.todoitbo.tallybookdasmart.service.*.*(..)) || execution(* com.todoitbo.tallybookdasmart.*.*(..))")
public void dataSourcePointCut() {
}
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Object target = joinPoint.getTarget();
String method = joinPoint.getSignature().getName();
Class<?> classz = target.getClass();
Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();
try {
// 使用反射獲取目標(biāo)類中指定方法名和參數(shù)類型的方法對象
Method m = classz.getMethod(method, parameterTypes);
// 設(shè)置默認(rèn)的數(shù)據(jù)源名稱
String dataSource = DataSourceType.PRIMARY;
// 判斷方法是否被@DataSource注解標(biāo)記。
if (m.isAnnotationPresent(DataSource.class)) {
// 通過getAnnotation()方法獲取方法上的@DataSource注解對象。
DataSource ds = m.getAnnotation(DataSource.class);
// 獲取注解對象中設(shè)置的數(shù)據(jù)源名稱
dataSource = ds.value();
}
// 將獲取到的數(shù)據(jù)源名稱設(shè)置到當(dāng)前線程的上下文中
DataSourceContextHolder.setDataSource(dataSource);
// 繼續(xù)執(zhí)行目標(biāo)方法
return joinPoint.proceed();
} finally {
DataSourceContextHolder.clearDataSourceType();
}
}
}
5??:存儲和獲取當(dāng)前線程數(shù)據(jù)源的上下文工具類
這段代碼是一個用于存儲和獲取當(dāng)前線程數(shù)據(jù)源的上下文工具類。它使用了Netty的FastThreadLocal
來實(shí)現(xiàn)線程本地的快速存取。
- 創(chuàng)建
FastThreadLocal
對象:在類中定義了一個名為CONTEXT_HOLDER
的FastThreadLocal
對象,用于存儲當(dāng)前線程的數(shù)據(jù)源信息。 - 設(shè)置數(shù)據(jù)源:
setDataSource
方法用于將數(shù)據(jù)源名稱設(shè)置到當(dāng)前線程的上下文中。通過調(diào)用CONTEXT_HOLDER.set(dataSource)
,將數(shù)據(jù)源名稱存儲在當(dāng)前線程中。 - 獲取數(shù)據(jù)源:
getDataSource
方法用于從當(dāng)前線程的上下文中獲取數(shù)據(jù)源名稱。通過調(diào)用CONTEXT_HOLDER.get()
,可以獲取當(dāng)前線程的數(shù)據(jù)源名稱。 - 清除數(shù)據(jù)源:
clearDataSourceType
方法用于清除當(dāng)前線程中存儲的數(shù)據(jù)源信息。通過調(diào)用CONTEXT_HOLDER.remove()
,可以清除當(dāng)前線程中的數(shù)據(jù)源信息。
package com.todoitbo.tallybookdasmart.multiDataSource;
import io.netty.util.concurrent.FastThreadLocal;
/**
* description: 存儲和獲取當(dāng)前線程數(shù)據(jù)源的上下文工具類
*
* @author bo
* @version 1.0
* @date 2023/5/19 08:44
*/
public class DataSourceContextHolder {
/**
* 創(chuàng)建FastThreadLocal對象,存儲當(dāng)前線程的數(shù)據(jù)源信息
*/
private static final FastThreadLocal<String> CONTEXT_HOLDER = new FastThreadLocal<String>();
/**
* 設(shè)置數(shù)據(jù)源
*/
public static void setDataSource(String dataSource) {
CONTEXT_HOLDER.set(dataSource);
}
/**
* 獲取數(shù)據(jù)源
*/
public static String getDataSource() {
return CONTEXT_HOLDER.get();
}
/**
* 清除數(shù)據(jù)源
*/
public static void clearDataSourceType() {
CONTEXT_HOLDER.remove();
}
}
6??:數(shù)據(jù)源類型
package com.todoitbo.tallybookdasmart.multiDataSource;
/**
* @author xiaobo
*/
public class DataSourceType {
public static final String PRIMARY = "primaryDataSource";
public static final String SECOND = "secondDataSource";
}
7??:根據(jù)當(dāng)前線程中的數(shù)據(jù)源上下文獲取對應(yīng)的數(shù)據(jù)源
package com.todoitbo.tallybookdasmart.multiDataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* description: 根據(jù)當(dāng)前線程中的數(shù)據(jù)源上下文獲取對應(yīng)的數(shù)據(jù)源。
*
* @author bo
* @version 1.0
* @date 2023/5/19 08:46
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
具體實(shí)現(xiàn)
在service的實(shí)現(xiàn)類的方法上加入注解即可
package com.todoitbo.tallybookdasmart.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.todoitbo.tallybookdasmart.entity.TbConfig;
import com.todoitbo.tallybookdasmart.mapper.TbConfigMapper;
import com.todoitbo.tallybookdasmart.multiDataSource.DataSource;
import com.todoitbo.tallybookdasmart.multiDataSource.DataSourceType;
import com.todoitbo.tallybookdasmart.service.ITbConfigService;
import com.todoitbo.tallybookdasmart.service.base.BaseServiceImpl;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* (TbConfig)服務(wù)
*
* @author bo
* @since 2023-04-18 21:13:14
*/
@Service
public class TbConfigServiceImpl extends BaseServiceImpl<TbConfigMapper,TbConfig> implements ITbConfigService {
@Resource
protected TbConfigMapper mapper;
@Override
@DataSource(DataSourceType.SECOND)
public List<TbConfig> testList() {
return mapper.selectList(new QueryWrapper<>());
}
}
效果圖:
文章來源:http://www.zghlxwxcb.cn/news/detail-689851.html
??:這里只是想展示他確實(shí)是走了從數(shù)據(jù)源了文章來源地址http://www.zghlxwxcb.cn/news/detail-689851.html
到了這里,關(guān)于springboot整合多數(shù)據(jù)源的配置以及動態(tài)切換數(shù)據(jù)源,注解切換數(shù)據(jù)源的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!