Spring Boot实现多数据源快速入门

news2024/11/16 7:55:05

1.为什么需要多数据源?

多数据源既动态数据源,项目开发逐渐扩大,单个数据源、单一数据源已经无法满足需求项目的支撑需求。本文采用dynamic-datasource-spring-boot-starter实现多数据源,

主要特性

  • 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。
  • 支持数据库敏感配置信息 加密 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的分布式事务方案。
  • 提供 本地多数据源事务方案

2.环境搭建

本文使用2个mysql作为数据源,表都是一样的结构

第一个数据库

docker run --name docker-mysql -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 -d mysql

第二个数据库

docker run --name docker-mysql-2 -e MYSQL_ROOT_PASSWORD=123456 -p 3307:3306 -d mysql

初始化数据

create database demo;
create table user_info
(
user_id     varchar(64)          not null primary key,
username    varchar(100)         null ,
age         int(3)               null ,
gender      tinyint(1)           null ,
remark      varchar(255)         null ,
create_time datetime             null ,
create_id   varchar(64)          null ,
update_time datetime             null ,
update_id   varchar(64)          null ,
enabled     tinyint(1) default 1 null
);

 备注说明

msyql account:root
mysql password:123456

3.代码工程

实验目标:实现数据源的切换

 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springboot-demo</artifactId>
        <groupId>com.et</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>dynamic-datasource</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- dynamic-datasource -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>
        <!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1</version>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.3.1</version>
        </dependency>

        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.31</version>
        </dependency>

    </dependencies>
</project>

代码生成crud操作

package com.et.dynamic.datasource;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;


public class GeneratorCode {
    /**
     * database connect
     * */
    private static final String dbUrl = "jdbc:mysql://localhost:3306/demo?useUnicode=true&useSSL=false&characterEncoding=utf8";
    /**
     * username
     * */
    private static final String username = "root";
    /**
     * pasword
     * */
    private static final String password = "123456";
    /**
     * moduleName
     * */
    private static final String moduleName = "/dynamic-datasource";

    /**
     * <p>
     * read console content
     * @param
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("please input:" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotBlank(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("please right conntent:" + tip + "!");
    }

    public static void main(String[] args) {
        // Code Generateor
        AutoGenerator mpg = new AutoGenerator();
        String module = scanner(" module");
        // GlobalCOnfig
        GlobalConfig gc = new GlobalConfig();
        //D:\IdeaProjects\ETFramework
        String basedir ="D:/IdeaProjects/ETFramework/";
        String projectPath = basedir+moduleName;
        System.out.println(projectPath);
        //OutputDir
        gc.setOutputDir(projectPath+"/src/main/java");
        gc.setAuthor("stopping");
        //some generate rule
        gc.setMapperName("%sMapper");
        gc.setServiceName("%sService");
        gc.setServiceImplName("%sServiceImp");
        gc.setControllerName("%sController");
        gc.setXmlName("%sMapper");
        gc.setIdType(IdType.AUTO);
        gc.setOpen(false);
        //IsOverride
        gc.setFileOverride(true);
        //isSwagger2
        gc.setSwagger2(false);
        mpg.setGlobalConfig(gc);

        //datasource
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl(dbUrl);
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername(username);
        dsc.setPassword(password);
        mpg.setDataSource(dsc);

        // PackageConfig
        PackageConfig pc = new PackageConfig();
        //package path
        pc.setParent("com.et.dynamic.datasource");
        //subpackage path
        pc.setMapper("mapper."+module);
        pc.setController("controller."+module);
        pc.setService("service."+module);
        pc.setServiceImpl("service."+module+".imp");
        pc.setEntity("model.entity");
        pc.setXml("Mapper");
        mpg.setPackageInfo(pc);

        //custom config
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };

        //  freemarker
        String templatePath = "/templates/mapper.xml.ftl";

        // FileOutConfig
        List<FileOutConfig> focList = new ArrayList<>();
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // Mapper
                String xmlUrl = projectPath + "/src/main/resources/mapper/" + module
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
                System.out.println("xml path:"+xmlUrl);
                return xmlUrl;
            }
        });

        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // templateConfig
        TemplateConfig templateConfig = new TemplateConfig();
        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // StrategyConfig
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        // common file
        //strategy.setSuperEntityColumns("id");
        strategy.setInclude(scanner("tablename,multi can be seperated ,").split(","));
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(pc.getModuleName() + "_");
        //isAnnotationEnable
        strategy.setEntityTableFieldAnnotationEnable(true);
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }
}

生成代码如下

1

然后修改service,增加切换数据源注解@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。 

注解结果
没有@DS默认数据源
@DS("dsName")dsName可以为组名也可以为具体某个库的名称
package com.et.dynamic.datasource.service.userinfo.imp;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.et.dynamic.datasource.model.entity.UserInfo;
import com.et.dynamic.datasource.mapper.userinfo.UserInfoMapper;
import com.et.dynamic.datasource.service.userinfo.UserInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author stopping
 * @since 2024-05-13
 */
@Service
@DS("slave_1")
public class UserInfoServiceImp extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
    @Resource
    UserInfoMapper userInfoMapper;
    @Override
    @DS("master")
    public List<UserInfo> testQueryWrapper(int age) {
        QueryWrapper<UserInfo> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.ge("age", age);
        List<UserInfo> userList = userInfoMapper.selectList(userQueryWrapper);
        return userList;
    }
}

application.yaml

配置下格式支持这几种

# 多主多从                      纯粹多库(记得设置primary)                   混合配置
spring:                               spring:                               spring:
  datasource:                           datasource:                           datasource:
    dynamic:                              dynamic:                              dynamic:
      datasource:                           datasource:                           datasource:
        master_1:                             mysql:                                master:
        master_2:                             oracle:                               slave_1:
        slave_1:                              sqlserver:                            slave_2:
        slave_2:                              postgresql:                           oracle_1:
        slave_3:                              h2:                                   oracle_2:

本文配置如下

   

server:
  port: 8088
spring:
  datasource:
    dynamic:
      primary: master
      strict: false
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/demo?serverTimezone=Asia/Shanghai
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave_1:
          url: jdbc:mysql://localhost:3307/demo?serverTimezone=Asia/Shanghai
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver

4.测试

测试插入

@Test
public void insert()  {
    log.info("your method test Code");

    for(int i =1;i<10;i++) {
        UserInfo ui =  new UserInfo();
        ui.setUserId(i+"id");
        userInfoService.removeById(i+"id");
        ui.setUsername("HBLOG"+i);
        ui.setAge(i);
        userInfoService.save(ui);
    }

由于类上@DS("slave_1"),所以预期插入的slave_1mysql库。

测试查询

@Test
public void testQueryWrapper()  {
    log.info("your method test Code");
    userInfoService.testQueryWrapper(3).forEach(System.out::println);
}

查询方法注解的@DS("master"),所以在mater库上根本查不到数据

5.参考引用

  • 多数据源 | MyBatis-Plus
  • Spring Boot实现多数据源快速入门 | Harries Blog™

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

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

相关文章

Ansible自动化运维中的User用户管理模块应用详解

作者主页&#xff1a;点击&#xff01; Ansible专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年5月14日14点12分 在Ansible中&#xff0c;user 模块主要用于管理系统用户账户。它可以创建、修改、删除用户&#xff0c;并管理用户的属性&#xff0c;比如密码、…

RK3576 Camera:资源介绍

RK3576是RK今年上市的中高端旗舰芯片&#xff0c;定位弱于RK3588。这篇文章主要分享一下RK3576这颗主控芯片的camera资源。 &#xff08;1&#xff09;RK3576 camera资源 ①RK3576 camera硬件框图 RK3576的camera硬件框图如图所示&#xff0c;拥有一路4lane的DCPHY&#xff…

中关村论坛 | 区块链与隐私计算论坛倒计时1天!

「区块链与隐私计算论坛」 倒计时1天&#xff01; 地址&#xff1a;中关村国家自主创新示范区会议中心&#xff08;新建宫门路2号&#xff09;万春厅 时间&#xff1a;2024年4月27日&#xff0c;下午14:30-17:00 本次论坛围绕释放数据要素价值深入探讨如何将区块链与隐私计算…

六西格玛培训证书攻略2024:一站式解决方案助你快速上手

目前&#xff0c;企业对于员工的专业能力和综合素质要求越来越高。六西格玛作为一种先进的质量管理方法&#xff0c;已经成为众多企业提升运营效率、降低成本的重要手段。张驰咨询针对2024年六西格玛培训证书考取&#xff0c;为广大学员制定了实用的攻略&#xff0c;帮助学员们…

【SRC实战】文件名回显导致反射型XSS,URL重定向

挖个洞先 https://mp.weixin.qq.com/s/hnrm-snkETuR-gqPOSnQXQ “ 以下漏洞均为实验靶场&#xff0c;如有雷同&#xff0c;纯属巧合 ” 01 — 漏洞证明 一、反射型XSS “ 文件名回显&#xff0c;能否触发XSS&#xff1f;” 1、灯塔扫到敏感文件&#xff0c;发现1.txt会在…

使用apifox连接WebSocket请求,重发消息

背景&#xff1a;在 在线文档中&#xff0c;通常使用的都是WebSocket请求&#xff0c;就会导致F12抓包不是通常的http/https 在浏览器中查看WebSocket的方法 打开F12&#xff0c;切换到网络&#xff0c;选择过滤WebSocket请求&#xff0c;刷新页面 连接WebSocket请求的方法 …

python 脚本压缩文件linux 正常,windows 文件夹/文件名称 被加上了上级文件夹名

场景&#xff1a; php 在调用python 脚本&#xff0c;进行文件压缩&#xff08;因为php的压缩大文件总是超时&#xff09;&#xff0c;linux/mac 环境文件/文件夹名压缩前后一致&#xff0c;windows 压缩后 文件/文件夹名被改变为 上级 文件夹原名 原因&#xff1a; window…

国标GB28181协议EasyCVR视频汇聚平台获取设备录像仅展示部分片段的原因排查

国标GB28181协议EasyCVR安防平台可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联、磁盘阵列存储、视频集中存储、云存储等丰富的视频能力&#xff0c;平台支持7*24小时实时高清视频监控&#xff0c;能同时播放多路监控视频流&#xf…

umi项目运行时配置,app.ts

运行时配置和配置的区别是他跑在浏览器端&#xff0c;基于此&#xff0c;我们可以在这里写函数、tsx、import 浏览器端依赖等等&#xff0c;注意不要引入 node 依赖。 getInitialState 用于获取初始化数据&#xff0c;初始化数据使用 useModel 在各个组件中使用&#xff0c;在…

信创替代后的设备处置

信创替代后的设备处置 在信创项目中替换下来的设备&#xff0c;如果从技术层面讲还具有较高的应用价值&#xff0c;如何处置呢&#xff1f; 一、数据处置 信创适配完成后&#xff0c;这些被替换下来的服务器上有大量的数据&#xff08;包括结构化和非结构化&#xff09;&…

打造动态图文展示,电子翻书一体机成展厅新宠!

随着多媒体技术的飞速演进&#xff0c;传统的静态展示方式已逐渐无法满足现代社会对于创新的渴望&#xff0c;为了跟上这一步伐&#xff0c;展厅中的展览形式也在不断地迭代&#xff0c;其中&#xff0c;电子翻书一体机以其独特的魅力&#xff0c;成为了备受瞩目的新型展项&…

JAVA实验项目(一):JAVA面向对象特征性实验

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

2024CCPC郑州邀请赛暨河南省赛

比赛记录&#xff1a;看群里大家嘎嘎拿牌&#xff0c;自己个人来solo了一下&#xff0c;发现简单到中等题很多&#xff0c;写了两小时出了7题&#xff0c;但是写的比较慢&#xff0c;对难题把握还是不准确 补题 &#xff1a; A题确实巧妙充分利用题目的数据范围来思考问题&…

Python邮件处理库之flanker使用详解

概要 Flanker是一个开源的邮件处理库,专门设计用于解析、验证和构建电子邮件地址和MIME消息。由Mailgun开发,它旨在提高邮件处理的效率和准确性,尤其适用于需要高效邮件验证和解析的应用程序。 安装 安装Flanker非常简单,可以通过Python的包管理器pip进行安装: pip ins…

GRE over IPsec VPN实验

一、拓扑图 二、组网需求 某企业总部、分支1、分支2分别通过 R1&#xff0c;R3&#xff0c;R4 接入互联网&#xff0c;配置默认路由连通公网按照图示配置 IP 地址&#xff0c;R1&#xff0c;R3&#xff0c;R4 分别配置 Loopback0 口匹配感兴趣流&#xff0c;Loopback1 口模拟业…

微软推出的Microsoft Fabric 到底是什么?

近期&#xff0c;总有客户问小编&#xff0c;微软推出的 Microsoft Fabric 是什么&#xff1f;这个产品有什么特别之处呢&#xff1f;希望下面这篇文章能为大家解开一些疑惑。 微软Fabric是2023年5月推出的一个数据分析平台&#xff0c;它将关键数据管理和分析工作负载整合到一…

Docker 使用 CentOS 镜像

使用 docker run 直接运行 CentOS 7 镜像&#xff0c;并登录 bash。 C:\Users\yhu>docker run -it centos:centos7 bash Unable to find image centos:centos7 locally centos7: Pulling from library/centos 2d473b07cdd5: Pull complete Digest: sha256:be65f488b7764ad36…

力扣HOT100 - 139. 单词拆分

解题思路&#xff1a; 动态规划 class Solution {public boolean wordBreak(String s, List<String> wordDict) {Set<String> wordDictSet new HashSet(wordDict);boolean[] dp new boolean[s.length() 1];dp[0] true;for (int i 1; i < s.length(); i) {…

数图智能营运管理系统助力企业数字化转型升级

数图智能营运管理系统不仅仅是一个业绩查看工具&#xff0c;它还具备了主动预警机制以及专家级的品类分析逻辑。系统能够协助企业持续优化库存管理&#xff0c;提升品类结构合理性&#xff0c;显著提高运营效率&#xff0c;减少对员工专业技能的依赖&#xff0c;并缩短处理时间…

数据库管理-第190期 备份堪比生死(20240515)

数据库管理190期 2024-05-15 数据库管理-第190期 备份堪比生死&#xff08;20240515&#xff09;1 DDL误操作2 强大的RMAN3 ZDLRA总结 数据库管理-第190期 备份堪比生死&#xff08;20240515&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文&#xff09; Oracle A…