Springboot整合多数据源代码示例详解
作者:william_zhao 发布时间:2022-03-31 07:48:59
标签:spring,boot,整合,多,数据源
最近有个老项目想逐步将新业务的数据放到新的数据库,以前的业务还得连接以前的数据库,于是需要整合多数据源 。
多数据源实际上是继承了AbstractRoutingDataSource类,这个类最终实现了DataSource接口,DataSource里只有一个getConnection方法,数据库每次访问的时候都要先通过这个方法获取连接,所有多数据源就是每次访问数据库之前动态的改变数据源。
在请求前改变数据源当然需要用到SpringAOP,自定义注解操作
项目结构
下面上代码:
首先是依赖:
<!--数据库连接-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--sqlserver-->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.2</version>
</dependency>
<!--数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.8</version>
</dependency><!--AOP-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
yml配置数据源
server:
port: 8888
spring:
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
datasource:
druid:
first:
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
jdbc-url: jdbc:mysql://rm-uf6265pj340sc9447oo.mysql.rds.54565.com:3306/dm?serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=utf-8
username: username
password: password
second:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
jdbc-url: jdbc:sqlserver://39.104.203.222:1433;DatabaseName=TestTLcom
username: root
password: 123456
mybatis-plus:
mapper-locations: classpath*:/mapper/*Mapper.xml
type-aliases-package: com.zdyl.dynamicdatasourcedemo.entity
global-config:
#主键类型 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
id-type: 3
#字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
field-strategy: 2
#驼峰下划线转换
db-column-underline: true
#刷新mapper 调试神器
refresh-mapper: true
#数据库大写下划线转换
#capital-mode: true
#序列接口实现类配置
#key-generator: com.baomidou.springboot.xxx
#逻辑删除配置
#logic-delete-value: 0
#logic-not-delete-value: 1
#自定义填充策略接口实现
#meta-object-handler: com.baomidou.springboot.xxx
#自定义SQL注入器
#sql-injector: com.baomidou.springboot.xxx
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
定义数据库名称
/**
* 数据库名称
*/
public interface DataSourceNames {
String FIRST = "first";
String SECOND = "second";
}
动态数据源
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* 动态数据源
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public DynamicDataSource(DataSource defaultTargetDataSource, Map<String, DataSource> targetDataSources) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(new HashMap<>(targetDataSources));
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey() {
return getDataSource();
}
public static String getDataSource() {
return contextHolder.get();
}
public static void setDataSource(String dataSource) {
contextHolder.set(dataSource);
}
public static void clearDataSource() {
contextHolder.remove();
}
}
配置多数据源
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.zdyl.dynamicdatasourcedemo.dynamicdatasource.DataSourceNames;
import com.zdyl.dynamicdatasourcedemo.dynamicdatasource.DynamicDataSource;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* 多数据源配置
*/
@Configuration
@MapperScan("com.zdyl.dynamicdatasourcedemo.**.mapper*")
public class MybatisPluConfig {
/**
* 数据源配置
* @return
*/
@Bean
@ConfigurationProperties(prefix="spring.datasource.druid.first")
public DataSource firstDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix="spring.datasource.druid.second")
public DataSource secondDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@Primary
public DynamicDataSource dataSource(DataSource firstDataSource, DataSource secondDataSource){
Map<String, DataSource> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceNames.FIRST, firstDataSource);
targetDataSources.put(DataSourceNames.SECOND, secondDataSource);
return new DynamicDataSource(firstDataSource, targetDataSources);
}
/**
* mybatis-plus分页插件<br>
* 文档:http://mp.baomidou.com<br>
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
下面就是自定义注解
import java.lang.annotation.*;
/**
* 多数据源注解
* AOP拦截此注解更换数据源
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CurDataSource {
String name() default "";
}
AOP
import com.zdyl.dynamicdatasourcedemo.dynamicdatasource.DataSourceNames;
import com.zdyl.dynamicdatasourcedemo.dynamicdatasource.DynamicDataSource;
import com.zdyl.dynamicdatasourcedemo.dynamicdatasource.annotation.CurDataSource;
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.Ordered;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* 多数据源,切面处理类
* AOP拦截多数据源注解 @CurDataSource 注解更换数据源
*/
@Slf4j
@Aspect
@Component
public class DataSourceAspect implements Ordered {
/**
* 切点
*/
@Pointcut("@annotation(com.zdyl.dynamicdatasourcedemo.dynamicdatasource.annotation.CurDataSource)")
public void dataSourcePointCut() {
}
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
CurDataSource curDataSource = method.getAnnotation(CurDataSource.class);
if (curDataSource == null) {
DynamicDataSource.setDataSource(DataSourceNames.FIRST);
log.info("set datasource is " + DataSourceNames.FIRST);
} else {
DynamicDataSource.setDataSource(curDataSource.name());
log.info("set datasource is " + curDataSource.name());
}
try {
return point.proceed();
} finally {
DynamicDataSource.clearDataSource();
log.info("clean datasource");
}
}
@Override
public int getOrder() {
return 1;
}
}
最后主启动了去掉数据源自动加载
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
最后我们来跑起来请求一下,测试一下是否正确
@RestController
public class CfgDeviceController {
@Resource
CfgDeviceService cfgDeviceService;
@Resource
CfgChargeStartInfoService cfgChargeStartInfoService;
@CurDataSource(name = DataSourceNames.FIRST)
@GetMapping("/test")
public void getUser() {
CfgDevice byId = cfgDeviceService.getById(19);
System.out.println(byId.toString());
}
@CurDataSource(name = DataSourceNames.SECOND)
@GetMapping("/test1")
public void getUser1() {
CfgChargeStartInfo byId = cfgChargeStartInfoService.getById(1);
System.out.println(byId.toString());
}
}
**如果不加注解,使用默认数据源
至此就整合完了
来源:https://www.cnblogs.com/wiliamzhao/p/13231663.html


猜你喜欢
- 本文实例为大家分享了android实现录屏功能的具体代码,供大家参考,具体内容如下1、mian.activitypackage com.fp
- 直接上代码:@Testpublic void testUnicode() { String a = "Hello&qu
- 前言MVC模式是目前主流项目的标准开发模式,这种模式下框架的分层结构清晰,主要分为Controller,Service,Dao。分层的结构下
- 写在前面为什么会突然想说说委托?原因吗,起于一个同事的想法,昨天下班的路上一直在想这个问题,如果给委托注册多个方法,会不会都执行呢?为了一探
- 首先说的是LinearLayout布局下的居中一般是这样的:(注意:android:layout_width="fill_pare
- 前言app启动后的白屏问题,默认都是在splash页面加主题配置,主题配置一个背景来达到用户点击app图标就立马启动app的假象,大多情况下
- 1. 文件上传a. 看看@FIEL注解的属性/** * 上传文件时使用该注解 设置文件相关参数 */@Retention(Retention
- 学C#的原因其实挺简单的,因为一直对游戏挺感兴趣,查了下比较流行的游戏引擎Unity的主要开发语言是C#,所以就决定从C#入手,学学面向对象
- 全局变量顾名思义就是在整个的类中或者可在多个函数中调用的变量。也称为外部变量。局部变量则是特定过程或函数中可以访问的变量。声明一个变量是很
- Maven打包没有指定主类在使用IDEA开发项目的时候经常会遇到使用Maven打包项目(打成jar包或者zip包),但是之前不太清楚打包插件
- 本文实例讲述了Android中Java根据文件头获取文件类型的方法。分享给大家供大家参考,具体如下:前面讲过Android系统内部的Medi
- 代码public class LamdaDemo{ public static void main( String[] args
- 我们绝大部分人估计都还在用着jdk8,12其实是一个非LTS(long time support)版本,而11与8一样是LTS版,意味着下个
- 这个比较简单,但是刚用as不久的朋友可能不知道。这里也不啰嗦了,给两张图就全懂了按ctrl+alt+s打开设置面板选择 Version &n
- 场景在任何一个Form表单的操作页面或者数据台账的查询页面,基本都会看到一个清除的按钮,其功能就是用来清除我们需要抛弃的已经写入到控件内的数
- 本文实例讲述了C#实现终止正在执行的线程的实现方法,并针对一些容易出错的地方进行了深入分析,具体方法如下:一般来说,很多人都会使用Abort
- 只是为了研究下idea这款编译器怎么使用。开门见山,说下如何配置这款编译器,不配置也能用,但是强迫症表示不服。下面直入正题:下载与安装就不说
- 前言说到对集合去重处理,第一时间想到的肯定是Linq的Distinct扩展方式,对于一般的值类型集合去重,很好处理,直接list.Disti
- 写在前面: 线程堆栈应该是多线程类应用程序非功能问题定位的最有效手段,可以说是杀手锏。线程堆栈最擅长与分析如下类型问题:系统无缘无故CPU过
- 本文主要给大家介绍了关于RxJava的一些特殊用法,分享出来供大家参考学习,需要的朋友们下面来一起看看吧。一、按钮绑定通过 RxView 可