java后端根据双数据源进行不同的接口查询

多数据源适配说明文档

一、背景说明

因业务拓展需要对接外部推送的业务数据,现有系统核心本地基础库采用 PostgreSQL 数据库(存储基础配置、核心元数据),新增的业务数据推送相关表部署在 MySQL 数据库(存储推送的业务明细数据)。为满足跨数据源的数据查询、整合需求,系统需适配 PostgreSQL + MySQL 双数据源架构,确保两类数据源独立管理、数据交互正常。

二、数据源配置说明

1. 核心配置文件(application.yml)

系统通过 application.yml 区分两个数据源的连接信息,关键配置如下:

复制代码
spring:
  # 多数据源配置
  datasource:
    # 主数据源:PostgreSQL(YFGASysDB)
    postgres:
      driver-class-name: org.postgresql.Driver
      jdbc-url: jdbc:postgresql://localhost:5432/xxx
      username: xxx
      password: xxx
      hikari:
        maximum-pool-size: 50
        minimum-idle: 5
        idle-timeout: 30000
        connection-timeout: 120000

    # 次数据源:MySQL(warehouse_manager)
    mysql:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql://localhost:3306/xxx?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
      username: xxx
      password: xxx
      hikari:
        maximum-pool-size: 20
        minimum-idle: 2
        idle-timeout: 30000
        connection-timeout: 60000

# MyBatis基础配置(不变)
mybatis:
  mapper-locations: classpath*:mapper/**/*.xml
  typeAliasesPackage: com.ss.emergency_systemctl.model
  configuration:
    call-setters-on-nulls: true
    map-underscore-to-camel-case: true

mybatis-plus:
  configuration:
    type-handlers-package: com.ss.emergency_systemctl.config

# Swagger开关(不变)
swagger:
  enable: true

# 日志配置(不变)
logging:
  level:
    root: info
    com.njcky: debug
    org.springframework: warn

2. 数据源核心配置类(DataSourceConfig.java)

通过 DataSourceConfig 类将配置文件中的连接信息转换为 Spring 可识别的 DataSource Bean,实现数据源的实例化与隔离:

  • 主数据源(PostgreSQL):Bean 名称 postgresDataSource,标记 @Primary 避免默认数据源冲突;
  • 次数据源(MySQL):Bean 名称 mysqlDataSource,专用于业务数据推送相关操作;
  • 同时为两个数据源配置独立的事务管理器(postgresTransactionManager/mysqlTransactionManager),确保事务隔离。

3. MyBatis 分库配置

为避免 SQL 执行与数据源不匹配,系统为两个数据源配置独立的 MyBatis 规则:

数据源 MyBatis 配置类 Mapper 接口包路径 XML 文件路径 核心规则
PostgreSQL PostgresMyBatisConfig com.ss.emergency_systemctl.mapper.postgres classpath:mapper/postgres/*.xml SQL 用 STRING_AGG 函数
MySQL MysqlMyBatisConfig com.ss.emergency_systemctl.mapper.mysql classpath:mapper/my

三、核心实现方案

1. 数据查询隔离

  • PostgreSQL 相关 Mapper 接口 / XML 仅操作本地基础库表,SQL 需兼容 PostgreSQL 语法
  • MySQL 相关 Mapper 接口 / XML 仅操作业务数据推送表(如 表一/表二/表三),SQL 兼容
  • Service 层通过注入对应包下的 Mapper 实现分库查询(如注入 mysql 包下的 )

MysqlMyBatisConfig

复制代码
package com.ss.emergency_systemctl.config;

import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * MySQL数据源的MyBatis配置
 * 扫描指定包下的Mapper,绑定MySQL数据源
 */
@Configuration
@MapperScan(
        basePackages = "com.ss.emergency_systemctl.mapper.mysql",
        sqlSessionTemplateRef = "mysqlSqlSessionTemplate"
)
public class MysqlMyBatisConfig {
    @Resource
    private  DataSource mysqlDataSource;


    @Bean(name = "mysqlSqlSessionFactory")
    public SqlSessionFactory mysqlSqlSessionFactory() throws Exception {
        MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
        // 设置数据源
        factoryBean.setDataSource(mysqlDataSource);
        // 设置Mapper XML文件路径
        factoryBean.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources("classpath:mapper/mysql/*.xml")
        );
        // 设置别名包
        factoryBean.setTypeAliasesPackage("com.ss.emergency_systemctl.entity.mysql");
        // 🔥 去掉手动创建Configuration的代码,复用全局配置
        return factoryBean.getObject();
    }

    @Bean(name = "mysqlSqlSessionTemplate")
    public SqlSessionTemplate mysqlSqlSessionTemplate(
            @Qualifier("mysqlSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

PostgresMyBatisConfig

复制代码
package com.ss.emergency_systemctl.config;

import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * PostgreSQL数据源的MyBatis配置
 * 扫描指定包下的Mapper,绑定PostgreSQL数据源
 */
@Configuration
@MapperScan(
        basePackages = "com.ss.emergency_systemctl.mapper.postgres",
        sqlSessionTemplateRef = "postgresSqlSessionTemplate"
)
public class PostgresMyBatisConfig {
    @Resource
    private  DataSource postgresDataSource;

    @Bean(name = "postgresSqlSessionFactory")
    public SqlSessionFactory postgresSqlSessionFactory() throws Exception {
        MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
        // 设置数据源
        factoryBean.setDataSource(postgresDataSource);
        // 设置Mapper XML文件路径
        factoryBean.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources("classpath:mapper/postgres/*.xml")
        );
        // 设置别名包(和全局配置一致)
        factoryBean.setTypeAliasesPackage("com.ss.emergency_systemctl.entity.postgres");
        // 🔥 去掉手动创建Configuration的代码,复用application.yml中的mybatis.configuration配置
        return factoryBean.getObject();
    }

    @Bean(name = "postgresSqlSessionTemplate")
    public SqlSessionTemplate postgresSqlSessionTemplate(
            @Qualifier("postgresSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

DataSourceConfig

复制代码
package com.ss.emergency_systemctl.config;

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
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 org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

/**
 * 多数据源配置类
 * 主数据源:PostgreSQL(标注@Primary)
 * 次数据源:MySQL
 */
@Configuration
public class DataSourceConfig {

    // ========== 主数据源:PostgreSQL ==========
    @Bean(name = "postgresDataSource")
    @Primary // 标记为主数据源,解决多数据源时的默认注入问题
    @ConfigurationProperties(prefix = "spring.datasource.postgres")
    public DataSource postgresDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    // ========== 次数据源:MySQL ==========
    @Bean(name = "mysqlDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.mysql")
    public DataSource mysqlDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    // ========== 事务管理器:PostgreSQL ==========
    @Bean(name = "postgresTransactionManager")
    @Primary
    public PlatformTransactionManager postgresTransactionManager(
            @Qualifier("postgresDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    // ========== 事务管理器:MySQL ==========
    @Bean(name = "mysqlTransactionManager")
    public PlatformTransactionManager mysqlTransactionManager(
            @Qualifier("mysqlDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

工程目录都可以创建包分类出来

每个包都可以分类:如 Controller(mysql,postgres)等等

四、注意事项

1. 数据源适配

  • 禁止在 PostgreSQL 的 MyBatis XML 中使用 MySQL 专属函数(如 GROUP_CONCAT),反之亦然;
  • 若需跨库联查,需通过程序层整合(避免直接数据库联表,因两库无直接连接)。

2. 事务管理

  • 操作 MySQL 数据时,必须指定事务管理器 mysqlTransactionManager

  • 否则会默认使用 PostgreSQL 的事务管理器导致事务失效;

  • 避免在一个事务中同时操作两个数据源(分布式事务需额外配置,当前架构暂不支持)。

    四、只有这些场景才必须用事务 + 指定管理器
    当你的 MySQL 操作包含「增 / 删 / 改」时,才需要加事务注解并指定管理器,比如:
    java
    运行
    // 示例:修改MySQL数据时,必须指定事务管理器
    @Transactional(transactionManager = "mysqlTransactionManager")
    public void updateModuleData(String qhmc, List<String> newModuleIds) {
    // 1. 删除旧数据
    dataPushTableMapper.deleteByQhmc(qhmc);
    // 2. 插入新数据
    dataPushTableMapper.batchInsert(qhmc, newModuleIds);
    // 若步骤1/2有一个失败,事务会回滚,避免数据不一致
    }

3. 配置校验

  • 启动前需验证 DataSourceConfig 被 Spring 扫描(主启动类包名需覆盖 config 子包);
  • 编译后检查 XML 文件路径与内容:MySQL 的 XML 需在 mapper/mysql 目录,且 namespace 与 Mapper 接口全类名一致。

多数据源适配核心总结

一、核心背景与架构

因业务拓展需对接外部推送的业务数据,系统采用「PostgreSQL(本地基础库)+ MySQL(业务数据使用库)」双数据源架构:

  • PostgreSQL:存储系统核心基础配置、元数据,作为主数据源(标记 @Primary);
  • MySQL:存储外部推送的业务明细数据(如 表一/表二等表),作为次数据源。

二、核心实现要点

1. 数据源隔离配置

  • 通过 DataSourceConfig 类创建两个命名化 DataSource Bean(postgresDataSource/mysqlDataSource),并绑定 application.yml 中的连接信息;
  • 为两个数据源配置独立的 MyBatis 规则:PostgreSQL 对应 mapper/postgres 目录(SQL 用 STRING_AGG),MySQL 对应 mapper/mysql 目录(SQL 用 GROUP_CONCAT),避免语法冲突。

2. 数据操作规范

  • 查询层面:PostgreSQL Mapper 操作基础库,MySQL Mapper 操作业务推送表,Service 层通过注入对应包下的 Mapper 实现分库查询;
  • 事务层面 :纯查询操作无需事务注解;MySQL 增删改操作需显式指定 @Transactional(transactionManager = "mysqlTransactionManager"),避免默认使用 PostgreSQL 事务管理器导致事务失效;

三、关键注意事项

  1. 语法适配 :严格区分数据源 SQL 语法,禁止跨库混用专属函数(如 MySQL 的 GROUP_CONCAT 不可用于 PostgreSQL);
  2. 配置校验 :确保 DataSourceConfig 被 Spring 扫描、Mapper 接口 / XML 文件名与 namespace 匹配,避免「找不到 Bean」「绑定语句不存在」等报错;
  3. 事务边界:禁止在一个事务中同时操作两个数据源,纯查询无需事务注解,仅增删改需指定 MySQL 事务管理器。

四、核心结论

本次多数据源适配实现了「基础库与业务库的隔离管理、数据查询的跨库整合」,核心原则为:数据源按用途隔离配置,SQL 按数据源语法适配,事务按操作类型精准管控,既保障了数据操作的正确性,也满足了业务拓展对多数据源的使用需求。

相关推荐
Mr -老鬼13 小时前
功能需求对前后端技术选型的横向建议
开发语言·前端·后端·前端框架
IT=>小脑虎13 小时前
Go语言零基础小白学习知识点【基础版详解】
开发语言·后端·学习·golang
程序猿阿伟13 小时前
《Python复杂结构静态分析秘籍:递归类型注解的深度实践指南》
java·数据结构·算法
qq_4061761413 小时前
关于JavaScript中的filter方法
开发语言·前端·javascript·ajax·原型模式
黑白极客13 小时前
怎么给字符串字段加索引?日志系统 一条更新语句是怎么执行的
java·数据库·sql·mysql·引擎
爬山算法14 小时前
Hibernate(32)什么是Hibernate的Criteria查询?
java·python·hibernate
醇氧14 小时前
Ping 127.0.0.1 具有 32 字节的数据:一般故障。【二】
运维·服务器·开发语言
码农水水14 小时前
中国邮政Java面试:热点Key的探测和本地缓存方案
java·开发语言·windows·缓存·面试·职场和发展·kafka
CCPC不拿奖不改名14 小时前
python基础:python语言中的控制结构+面试习题
开发语言·python·学习