SpringBoot采用Dynamic-Datasource方式实现多JDBC数据源

news2024/9/28 13:23:37

目录

  • 1. Dynamic-Datasource实现多JDBC数据源配置
    • 1.1 特性
    • 1.2 Mysql数据准备
    • 2.2 通过Dynamic-Datasource实现多JDBC数据源
      • 2.2.1 pom.xml依赖
    • 2.2.2 application.properties配置
      • 2.2.3 使用@DS注解选择DataSource
      • 2.2.4 使用@Transactional + @DSTransactional实现事务
    • 2.3 动态数据源测试
      • 2.3.1 创建User类
      • 2.3.2 Mapper接口实现
      • 2.3.3 Service实现
      • 2.3.4 测试
    • 2.4 removeDataSource删除DataSource

1. Dynamic-Datasource实现多JDBC数据源配置

1.1 特性

  • 提供本地多数据源事务方案。由AOP实现
  • 支持数据源分组,适用于多种场景,如多库读写分离、一主多从(实现了数据库负载均衡算法)、混合模式
  • 提供自定义数据源来源方案(如全从数据库加载)
  • 提供项目启动后动态增加移除数据源方案
  • 支持数据库敏感配置信息加密(可自定义)ENC()
  • 支持每个数据库独立初始化表结构schema和数据库database
  • 支持无数据源启动,支持懒加载数据源(需要的时候再创建连接)
  • 支持自定义注解,需继承DS(3.2.0+)
  • 提供并简化对Druid、HikariCp、BeeCp、Dbcp2的快速集成
  • 提供对Mybatis-Plus、Quartz、ShardingJdbc、P6sy、Jndi等组件的集成方案
  • 提供Mybatis环境下的纯读写分离方案
  • 提供使用spel动态参数解析数据源方案。内置spel、session、header,支持自定义
  • 支持多层数据源嵌套切换。(ServiceA >>> ServiceB >>> ServiceC)
  • 支持Seata分布式事务

1.2 Mysql数据准备

分别创建read_db.user和write_db.user,并向read_db.user写入数据

mysql> create database read_db;
Query OK, 1 row affected (0.14 sec)

mysql> create database write_db;
Query OK, 1 row affected (0.01 sec)

mysql> create table read_db.user (
    -> id bigint(20) auto_increment not null comment '主键ID',
    -> name varchar(30) null default null comment '姓名',
    -> primary key (id)
    -> );
Query OK, 0 rows affected, 1 warning (0.29 sec)

mysql> 
mysql> insert into read_db.user (id, name) values
    -> (1, 'read_name1'),
    -> (2, 'read_name2'),
    -> (3, 'read_name3'),
    -> (4, 'read_name4'),
    -> (5, 'read_name5');
Query OK, 5 rows affected (0.16 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> 
mysql> create table write_db.user (
    -> id bigint(20) auto_increment not null comment '主键ID',
    -> name varchar(30) null default null comment '姓名',
    -> primary key (id)
    -> );
Query OK, 0 rows affected, 1 warning (0.04 sec)

mysql> 

2.2 通过Dynamic-Datasource实现多JDBC数据源

2.2.1 pom.xml依赖

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.31</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.15</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>

可以看到自动添加了spring-boot-starter-jdbc、spring-boot-starter-aop依赖
dynamic-datasource-spring-boot-starter

2.2.2 application.properties配置

通过dynamic方式,指定了datasource1和datasource2两个DataSource

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
# 设置默认的数据源。默认是master
spring.datasource.dynamic.primary=datasource1
# 严格匹配数据源, 默认false。true表示未匹配到指定数据源时抛异常, false表示使用默认数据源
spring.datasource.dynamic.strict=true

# 用于读的数据库
spring.datasource.dynamic.datasource.datasource1.url=jdbc:mysql://192.168.28.12:3306/read_db
spring.datasource.dynamic.datasource.datasource1.username=root
spring.datasource.dynamic.datasource.datasource1.password=Root_123
spring.datasource.dynamic.datasource.datasource1.driver-class-name=com.mysql.cj.jdbc.Driver

# 用于写的数据库
spring.datasource.dynamic.datasource.datasource2.url=jdbc:mysql://192.168.28.12:3306/write_db
spring.datasource.dynamic.datasource.datasource2.username=root
spring.datasource.dynamic.datasource.datasource2.password=Root_123
spring.datasource.dynamic.datasource.datasource2.driver-class-name=com.mysql.cj.jdbc.Driver

2.2.3 使用@DS注解选择DataSource

可以在Service的类或方法上(优先级更高)使用@DS注解选择DataSource

2.2.4 使用@Transactional + @DSTransactional实现事务

可以在Service的类或方法上(优先级更高)使用@Transactional实现事务。对异常的捕获后,通过TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()进行手动回滚

@Transactional注解会让@DS的多数据源切换失效,虽然可以通过@Transactional(propagation = Propagation.REQUIRES_NEW)开启一个内部子事务来使@DS切换数据源,但是大部分场景不适用

@DSTransactional能和@DS多数据源切换配合使用,@DSTransactional注解最好使用在方法上(类似好像不生效),@DSTransactional注解不能对异常进行捕获手动回滚,只能自动回滚然后抛出异常

2.3 动态数据源测试

2.3.1 创建User类

package com.hh.springboottest.myController;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@NoArgsConstructor
@AllArgsConstructor
@Data
@ToString
public class User {

    private Long id;
    private String name;

}

2.3.2 Mapper接口实现

package com.hh.springboottest.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hh.springboottest.myController.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {

}

2.3.3 Service实现

Service接口实现

package com.hh.springboottest.service;

import com.hh.springboottest.myController.User;

public interface UserService {

    public User getUser(Long id);

    public void saveMultiUser();
}

ServiceImpl实现类。说明如下:

  • @DS(“datasource1”)注解,让userMapper.selectById使用了datasource1数据源
  • @DS(“datasource2”)注解,让userMapper.insert使用了datasource2数据源
  • @Transactional注解,当userMapper.insert(user1)执行成功,userMapper.insert(user2)执行失败,会回滚userMapper.insert(user1)的操作
package com.hh.springboottest.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.hh.springboottest.mapper.UserMapper;
import com.hh.springboottest.myController.User;
import com.hh.springboottest.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;

    @DS("datasource1")
    @Override
    public User getUser(Long id) {
        return userMapper.selectById(id);
    }

    @Transactional
    @DS("datasource2")
    @Override
    public void saveMultiUser() {
        User user2 = new User(2L, "write_name2");
        userMapper.insert(user2);


        User user3 = new User(3L, "write_name3");
        userMapper.insert(user3);
    }

}

2.3.4 测试

package com.hh.springboottest;

import com.hh.springboottest.myController.User;
import com.hh.springboottest.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@Slf4j
@SpringBootTest
public class MyApplicationTest {

    @Autowired
    UserService userService;

    @Test
    public void dynamicDataSourceTest() {

        User user = userService.getUser(1L);
        log.info("获取到的用户为:{}", user);

        userService.saveMultiUser();
    }

}

运行程序,结果如下:

2022-11-24 09:16:15.733  INFO 33760 --- [           main] com.hh.springboottest.MyApplicationTest  : 获取到的用户为:User(id=1, name=read_name1)
2022-11-24 09:16:15.857  INFO 33760 --- [ionShutdownHook] c.b.d.d.DynamicRoutingDataSource         : dynamic-datasource start closing ....
2022-11-24 09:16:15.861  INFO 33760 --- [ionShutdownHook] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} closing ...
2022-11-24 09:16:15.866  INFO 33760 --- [ionShutdownHook] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} closed
2022-11-24 09:16:15.866  INFO 33760 --- [ionShutdownHook] com.alibaba.druid.pool.DruidDataSource   : {dataSource-2} closing ...
2022-11-24 09:16:15.867  INFO 33760 --- [ionShutdownHook] com.alibaba.druid.pool.DruidDataSource   : {dataSource-2} closed
2022-11-24 09:16:15.867  INFO 33760 --- [ionShutdownHook] c.b.d.d.DynamicRoutingDataSource         : dynamic-datasource all closed success,bye

同时查看write_db.user表,数据如下:

mysql> select * from write_db.user;
+----+--------------+
| id | name         |
+----+--------------+
|  1 | write_name1  |
|  2 | write_name2  |
|  3 | write_name3  |
+----+--------------+
3 rows in set (0.11 sec)

mysql> 

2.4 removeDataSource删除DataSource

通过DynamicRoutingDataSource的removeDataSource方法删除DataSource

package com.hh.springboottest.service.impl;

import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;

@Service
public class DataSourceServiceImpl implements DataSourceService {

    @Autowired
    DataSource dataSource;

    public void removeDataSource(String dataSourceName) {
        DynamicRoutingDataSource dynamicRoutingDataSource =
                (DynamicRoutingDataSource) dataSource;
        dynamicRoutingDataSource.removeDataSource(dataSourceName);
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1078169.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

下一代架构设计:云原生、容器和微前端的综合应用

文章目录 云原生&#xff1a;构建可弹性扩展的应用1. 微服务架构2. 容器化3. 自动化和自动扩展 容器化和云原生的结合1. 一致性和可移植性2. 弹性和可伸缩性3. 快速部署和更新4. 资源利用率 微前端&#xff1a;前端架构的演进1. 微前端应用2. 统一的外壳应用3. 独立部署 云原生…

TikTok在跨境电商中的作用:挖掘潜在客户的最佳途径

​随着全球数字化浪潮的不断发展&#xff0c;跨境电商行业也经历了巨大的变革。传统的市场营销渠道已经不再足够&#xff0c;企业们需要不断探寻新的方法来吸引潜在客户。在这个过程中&#xff0c;社交媒体平台TikTok逐渐崭露头角&#xff0c;成为了吸引潜在客户的一个选择。本…

[PwnThyBytes 2019]Baby_SQL - 代码审计+布尔盲注+SESSION_UPLOAD_PROGRESS利用

[PwnThyBytes 2019]Baby_SQL 1 解题流程1.1 分析1.2 解题 2 思考总结 1 解题流程 1.1 分析 此题参考文章&#xff1a;浅谈 SESSION_UPLOAD_PROGRESS 的利用 访问正常来讲用ctf-wscan是能扫出source.zip文件的&#xff0c;且F12后提示了有source.zip&#xff0c;那我们就下载…

Apache POI使用

1.导入坐标 <!-- poi --><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>${poi}</version></dependency><dependency><groupId>org.apache.poi</groupId><a…

elasticSearch7.9数据占用磁盘存储空间情况

最近&#xff0c;在VMware Workstation虚拟机上安装了es7.9&#xff0c;单节点的es&#xff0c;不是集群&#xff0c;然后建了一个索引&#xff08;包含3个分片和一个副本&#xff09;&#xff0c;插入了500万条数据&#xff0c;占据磁盘空间17G。如下图&#xff1a; 索引的字…

什么样的人适合下班后做点兼职副业

我们身边不乏一些讨论兼职副业的人&#xff0c;可是很多人都只停留在“想”的层面上&#xff0c;真正有执行力的人早就偷偷做起了副业&#xff0c;能力强的还做得风生水起。 什么样的人适合下班后做点副业呢&#xff1f;我觉得下班后&#xff0c;时间很宽裕&#xff0c;或者经济…

S7-1200PLC与昆仑通态触摸屏通讯

测试环境&#xff1a;Win10、MCGS、博图V16、1214DCDCDC 博途工控人平时在哪里技术交流博途工控人社群 博途工控人平时在哪里技术交流博途工控人社群 将PLC端做如下配置 1-MCGS配置S7-1200驱动 1.1-添加驱动 双击设备窗口 点击设备组态窗口下的设备管理&#xff0c;选择西门…

串级/级联控制知识点整理

串级控制系统是改善控制质量的有效方法之一&#xff0c;在过程控制中得到了广泛的应用。所谓串级控制&#xff0c;就是采用两个控制器串联工作&#xff0c;外环控制器的输出作为内环控制器的设定值&#xff0c;由内环控制器的输出去操纵控制阀&#xff0c;从而对外环被控量具有…

【力扣LCP】速算机器人

&#x1f451;专栏内容&#xff1a;力扣刷题⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录 一、题目描述二、题目分析1、常规解法2、取巧解法 一、题目描述 题目链接&#xff1a;力扣LCP.14 速算机器人 小扣在秋日…

app如何新增广告位以提升广告变现收益?

app广告位资源是平台变现能力之一&#xff0c;广告位资源包括开屏广告、首页轮播广告、首页弹窗等大家熟知的广告位&#xff0c;流量主为了获得更高的收益&#xff0c;通常会考虑在应用中增加广告位。 增设新的广告位&#xff0c;流量主应该从以下几方面考虑。 1、广告类型 …

overflow真实使用场景-表格最右侧显示空白

问题 先看问题。下方滚动条滚动到右侧之后上下都有空白&#xff0c;但是缩放之后正常。分析之后是overflow的问题。 overflow作用是什么&#xff1f; overflow在内容大于元素框高度或者宽度时候设置&#xff0c;保证内容显示正常。 单独一个内容大于元素框高度或者宽度比较…

手机端下载文件时显示0B问题

文章目录 下载文件时显示文件大小如果是OutputStream输出流&#xff0c;如何设置大小扩展问题pdfjs预览pdf文件时遇到的问题 下载文件时显示文件大小 设置下载文件的大小 File filenew File("D:/test.txt");response.setHeader("Accept-Ranges","byt…

Axios 封装

请注意以下文件夹: utils下的setToken.js 是token封装(封装 Token-CSDN博客),service.js 是axios封装。 Axios封装: 1.安装axios 在项目终端下 输入: npm install axios --save 2.在main.js全局引入axios import axios from axiosVue.prototype.$axios =axios //挂…

python psutil库之——获取网络信息(网络接口信息、网络配置信息、以太网接口、ip信息、ip地址信息)

文章目录 使用Python psutil库获取网络信息安装psutil库获取网络连接信息查看所有网络连接过滤特定状态的连接 获取网络接口信息获取网络IO统计信息实例1实例2 总结 使用Python psutil库获取网络信息 Python的psutil库是一个跨平台库&#xff0c;能够方便地获取系统使用情况和…

C200/10/1/1/1/00 VPM04D300000 VDM01U30AL00

C200/10/1/1/1/00 VPM04D300000 VDM01U30AL00 受其客户对集成、远程和日益自主的运营的关注&#xff0c;横河于2022年6月6日推出了OpreX Asset Health Insights&#xff0c;以使资产数据更加可见、集成和可操作。 Asset Health Insights的原始版本支持Amazon Web Services和…

县域农牧业信息化项目建设技术解决方案(完整版本)

扫码关注下方公众号&#xff0c;免费获取项目建设方案&#xff01;↓↓↓ 文章目录 一、项目背景二、项目建设意义及目标三、建设内容&#xff08;1&#xff09;XX智慧养殖管理 SaaS 云平台&#xff08;2&#xff09;XX智慧养殖管理平台&#xff08;3&#xff09;XX投入品质量…

一款基于javafx的自有IP地址查询工具

SelfIPAdressQuery 使用场景 在攻防演习中、hvv防守方人员会封禁大量IP地址,人员疏忽会导致自有地址被封禁,可以使用此工具进行批量筛选自有地址. IP地址归属批量查询. 功能介绍 通过IP地址查询数据库中符合的自有IP地址或自有IP段,不存在的IP地址通过离线模式或联网模式查…

进阶JAVA篇-深入了解枚举与抽象枚举

目录 介绍一下枚举&#xff1a; 1.1枚举的概念 1.2具体如何来使用呢&#xff1f; 1.3对枚举小结 1.4抽象枚举概念 1.5对抽象枚举小结 介绍一下枚举&#xff1a; 1.1枚举的概念 在JAVA中&#xff0c;枚举是一种特殊的类&#xff0c;用于定义一组常量。Java中的枚举类型是通过使用…

spring IOC AOP核心思想

我的理解&#xff1a;一开始各个对象之间相互合作&#xff0c;是多个对象对应多个对象去使用&#xff0c;如果有一个对象出现问题就可能影响到全局&#xff0c;但是使用ioc就是在两者之间加入了一个中间媒介(spring bean也就是通过xml配置文件装配对象)&#xff0c;如果相互需要…

pytorch中nn.DataParallel多次使用

pytorch中nn.DataParallel多次使用 import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader# 定义模型 class MyModel(nn.Module):def __init__(self):super(MyModel, self).__init__()self.fc nn.Linear(10, 1)def forwa…