springboot整合多数据源的配置以及动态切换数据源,注解切换数据源

springboot整合多数据源的配置以及动态切换数据源,注解切换数据源

在许多应用程序中,可能需要使用多个数据库或数据源来处理不同的业务需求。Spring Boot提供了简便的方式来配置和使用多数据源,使开发人员能够轻松处理多个数据库连接。如果你的项目中可能需要随时切换数据源的话,那我这篇文章可能能帮助到你

ℹ️:这里对于pom文件中坐标的引入我就不多赘言了

配置文件1️⃣:properties文件中

代码语言:javascript代码运行次数:0运行复制# 数据源配置

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

# 数据源配置

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就是区分

2️⃣:配置类实现多数据源配置

代码语言:javascript代码运行次数:0运行复制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.设置默认数据源

dynamicDataSource.setDefaultTargetDataSource(primaryDataSource);

// 2.配置多数据源

Map map = new HashMap<>();

map.put(DataSourceType.PRIMARY, primaryDataSource);

map.put(DataSourceType.SECOND, secondDataSource);

// 3.存放数据源集

dynamicDataSource.setTargetDataSources(map);

return dynamicDataSource;

}

@Bean(name = DataSourceType.PRIMARY)

@ConfigurationProperties(prefix = "spring.datasource.mysql.primary")

public DataSource primaryDataSource() {

log.info("主数据库连接池创建中.......");

return DruidDataSourceBuilder.create().build();

}

@Bean(name = DataSourceType.SECOND)

@ConfigurationProperties(prefix = "spring.datasource.mysql.slave1")

public DataSource secondDataSource() {

log.info("second数据库连接池创建中.......");

return DruidDataSourceBuilder.create().build();

}

}3️⃣:自定义注解实现,可使用自定义注解来切换数据源

代码语言:javascript代码运行次数:0运行复制package com.todoitbo.tallybookdasmart.multiDataSource;

import java.lang.annotation.*;

/**

* description: 自定义注解,标记数据源

*

* @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,用于在方法调用前后切换数据源。以下是代码的解释:

@Aspect:指定该类为切面类,用于定义切面的切入点和增强逻辑。@Order(value=1):指定切面的执行顺序,数值越小优先级越高。@Component:将该切面类声明为Spring的组件,使其可以被自动扫描并装配到Spring容器中。@Pointcut(value = "execution(* com.todoitbo.tallybookdasmart.service.*.*(..)) || execution(* com.todoitbo.tallybookdasmart.*.*(..))"):定义切入点表达式,指定需要切入的目标方法。@Around("dataSourcePointCut()"):定义环绕通知,表示在目标方法执行前后执行切面逻辑。public Object around(ProceedingJoinPoint joinPoint) throws Throwable:环绕通知方法,包含切面逻辑。在方法中通过反射获取目标方法的注解信息,判断是否存在@DataSource注解,并获取注解中设置的数据源名称。调用DataSourceContextHolder.setDataSource(dataSource)方法,将获取到的数据源名称设置到当前线程的上下文中。调用joinPoint.proceed()方法,继续执行目标方法。在finally块中调用DataSourceContextHolder.clearDataSourceType()方法,清除当前线程中存储的数据源信息。代码语言:javascript代码运行次数:0运行复制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 {

/** 定义切入点表达式*/

@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 {

// 使用反射获取目标类中指定方法名和参数类型的方法对象

Method m = classz.getMethod(method, parameterTypes);

// 设置默认的数据源名称

String dataSource = DataSourceType.PRIMARY;

// 判断方法是否被@DataSource注解标记。

if (m.isAnnotationPresent(DataSource.class)) {

// 通过getAnnotation()方法获取方法上的@DataSource注解对象。

DataSource ds = m.getAnnotation(DataSource.class);

// 获取注解对象中设置的数据源名称

dataSource = ds.value();

}

// 将获取到的数据源名称设置到当前线程的上下文中

DataSourceContextHolder.setDataSource(dataSource);

// 继续执行目标方法

return joinPoint.proceed();

} finally {

DataSourceContextHolder.clearDataSourceType();

}

}

}5️⃣:存储和获取当前线程数据源的上下文工具类

这段代码是一个用于存储和获取当前线程数据源的上下文工具类。它使用了Netty的FastThreadLocal来实现线程本地的快速存取。

创建FastThreadLocal对象:在类中定义了一个名为CONTEXT_HOLDER的FastThreadLocal对象,用于存储当前线程的数据源信息。设置数据源:setDataSource方法用于将数据源名称设置到当前线程的上下文中。通过调用CONTEXT_HOLDER.set(dataSource),将数据源名称存储在当前线程中。获取数据源:getDataSource方法用于从当前线程的上下文中获取数据源名称。通过调用CONTEXT_HOLDER.get(),可以获取当前线程的数据源名称。清除数据源:clearDataSourceType方法用于清除当前线程中存储的数据源信息。通过调用CONTEXT_HOLDER.remove(),可以清除当前线程中的数据源信息。代码语言:javascript代码运行次数:0运行复制package com.todoitbo.tallybookdasmart.multiDataSource;

import io.netty.util.concurrent.FastThreadLocal;

/**

* description: 存储和获取当前线程数据源的上下文工具类

*

* @author bo

* @version 1.0

* @date 2023/5/19 08:44

*/

public class DataSourceContextHolder {

/**

* 创建FastThreadLocal对象,存储当前线程的数据源信息

*/

private static final FastThreadLocal CONTEXT_HOLDER = new FastThreadLocal();

/**

* 设置数据源

*/

public static void setDataSource(String dataSource) {

CONTEXT_HOLDER.set(dataSource);

}

/**

* 获取数据源

*/

public static String getDataSource() {

return CONTEXT_HOLDER.get();

}

/**

* 清除数据源

*/

public static void clearDataSourceType() {

CONTEXT_HOLDER.remove();

}

}6️⃣:数据源类型

代码语言:javascript代码运行次数:0运行复制package com.todoitbo.tallybookdasmart.multiDataSource;

/**

* @author xiaobo

*/

public class DataSourceType {

public static final String PRIMARY = "primaryDataSource";

public static final String SECOND = "secondDataSource";

}7️⃣:根据当前线程中的数据源上下文获取对应的数据源

代码语言:javascript代码运行次数:0运行复制package com.todoitbo.tallybookdasmart.multiDataSource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**

* description: 根据当前线程中的数据源上下文获取对应的数据源。

*

* @author bo

* @version 1.0

* @date 2023/5/19 08:46

*/

public class DynamicDataSource extends AbstractRoutingDataSource {

@Override

protected Object determineCurrentLookupKey() {

return DataSourceContextHolder.getDataSource();

}

}具体实现 在service的实现类的方法上加入注解即可

代码语言:javascript代码运行次数:0运行复制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)服务

*

* @author bo

* @since 2023-04-18 21:13:14

*/

@Service

public class TbConfigServiceImpl extends BaseServiceImpl implements ITbConfigService {

@Resource

protected TbConfigMapper mapper;

@Override

@DataSource(DataSourceType.SECOND)

public List testList() {

return mapper.selectList(new QueryWrapper<>());

}

}效果图:

image-20230522084706414⚠️:这里只是想展示他确实是走了从数据源了

相关推荐

灵枢·杂病篇第二十六
华为怎么进BT365

灵枢·杂病篇第二十六

📅 07-28 👁️ 4683
同学聚会活动组织计划如何制定(5篇)
华为怎么进BT365

同学聚会活动组织计划如何制定(5篇)

📅 07-08 👁️ 3242
救命🆘这些订机票app,真的很实用!
体育365网投

救命🆘这些订机票app,真的很实用!

📅 07-11 👁️ 9407