springboot,druid动态数据源切换

news2024/9/29 1:24:24

关键字:springboot,druid数据库连接池,两个数据源(可以切换成多个),事务管理

关于druid简介传送门:https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98
在这里插入图片描述

具体分为四步:

1,ThreadLocal维护分支信息,

/**
 * @ClassName DataSourceContextHolder
 * @Description: 设置threadlocal数据元信息
 * @Author gjq
 * @Date 2024/2/29
 * @Version V1.0
 **/
public class DataSourceContextHolder {

    /**使用Threadlocal维护变量,threadlocal为每个使用该变量的现成提供独立的变量副本,
        所以每一个线程可以独立的改变自己的副本,而不会影响其他线程使用对应的副本
     **/
    private static final ThreadLocal<String> HOLDER = new InheritableThreadLocal<>();

    public static void setDataSource(String key) {
        HOLDER.set(key);
    }

    public static String getDataSource() {
        return HOLDER.get();
    }

    public static void clearDataSource() {
        HOLDER.remove();
    }

}

2,创建数据源设置方法,初始化数据源信息

package com.pg.ga.datasource;

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

import javax.sql.DataSource;
import java.util.Map;

/**
 * @ClassName DynamicDataSource
 * @Description:
 * @Author gjq
 * @Date 2024/2/29
 * @Version V1.0
 **/
public class DynamicDataSource extends AbstractRoutingDataSource {

    public DynamicDataSource(DataSource dataSource, Map<Object, Object> targetDataSources) {
        super.setDefaultTargetDataSource(dataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }

}

3,多数据源配置,及枚举值定义

枚举值定义:
package com.pg.ga.datasource;

public enum DataSourceType {
    MASTER,
    SLAVE
}

package com.pg.ga.conf;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.pg.ga.datasource.DataSourceType;
import com.pg.ga.datasource.DynamicDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.*;

/**
 * @ClassName PgConfig
 * @Description:类描述
 * @Author gjq
 * @Date 2024/2/29
 * @Version V1.0
 **/
@Configuration
@MapperScan({"com.pg.ga.dao"})
public class PgConfig {


    @Bean
    @ConfigurationProperties("spring.datasource.druid.master")
    public DruidDataSource masterDataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.druid.slave")
    // @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enable", havingValue = "true")
    public DataSource slaveDataSource() {
        return DruidDataSourceBuilder.create().build();
    }


    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dataSource(DataSource masterDataSource, DataSource slaveDataSource) {
        Map<Object, Object> targetDataSource = new HashMap<>();
        targetDataSource.put(DataSourceType.MASTER.name(), masterDataSource);
        targetDataSource.put(DataSourceType.SLAVE.name(), slaveDataSource);
        return new DynamicDataSource(masterDataSource(), targetDataSource);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception {
        //核心就是这个,替换原始的SqlSessionFactoryBean 用mybatisSqlSessionFactoryBean即可
        MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
        factoryBean.setDataSource(dynamicDataSource);
        //设置mapper.xml位置路径
        factoryBean.setMapperLocations(resolveMapperLocations());
        return factoryBean.getObject();
    }

    public Resource[] resolveMapperLocations() {
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        List<String> mapperLocations = new ArrayList<>();
        mapperLocations.add("classpath*:mapper/*.xml");
        List<Resource> resourceList = new ArrayList<>();
        if (null != mapperLocations) {
            for (String mapperLocation : mapperLocations) {
                try {
                    Resource[] mappers = resourcePatternResolver.getResources(mapperLocation);
                    resourceList.addAll(Arrays.asList(mappers));
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }
        return resourceList.toArray(new Resource[resourceList.size()]);
    }


    /**
     * @MethodName:
     * @Description: 配置@Transactional注解事务
     * @Param:
     * @Return:
     * @Author: gjq
     * @Date: 2024/2/29
     **/
    @Bean
    public PlatformTransactionManager transactionManager(DynamicDataSource dynamicDataSource) {
        return new DataSourceTransactionManager(dynamicDataSource);
    }

}

4,动态切换注解定义

package com.pg.ga.datasource;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {

    /**
     @MethodName:
     @Description: 切换数据源名称
     @Param:
     @Return:
     @Author: gjq
     @Date: 2024/2/29
     **/
    DataSourceType value() default DataSourceType.MASTER;
}

切面实现
package com.pg.ga.datasource;

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;

/**
 * @ClassName DataSourceAspect
 * @Description:类描述
 * @Author gjq
 * @Date 2024/2/29
 * @Version V1.0
 **/
@Aspect
@Component
@Order(1)
public class DataSourceAspect {
    //先声明切点
    @Pointcut("@annotation(com.pg.ga.datasource.DataSource)")
    public void dsPointCut() {
        System.out.println(11111);
    }

    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable{
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        DataSource dataSource = method.getAnnotation(DataSource.class);
        if(null!=dataSource){
            DataSourceContextHolder.setDataSource(dataSource.value().name());
        }
        try{
            return point.proceed();
        }finally {
            DataSourceContextHolder.clearDataSource();
        }
    }
}

数据库配置信息.yaml文件配置

server :
  port : 8090
  ##项目名字配置
  #servlet :
  #  context-path : /demo
  tomcat :
    uri-encoding : UTF-8
    #xx 报错修改的地方
    max-connections: 200000
    max-http-form-post-size: 9000000
    threads:
      max: 128
      min-spare: 5
spring :
  # 环境 dev|test|prod
  profiles :
    active : dev
    #引入其他配置文件,例如ftpHX 未配置文件application-ftpHX.yml
    #include: ftpHX,ftpCloud
  servlet:
    multipart:
      #设置总上传的数据大小
      max-request-size: 100MB
      #单个文件大小
      maxFileSize : 30MB
        #xx 报错修改的地方
    max-connections: 200000
    max-http-post-size: 9000000
  #热部署模块
  devtools:
    restart:
      #热部署开关
      enabled: true
      #指定热部署的目录
      additional-paths: src/main/java
      #指定目录不更新
      exclude: test/**
  mvc:   #静态文件
    static-path-pattern : /static/**
    pathmatch:
      matching-strategy: ant_path_matcher
  #模板引擎
  thymeleaf:
    model: HTML5
    prefix: classpath:/templates/
    suffix: .html
    #指定编码
    encoding: utf-8
    #禁用缓存 默认false
    cache: false
  jackson:
    time-zone: GMT+8
    date-format: yyyy-MM-dd HH:mm:ss
  redis:
    ssl: false
    database: 0
    host: 127.0.0.1
    port: 6379
    password: 
    timeout: 1000
    lettuce:
      pool:
        max-active: 200
        max-wait: -1
        max-idle: 10
        min-idle: 0

# 控制台输出sql、下划线转驼峰
mybatis-plus:
  mapper-locations: classpath:/mapper/*.xml
  typeAliasesPackage: com.pg.ga.model
  # 控制台输出sql、下划线转驼峰
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true

#pagehelper分页插件
pagehelper:
    helperDialect: mysql
    reasonable: true
    supportMethodsArguments: true
    params: count=countSql
application-dev.yaml配置信息
#dev环境  mysql7.0
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: org.postgresql.Driver
    #druid连接池配置
    druid:
     #主库数据源
     master:
        url: jdbc:postgresql://X.X.X.X:30811/ticket
        username: postgres
        password: postgres
     #备数据源 #关闭
     slave:
        enabled: false
        url: jdbc:postgresql://X.X.X.X:30811/ticket
        username: postgres
        password: postgres
        #配置初始化连接数大小
     initial-size: 10
     # 最大连接数
     maxActive: 50
     #最小连接数
     minIdle: 10
     #获取连接等待超时时间
     maxWait: 5000
     poolPreparedStatements: true #是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
     maxPoolPreparedStatementPerConnection-size: 20
     validationQuery: SELECT 1 FROM DUAL
     validationQueryTimeout: 20000
     testOnBorrow: false #申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
     testOnReturn: false #归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
     testWhileIdle: true #建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
     timeBetweenEvictionRunsMillis: 60000 #配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
     minEvictableIdleTimeMillis: 300000  #一个连接在池中最小生存的时间,单位是毫秒
     maxEvictableIdleTimeMillis: 900000  # 配置一个连接在池中最大生存的时间,单位是毫秒
     #StatViewServlet配置。(因为暴露的监控信息比较敏感,支持密码加密和访问ip限定)
     webStatFilter:
      enabled: true
     statViewServlet:
      enabled: true
      urlPattern: /druid/*
      #可以增加访问账号密码【去掉注释就可以】
      #login-username: admin
      #login-password: admin
     filter:
      stat:
        enabled: true
        # 慢SQL记录
        log-slow-sql: true
        slow-sql-millis: 1000
        merge-sql: true
      wall:
        config:
          multi-statement-allow: true
测试controller
package com.pg.ga.controller;

import com.alibaba.fastjson.JSONObject;
import com.pg.ga.model.TicketBall;
import com.pg.ga.service.TickService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/crud")
public class Crud {
    @Autowired
    TickService tickService;

    @GetMapping("/master")
    public String master() {
        TicketBall ticketBall = tickService.selectMaster();
        System.out.println(JSONObject.toJSON(ticketBall));
        return null;
    }

    @GetMapping("/slave")
    public String create() {
        TicketBall ticketBall = tickService.selectSlave();
        System.out.println(JSONObject.toJSON(ticketBall));
        return null;
    }
}

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

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

相关文章

Linux进程详细介绍

文章目录 Linux进程1、计算机体系结构和操作系统管理1.1、计算机体系结构 -- 硬件1.2、操作系统&#xff08;Operator System&#xff09; -- 软件 2、进程2.1、进程基本概念2.2、进程标识符2.2.1、获取当前进程标识符和当前进程的父进程标识符2.2.2、通过系统调用创建进程 -- …

FIFO漫谈

文章目录 目录 概要 整体架构流程 技术名词解释 技术细节 为什么需要FIFO&#xff1f; 小结 概要 FIFO&#xff0c;全称为First-In, First-Out&#xff0c;意为先进先出。它就像是一个排队买东西的队伍&#xff0c;第一个进入队伍的人会第一个离开队伍。在芯片中&#xff0c;F…

概要了解postman、jmeter 、loadRunner

postman还蛮好理解的&#xff0c;后续复习的话着重学习关联接口测试即可&#xff0c;感觉只要用几次就会记住&#xff1a; 1 从接口的响应结果当中提取需要的数据 2 设置成环境变量/全局变量&#xff08;json value check 、set environment para 3写入到下一个接口的请求数据中…

GIS软件应用(一)

任务&#xff1a; 1.加载南京市边界数据、查看投影坐标系并完成投影转换 2.加载科教文卫POI数据、查看投影坐标系并完成投影转换 3.出图要求添加完整出图要素 步骤&#xff1a; 选中shp文件&#xff0c;加载南京市边界数据 在ArcToolbox工具箱中选中Projections and Transf…

idea内置的database和chat2DB如何?

捉妖啦 最近由于某些众所周知的因素&#xff0c;要求卸载navicat,所以寻找替代品是当下任务。如果知识MySQL数据库的话&#xff0c;那替代品可太多了&#xff0c;由于使用的是MongoDB&#xff0c;所以至今没有找到一个称手的工具。 需要一款像Navicat一样&#xff0c;可以直…

MySQL性能优化-范式设计和反范式设计

范式化设计 范式化设计背景 范式是数据表设计的基本原则&#xff0c;又很容易被忽略。很多时候&#xff0c;当数据库运行了一段时间之后&#xff0c;我们才发现数据表设计得有问题。重新调整数据表的结构&#xff0c;就需要做数据迁移&#xff0c;还有可能影响程序的业务逻辑…

【WPS】Excel查重数据对比

数据对比 数据对比标记重复数据查询过滤处理

C++基于多设计模式下的同步异步日志系统day1

C基于多设计模式下的同步&异步日志系统day1 &#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;C基于多设计模式下的同步&异步日志系统 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&am…

Windows安装tdm-gcc(详细图文)

目录 一、tdm-gcc的下载地址 二、安装tdm-gcc 1.双击下载的tdm-gcc 2.点击create 下载 3.下载完成之后&#xff0c;点击第二个&#xff0c;再点击next。 4.更换安装目录 5. 全部勾选&#xff0c;点击install 三、配置系统环境变量 四、验证TDM-GCC 一、tdm-gcc的下载地…

O2O:Offline–Online Actor–Critic

IEEE TAI 2024 paper 1 Introduction 一篇offline to online 的文章&#xff0c;有效解决迁移过程出现的performance drop。所提出的O2AC算法首先在离线阶段添加一项BC惩罚项&#xff0c;用于限制策略靠近专家策略&#xff1b;而在在线微调阶段&#xff0c;通过动态调整BC的权…

鸿蒙全栈开发必学!码牛课堂《HarmonyOS NEXT星河版零基础入门到实战教程》,学到就是赚到!

众所周知&#xff0c;码牛发布的免费教程不仅质量高&#xff0c;而且更新快&#xff0c;帮助无数大学生成功踏入IT行业&#xff0c;被同学们亲切的称为“IT启蒙导师”。 今年被称为鸿蒙元年&#xff0c;各行业急缺鸿蒙相关人才&#xff0c;从招聘情况来看&#xff0c;鸿蒙人才…

低代码工具APEX的入门使用(未包含安装)

第一次使用APEX是2019年&#xff0c;这个技术成名已久只是我了解的比较晚。请看Oracle ACE的网站&#xff0c;这就是用APEX做的。实际上有一次我看O记的人操作他们的办公流程&#xff0c;都是用APEX做的。 那一年&#xff0c;我用APEX做了一个CMDB的管理系统。那时候还没有流行…

微信小程序开发学习笔记《19》uni-app框架-配置小程序分包与轮播图跳转

微信小程序开发学习笔记《19》uni-app框架-配置小程序分包与轮播图跳转 博主正在学习微信小程序开发&#xff0c;希望记录自己学习过程同时与广大网友共同学习讨论。建议仔细阅读uni-app对应官方文档 一、配置小程序分包 分包可以减少小程序首次启动时的加载时间 为此&#…

S/4 HANA CLOUD Workaround 销售含税价

业务场景&#xff1a; 在中国及其他亚洲或者东南亚国家&#xff0c;采购合同和发票都是以含税价的方式计价&#xff0c;这与美国及欧洲国家通过净价加税的计价方式不同。而目前S/4 HANA CLOUD交付的标准定价方式采取的是后者&#xff0c;大部分的中国客户对含税价的功能都有需求…

图书推荐|Word文稿之美

让你的文档从平凡到出众&#xff01; 本书内容 《Word文稿之美》是一本全面介绍Word排版技巧和应用的实用指南。从初步认识数字排版到高效利用模板、图文配置和表格与图表的排版技巧&#xff0c;再到快速修正错误和保护文件&#xff0c;全面系统地讲解数字排版的技术和能力&…

Git 撤销修改

如果我们在我们的工作区写了很长时间代码&#xff0c;发现出现错误&#xff0c;想回退到之前的版本&#xff0c;这时改怎么做呢&#xff1f; 情况一&#xff1a;对于工作区的代码&#xff0c;还没有 add 我们当然也可以使用git diff 查看与上次提交的差异&#xff0c;进行手动删…

【数据结构】用栈实现队列

前言&#xff1a;本节博客分享了用栈实现队列效果的思路以及代码&#xff0c;有需要借鉴即可。 1.题目及链接 LINK 2.思路分析 如果要用栈实现队列&#xff0c;我们直到栈是先入后出的一个效果&#xff0c;所以我们可以用两个栈&#xff0c;这样逆转两次数不就是入栈之前数组…

代码随想录算法训练营第十四天| 144. 二叉树的前序遍历 ,145. 二叉树的后序遍历,94. 二叉树的中序遍历

两种写法&#xff0c;递归和非递归写法 递归&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : va…

如何使用宝塔面板部署MySQL数据库,并结合内网穿透实现固定公网地址远程连接

文章目录 前言1.Mysql服务安装2.创建数据库3.安装cpolar3.1 开放局域网端口3.2 创建HTTP隧道 4.远程连接5.固定TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 宝塔面板的简易操作性,使得运维难度降低,简化了Linux命令行进行繁琐的配置,下面简单几…

VMvare17安装centos8安装宝塔面板 教程

阿里镜像站&#xff1a;https://mirrors.aliyun.com/centos centos-8-isos-x86_64安装包下载_开源镜像站-阿里云 https://mirrors.aliyun.com/centos/8/isos/x86_64/CentOS-8.5.2111-x86_64-dvd1.iso 将上面的链接复制到迅雷进行高速下载 vmvare安装配置教程安装教程 CentOS…