若以SpringBoot-Vue, 数据源切换架构详解

数据源切换架构详解

本篇深入剖析 base-multidb 项目「改一处配置,即可切换数据库 」的核心机制与整体架构。

配套图示:架构图、流程图、时序图、思维导图、类图、表格,力求结构清晰、可复盘、可答辩。


一、一句话核心

整个项目只需要修改 application.yml 中的 一个配置项

yaml 复制代码
database:
  type: mysql        # ← 只改这里:mysql / opengauss / postgresql ...

它通过 四层联动机制 向下传递,自动完成「数据源连接信息、MyBatis Mapper 文件、分页方言、连接池参数」的全部切换,业务代码无需任何改动。

复制代码
一处修改 (database.type)  ──►  五处自动联动 (连接/Mapper/方言/池/加密密码)

二、整体架构总览

2.1 分层架构图

#mermaid-svg-sEHWLC5Xarj3RGj4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-sEHWLC5Xarj3RGj4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sEHWLC5Xarj3RGj4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sEHWLC5Xarj3RGj4 .error-icon{fill:#552222;}#mermaid-svg-sEHWLC5Xarj3RGj4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sEHWLC5Xarj3RGj4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sEHWLC5Xarj3RGj4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sEHWLC5Xarj3RGj4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sEHWLC5Xarj3RGj4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sEHWLC5Xarj3RGj4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sEHWLC5Xarj3RGj4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sEHWLC5Xarj3RGj4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sEHWLC5Xarj3RGj4 .marker.cross{stroke:#333333;}#mermaid-svg-sEHWLC5Xarj3RGj4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sEHWLC5Xarj3RGj4 p{margin:0;}#mermaid-svg-sEHWLC5Xarj3RGj4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-sEHWLC5Xarj3RGj4 .cluster-label text{fill:#333;}#mermaid-svg-sEHWLC5Xarj3RGj4 .cluster-label span{color:#333;}#mermaid-svg-sEHWLC5Xarj3RGj4 .cluster-label span p{background-color:transparent;}#mermaid-svg-sEHWLC5Xarj3RGj4 .label text,#mermaid-svg-sEHWLC5Xarj3RGj4 span{fill:#333;color:#333;}#mermaid-svg-sEHWLC5Xarj3RGj4 .node rect,#mermaid-svg-sEHWLC5Xarj3RGj4 .node circle,#mermaid-svg-sEHWLC5Xarj3RGj4 .node ellipse,#mermaid-svg-sEHWLC5Xarj3RGj4 .node polygon,#mermaid-svg-sEHWLC5Xarj3RGj4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-sEHWLC5Xarj3RGj4 .rough-node .label text,#mermaid-svg-sEHWLC5Xarj3RGj4 .node .label text,#mermaid-svg-sEHWLC5Xarj3RGj4 .image-shape .label,#mermaid-svg-sEHWLC5Xarj3RGj4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-sEHWLC5Xarj3RGj4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-sEHWLC5Xarj3RGj4 .rough-node .label,#mermaid-svg-sEHWLC5Xarj3RGj4 .node .label,#mermaid-svg-sEHWLC5Xarj3RGj4 .image-shape .label,#mermaid-svg-sEHWLC5Xarj3RGj4 .icon-shape .label{text-align:center;}#mermaid-svg-sEHWLC5Xarj3RGj4 .node.clickable{cursor:pointer;}#mermaid-svg-sEHWLC5Xarj3RGj4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-sEHWLC5Xarj3RGj4 .arrowheadPath{fill:#333333;}#mermaid-svg-sEHWLC5Xarj3RGj4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-sEHWLC5Xarj3RGj4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-sEHWLC5Xarj3RGj4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sEHWLC5Xarj3RGj4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-sEHWLC5Xarj3RGj4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sEHWLC5Xarj3RGj4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-sEHWLC5Xarj3RGj4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-sEHWLC5Xarj3RGj4 .cluster text{fill:#333;}#mermaid-svg-sEHWLC5Xarj3RGj4 .cluster span{color:#333;}#mermaid-svg-sEHWLC5Xarj3RGj4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-sEHWLC5Xarj3RGj4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-sEHWLC5Xarj3RGj4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-sEHWLC5Xarj3RGj4 .icon-shape,#mermaid-svg-sEHWLC5Xarj3RGj4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sEHWLC5Xarj3RGj4 .icon-shape p,#mermaid-svg-sEHWLC5Xarj3RGj4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-sEHWLC5Xarj3RGj4 .icon-shape .label rect,#mermaid-svg-sEHWLC5Xarj3RGj4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sEHWLC5Xarj3RGj4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-sEHWLC5Xarj3RGj4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-sEHWLC5Xarj3RGj4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-sEHWLC5Xarj3RGj4 .layer>*{fill:#eef5ff!important;stroke:#3b82f6!important;stroke-width:1px!important;}#mermaid-svg-sEHWLC5Xarj3RGj4 .layer span{fill:#eef5ff!important;stroke:#3b82f6!important;stroke-width:1px!important;}#mermaid-svg-sEHWLC5Xarj3RGj4 .highlight>*{fill:#fff7e6!important;stroke:#f59e0b!important;stroke-width:2px!important;}#mermaid-svg-sEHWLC5Xarj3RGj4 .highlight span{fill:#fff7e6!important;stroke:#f59e0b!important;stroke-width:2px!important;} ⑤ 运行期 (AOP)
④ SQL 方言层
③ 数据源装配层 (Spring)
② 属性绑定层 (Java)
① 配置层 (YAML)
Profile 分组

mysql → db-mysql
占位符 {...} determineCurrentLookupKey 占位符 {database.type}
占位符 ${database.type}
application.yml

database.type = mysql

(唯一开关)
application-druid.yml

(连接池通用参数)
application-db-mysql.yml

(本库连接信息)
DatabaseType 枚举

(驱动/URL模板/方言/端口)
DatabaseProperties

@ConfigurationProperties(database)
DruidProperties

(池参数)
DruidConfig

masterDataSource()
DynamicDataSource

@Primary 路由数据源

extends AbstractRoutingDataSource
DynamicDataSourceContextHolder

ThreadLocal
MyBatisConfig

mapper/${database.type}/**
PageHelper

dialect = {database.dialect.{type}}
@DataSource 注解

MASTER / SLAVE
DataSourceAspect 切面

2.2 五层职责一览

所在模块 关键组件 职责
① 配置层 admin/resources application.yml + application-db-*.yml + application-druid.yml 声明 database.type 与各库连接信息
② 属性绑定层 framework / common DatabaseProperties + DatabaseType 枚举 database.* 配置绑定成对象,并按类型推导驱动/方言/端口
③ 数据源装配层 framework DruidConfig + DynamicDataSource + DynamicDataSourceContextHolder 创建 Druid 连接池,组装成可路由的动态数据源
④ SQL 方言层 framework + 各业务模块 MyBatisConfig + PageHelper 按类型加载对应库的 Mapper XML 与分页方言
⑤ 运行期 AOP framework + common @DataSource 注解 + DataSourceAspect 运行时按注解切换主/从库(读写分离)

⚠️ 注意:项目存在 两个层面 的「切换」,切勿混淆------

数据库类型切换 (MySQL↔openGauss,启动期、靠配置) 与 主从读写分离切换 (MASTER↔SLAVE,运行期、靠注解)。

详见 第五章


三、核心答案:一个配置如何生效

3.1 唯一开关

yaml 复制代码
# application.yml
database:
  type: mysql        # ✅ 全项目唯一需要修改的数据库切换开关
  dialect:           # 类型 → 分页方言 映射表
    mysql: mysql
    opengauss: postgresql
    postgresql: postgresql

3.2 一处配置 → 五处联动

database.type 这个值被 四种 Spring 机制 消费,分别驱动五个下游:

# 消费机制 触达的配置/代码 最终效果
1 Profile 占位符 spring.profiles.active: druid,${database.type} 决定激活哪个 Profile
2 Profile 分组 spring.profiles.group.mysql: db-mysql mysql 映射成 db-mysql,加载 application-db-mysql.yml连接信息来源
3 属性占位符 mybatis.mapperLocations: classpath*:mapper/${database.type}/** 只加载该库的 Mapper XML(SQL 方言来源
4 嵌套属性占位符 pagehelper.helperDialect: ${database.dialect.${database.type}:mysql} 选用该库的分页方言
5 属性绑定 DatabaseProperties.typeDatabaseType 枚举 自动推导驱动类、URL 模板、验证 SQL、默认端口

3.3 配置生效链路流程图

#mermaid-svg-Q3vcmNdcW21L1Arp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Q3vcmNdcW21L1Arp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Q3vcmNdcW21L1Arp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Q3vcmNdcW21L1Arp .error-icon{fill:#552222;}#mermaid-svg-Q3vcmNdcW21L1Arp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Q3vcmNdcW21L1Arp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Q3vcmNdcW21L1Arp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Q3vcmNdcW21L1Arp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Q3vcmNdcW21L1Arp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Q3vcmNdcW21L1Arp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Q3vcmNdcW21L1Arp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Q3vcmNdcW21L1Arp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Q3vcmNdcW21L1Arp .marker.cross{stroke:#333333;}#mermaid-svg-Q3vcmNdcW21L1Arp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Q3vcmNdcW21L1Arp p{margin:0;}#mermaid-svg-Q3vcmNdcW21L1Arp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Q3vcmNdcW21L1Arp .cluster-label text{fill:#333;}#mermaid-svg-Q3vcmNdcW21L1Arp .cluster-label span{color:#333;}#mermaid-svg-Q3vcmNdcW21L1Arp .cluster-label span p{background-color:transparent;}#mermaid-svg-Q3vcmNdcW21L1Arp .label text,#mermaid-svg-Q3vcmNdcW21L1Arp span{fill:#333;color:#333;}#mermaid-svg-Q3vcmNdcW21L1Arp .node rect,#mermaid-svg-Q3vcmNdcW21L1Arp .node circle,#mermaid-svg-Q3vcmNdcW21L1Arp .node ellipse,#mermaid-svg-Q3vcmNdcW21L1Arp .node polygon,#mermaid-svg-Q3vcmNdcW21L1Arp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Q3vcmNdcW21L1Arp .rough-node .label text,#mermaid-svg-Q3vcmNdcW21L1Arp .node .label text,#mermaid-svg-Q3vcmNdcW21L1Arp .image-shape .label,#mermaid-svg-Q3vcmNdcW21L1Arp .icon-shape .label{text-anchor:middle;}#mermaid-svg-Q3vcmNdcW21L1Arp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Q3vcmNdcW21L1Arp .rough-node .label,#mermaid-svg-Q3vcmNdcW21L1Arp .node .label,#mermaid-svg-Q3vcmNdcW21L1Arp .image-shape .label,#mermaid-svg-Q3vcmNdcW21L1Arp .icon-shape .label{text-align:center;}#mermaid-svg-Q3vcmNdcW21L1Arp .node.clickable{cursor:pointer;}#mermaid-svg-Q3vcmNdcW21L1Arp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Q3vcmNdcW21L1Arp .arrowheadPath{fill:#333333;}#mermaid-svg-Q3vcmNdcW21L1Arp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Q3vcmNdcW21L1Arp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Q3vcmNdcW21L1Arp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Q3vcmNdcW21L1Arp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Q3vcmNdcW21L1Arp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Q3vcmNdcW21L1Arp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Q3vcmNdcW21L1Arp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Q3vcmNdcW21L1Arp .cluster text{fill:#333;}#mermaid-svg-Q3vcmNdcW21L1Arp .cluster span{color:#333;}#mermaid-svg-Q3vcmNdcW21L1Arp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Q3vcmNdcW21L1Arp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Q3vcmNdcW21L1Arp rect.text{fill:none;stroke-width:0;}#mermaid-svg-Q3vcmNdcW21L1Arp .icon-shape,#mermaid-svg-Q3vcmNdcW21L1Arp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Q3vcmNdcW21L1Arp .icon-shape p,#mermaid-svg-Q3vcmNdcW21L1Arp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Q3vcmNdcW21L1Arp .icon-shape .label rect,#mermaid-svg-Q3vcmNdcW21L1Arp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Q3vcmNdcW21L1Arp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Q3vcmNdcW21L1Arp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Q3vcmNdcW21L1Arp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-Q3vcmNdcW21L1Arp .change>*{fill:#fff7e6!important;stroke:#f59e0b!important;stroke-width:2px!important;}#mermaid-svg-Q3vcmNdcW21L1Arp .change span{fill:#fff7e6!important;stroke:#f59e0b!important;stroke-width:2px!important;}#mermaid-svg-Q3vcmNdcW21L1Arp .ok>*{fill:#e6ffed!important;stroke:#22c55e!important;stroke-width:2px!important;}#mermaid-svg-Q3vcmNdcW21L1Arp .ok span{fill:#e6ffed!important;stroke:#22c55e!important;stroke-width:2px!important;} 是: mysql → db-mysql
修改 database.type = mysql
Spring Boot 启动

读取 application.yml
解析占位符

spring.profiles.active = druid,mysql
Profile 分组

mysql ∈ group?
加载 application-db-mysql.yml

注入 database.host/port/url/username/password
加载 application-druid.yml

注入连接池参数
DatabaseProperties 绑定 database.*

并按 DatabaseType 推导驱动/方言
DruidProperties 绑定池参数
DruidConfig.masterDataSource()

new DruidDataSource + 连接信息
组装 DynamicDataSource(@Primary)

master (+可选 slave)
MyBatis mapperLocations = mapper/mysql/**
PageHelper dialect = mysql
✅ 启动完成

连接池/Mapper/方言 全部就绪

3.4 启动期装配时序图

PageHelper MyBatisConfig DynamicDataSource DruidConfig DruidProperties DatabaseType枚举 DatabaseProperties application-db-mysql.yml application.yml PageHelper MyBatisConfig DynamicDataSource DruidConfig DruidProperties DatabaseType枚举 DatabaseProperties application-db-mysql.yml application.yml #mermaid-svg-uuwGAta6zhjnRKbO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uuwGAta6zhjnRKbO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uuwGAta6zhjnRKbO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uuwGAta6zhjnRKbO .error-icon{fill:#552222;}#mermaid-svg-uuwGAta6zhjnRKbO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uuwGAta6zhjnRKbO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uuwGAta6zhjnRKbO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uuwGAta6zhjnRKbO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uuwGAta6zhjnRKbO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uuwGAta6zhjnRKbO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uuwGAta6zhjnRKbO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uuwGAta6zhjnRKbO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uuwGAta6zhjnRKbO .marker.cross{stroke:#333333;}#mermaid-svg-uuwGAta6zhjnRKbO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uuwGAta6zhjnRKbO p{margin:0;}#mermaid-svg-uuwGAta6zhjnRKbO .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-uuwGAta6zhjnRKbO text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-uuwGAta6zhjnRKbO .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-uuwGAta6zhjnRKbO .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-uuwGAta6zhjnRKbO .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-uuwGAta6zhjnRKbO .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-uuwGAta6zhjnRKbO #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-uuwGAta6zhjnRKbO .sequenceNumber{fill:white;}#mermaid-svg-uuwGAta6zhjnRKbO #sequencenumber{fill:#333;}#mermaid-svg-uuwGAta6zhjnRKbO #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-uuwGAta6zhjnRKbO .messageText{fill:#333;stroke:none;}#mermaid-svg-uuwGAta6zhjnRKbO .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-uuwGAta6zhjnRKbO .labelText,#mermaid-svg-uuwGAta6zhjnRKbO .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-uuwGAta6zhjnRKbO .loopText,#mermaid-svg-uuwGAta6zhjnRKbO .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-uuwGAta6zhjnRKbO .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-uuwGAta6zhjnRKbO .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-uuwGAta6zhjnRKbO .noteText,#mermaid-svg-uuwGAta6zhjnRKbO .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-uuwGAta6zhjnRKbO .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-uuwGAta6zhjnRKbO .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-uuwGAta6zhjnRKbO .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-uuwGAta6zhjnRKbO .actorPopupMenu{position:absolute;}#mermaid-svg-uuwGAta6zhjnRKbO .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-uuwGAta6zhjnRKbO .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-uuwGAta6zhjnRKbO .actor-man circle,#mermaid-svg-uuwGAta6zhjnRKbO line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-uuwGAta6zhjnRKbO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Profile 分组: mysql → db-mysql getJdbcUrl()/getDriverClassName()/... 就绪 标注 @Primary,作为全局数据源 ✅ 数据源 + SQL 方言 同步就绪 database.type = mysql 1 profiles.active = druid,{database.type} 2 激活 db-mysql,加载连接信息 3 绑定 database.host/port/url/username/password 4 DatabaseType.fromCode("mysql") 5 返回 MYSQL(驱动/URL模板/方言/端口) 6 注入池参数(initialSize/maxActive...) 7 masterDataSource() 取连接信息 8 druidProperties.dataSource() 套用池参数 9 new DynamicDataSource(master, targets) 10 mapperLocations = mapper/{database.type}/** 11 解析为 mapper/mysql/**,加载 19 个 Mapper XML 12 dialect = ${database.dialect.mysql} = mysql 13


四、四层联动设计详解

4.1 配置层:Profile 分组 + 占位符

三个配置文件的分工
文件 何时加载 承载内容
application.yml 总是加载 database.type 开关 + Profile 分组 + Redis/Token/MyBatis 等全局配置
application-db-<type>.yml 仅当 database.type 匹配时 该库的 database.host/port/url/username/password连接信息
application-druid.yml 总是加载(druid Profile) Druid 连接池通用参数、监控台、慢 SQL、防火墙(池参数
Profile 分组的精妙之处
yaml 复制代码
spring:
  profiles:
    active: druid,${database.type}   # 例: druid,mysql
    group:
      mysql: db-mysql                # 业务类型名  →  物理文件 Profile 名
      opengauss: db-opengauss
      postgresql: db-postgresql

为什么要套一层 group?

因为同一个 database.type=mysql 这个「业务类型名」要服务于两个不同用途,且命名空间不同:

  • 作为 Profile 名 时,对应的配置文件是 application-**db-mysql**.yml(物理 Profile 名带 db- 前缀);
  • 作为 Mapper 目录 / 方言 Key 时,用的是裸的 mysqlmapper/mysql/database.dialect.mysql)。

group 正是这两套命名之间的桥梁 :用业务名 mysql 激活,由 group 翻译成物理文件 db-mysql,而占位符 ${database.type} 仍可一致地复用裸名 mysql

占位符的复用
yaml 复制代码
# 同一个 ${database.type} 变量,被三处复用
mybatis:
  mapperLocations: classpath*:mapper/${database.type}/**/*Mapper.xml

pagehelper:
  helperDialect: ${database.dialect.${database.type}:mysql}   # 嵌套占位符

spring:
  profiles:
    active: druid,${database.type}

4.2 属性绑定层:DatabaseProperties + DatabaseType

DatabaseType 枚举------数据库的「元数据中心」

每种数据库的元信息被集中封装为一个枚举常量,新增一种数据库只需增加一行枚举

java 复制代码
public enum DatabaseType {
    MYSQL ("mysql","MySQL","com.mysql.cj.jdbc.Driver",
           "jdbc:mysql://localhost:3306/%s?...","SELECT 1","mysql"),
    OPENGAUSS("opengauss","openGauss","org.postgresql.Driver",
           "jdbc:opengauss://localhost:54321/%s","SELECT 1","postgresql"), // 复用PG方言
    DM    ("dm","达梦","dm.jdbc.driver.DmDriver",
           "jdbc:dm://localhost:5236/%s","SELECT 1","oracle"); // 复用Oracle方言
    // ...
}

国产化适配的关键经验:openGauss / 人大金仓 沿用 PostgreSQL 方言;达梦 沿用 Oracle 方言------这就是为什么它们能低成本接入。

DatabaseProperties------统一配置出口

@ConfigurationProperties(prefix = "database") 统一承接配置,并提供「自动推导」能力:
#mermaid-svg-z7vBpK4bOXI3BB2u{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-z7vBpK4bOXI3BB2u .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-z7vBpK4bOXI3BB2u .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-z7vBpK4bOXI3BB2u .error-icon{fill:#552222;}#mermaid-svg-z7vBpK4bOXI3BB2u .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-z7vBpK4bOXI3BB2u .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-z7vBpK4bOXI3BB2u .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-z7vBpK4bOXI3BB2u .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-z7vBpK4bOXI3BB2u .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-z7vBpK4bOXI3BB2u .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-z7vBpK4bOXI3BB2u .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-z7vBpK4bOXI3BB2u .marker{fill:#333333;stroke:#333333;}#mermaid-svg-z7vBpK4bOXI3BB2u .marker.cross{stroke:#333333;}#mermaid-svg-z7vBpK4bOXI3BB2u svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-z7vBpK4bOXI3BB2u p{margin:0;}#mermaid-svg-z7vBpK4bOXI3BB2u .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-z7vBpK4bOXI3BB2u .cluster-label text{fill:#333;}#mermaid-svg-z7vBpK4bOXI3BB2u .cluster-label span{color:#333;}#mermaid-svg-z7vBpK4bOXI3BB2u .cluster-label span p{background-color:transparent;}#mermaid-svg-z7vBpK4bOXI3BB2u .label text,#mermaid-svg-z7vBpK4bOXI3BB2u span{fill:#333;color:#333;}#mermaid-svg-z7vBpK4bOXI3BB2u .node rect,#mermaid-svg-z7vBpK4bOXI3BB2u .node circle,#mermaid-svg-z7vBpK4bOXI3BB2u .node ellipse,#mermaid-svg-z7vBpK4bOXI3BB2u .node polygon,#mermaid-svg-z7vBpK4bOXI3BB2u .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-z7vBpK4bOXI3BB2u .rough-node .label text,#mermaid-svg-z7vBpK4bOXI3BB2u .node .label text,#mermaid-svg-z7vBpK4bOXI3BB2u .image-shape .label,#mermaid-svg-z7vBpK4bOXI3BB2u .icon-shape .label{text-anchor:middle;}#mermaid-svg-z7vBpK4bOXI3BB2u .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-z7vBpK4bOXI3BB2u .rough-node .label,#mermaid-svg-z7vBpK4bOXI3BB2u .node .label,#mermaid-svg-z7vBpK4bOXI3BB2u .image-shape .label,#mermaid-svg-z7vBpK4bOXI3BB2u .icon-shape .label{text-align:center;}#mermaid-svg-z7vBpK4bOXI3BB2u .node.clickable{cursor:pointer;}#mermaid-svg-z7vBpK4bOXI3BB2u .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-z7vBpK4bOXI3BB2u .arrowheadPath{fill:#333333;}#mermaid-svg-z7vBpK4bOXI3BB2u .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-z7vBpK4bOXI3BB2u .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-z7vBpK4bOXI3BB2u .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-z7vBpK4bOXI3BB2u .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-z7vBpK4bOXI3BB2u .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-z7vBpK4bOXI3BB2u .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-z7vBpK4bOXI3BB2u .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-z7vBpK4bOXI3BB2u .cluster text{fill:#333;}#mermaid-svg-z7vBpK4bOXI3BB2u .cluster span{color:#333;}#mermaid-svg-z7vBpK4bOXI3BB2u div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-z7vBpK4bOXI3BB2u .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-z7vBpK4bOXI3BB2u rect.text{fill:none;stroke-width:0;}#mermaid-svg-z7vBpK4bOXI3BB2u .icon-shape,#mermaid-svg-z7vBpK4bOXI3BB2u .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-z7vBpK4bOXI3BB2u .icon-shape p,#mermaid-svg-z7vBpK4bOXI3BB2u .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-z7vBpK4bOXI3BB2u .icon-shape .label rect,#mermaid-svg-z7vBpK4bOXI3BB2u .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-z7vBpK4bOXI3BB2u .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-z7vBpK4bOXI3BB2u .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-z7vBpK4bOXI3BB2u :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 自动推导
配置输入
database.type
database.host/port/database-name
database.url (完整URL模式)
database.username/password
getDriverClassName()

← 枚举推导
getValidationQuery()

← 枚举推导
getDialect()

← 枚举推导
getJdbcUrl()

完整URL 或 拼接
getPort()

← 按类型默认端口

支持两种连接信息写法(由 use-full-url 开关决定):

模式 配置写法 适用场景
完整 URL 模式(当前采用) database.url: jdbc:mysql://... 需要精细控制连接参数
拼接模式 database.host + port + database-name + extraParams 简洁,URL 由枚举模板生成

4.3 数据源装配层:DruidConfig + DynamicDataSource

核心类职责

#mermaid-svg-xeoeQ5BeNZrh7s7p{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-xeoeQ5BeNZrh7s7p .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xeoeQ5BeNZrh7s7p .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xeoeQ5BeNZrh7s7p .error-icon{fill:#552222;}#mermaid-svg-xeoeQ5BeNZrh7s7p .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xeoeQ5BeNZrh7s7p .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xeoeQ5BeNZrh7s7p .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xeoeQ5BeNZrh7s7p .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xeoeQ5BeNZrh7s7p .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xeoeQ5BeNZrh7s7p .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xeoeQ5BeNZrh7s7p .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xeoeQ5BeNZrh7s7p .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xeoeQ5BeNZrh7s7p .marker.cross{stroke:#333333;}#mermaid-svg-xeoeQ5BeNZrh7s7p svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xeoeQ5BeNZrh7s7p p{margin:0;}#mermaid-svg-xeoeQ5BeNZrh7s7p g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-xeoeQ5BeNZrh7s7p g.classGroup text .title{font-weight:bolder;}#mermaid-svg-xeoeQ5BeNZrh7s7p .cluster-label text{fill:#333;}#mermaid-svg-xeoeQ5BeNZrh7s7p .cluster-label span{color:#333;}#mermaid-svg-xeoeQ5BeNZrh7s7p .cluster-label span p{background-color:transparent;}#mermaid-svg-xeoeQ5BeNZrh7s7p .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xeoeQ5BeNZrh7s7p .cluster text{fill:#333;}#mermaid-svg-xeoeQ5BeNZrh7s7p .cluster span{color:#333;}#mermaid-svg-xeoeQ5BeNZrh7s7p .nodeLabel,#mermaid-svg-xeoeQ5BeNZrh7s7p .edgeLabel{color:#131300;}#mermaid-svg-xeoeQ5BeNZrh7s7p .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-xeoeQ5BeNZrh7s7p .label text{fill:#131300;}#mermaid-svg-xeoeQ5BeNZrh7s7p .labelBkg{background:#ECECFF;}#mermaid-svg-xeoeQ5BeNZrh7s7p .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-xeoeQ5BeNZrh7s7p .classTitle{font-weight:bolder;}#mermaid-svg-xeoeQ5BeNZrh7s7p .node rect,#mermaid-svg-xeoeQ5BeNZrh7s7p .node circle,#mermaid-svg-xeoeQ5BeNZrh7s7p .node ellipse,#mermaid-svg-xeoeQ5BeNZrh7s7p .node polygon,#mermaid-svg-xeoeQ5BeNZrh7s7p .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xeoeQ5BeNZrh7s7p .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-xeoeQ5BeNZrh7s7p g.clickable{cursor:pointer;}#mermaid-svg-xeoeQ5BeNZrh7s7p g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-xeoeQ5BeNZrh7s7p g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-xeoeQ5BeNZrh7s7p .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-xeoeQ5BeNZrh7s7p .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-xeoeQ5BeNZrh7s7p .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-xeoeQ5BeNZrh7s7p .dashed-line{stroke-dasharray:3;}#mermaid-svg-xeoeQ5BeNZrh7s7p .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-xeoeQ5BeNZrh7s7p #compositionStart,#mermaid-svg-xeoeQ5BeNZrh7s7p .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-xeoeQ5BeNZrh7s7p #compositionEnd,#mermaid-svg-xeoeQ5BeNZrh7s7p .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-xeoeQ5BeNZrh7s7p #dependencyStart,#mermaid-svg-xeoeQ5BeNZrh7s7p .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-xeoeQ5BeNZrh7s7p #dependencyStart,#mermaid-svg-xeoeQ5BeNZrh7s7p .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-xeoeQ5BeNZrh7s7p #extensionStart,#mermaid-svg-xeoeQ5BeNZrh7s7p .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-xeoeQ5BeNZrh7s7p #extensionEnd,#mermaid-svg-xeoeQ5BeNZrh7s7p .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-xeoeQ5BeNZrh7s7p #aggregationStart,#mermaid-svg-xeoeQ5BeNZrh7s7p .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-xeoeQ5BeNZrh7s7p #aggregationEnd,#mermaid-svg-xeoeQ5BeNZrh7s7p .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-xeoeQ5BeNZrh7s7p #lollipopStart,#mermaid-svg-xeoeQ5BeNZrh7s7p .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-xeoeQ5BeNZrh7s7p #lollipopEnd,#mermaid-svg-xeoeQ5BeNZrh7s7p .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-xeoeQ5BeNZrh7s7p .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-xeoeQ5BeNZrh7s7p .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xeoeQ5BeNZrh7s7p .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xeoeQ5BeNZrh7s7p .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xeoeQ5BeNZrh7s7p :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 读取当前Key
取连接信息
创建
<<Spring>>
AbstractRoutingDataSource
+determineCurrentLookupKey() : Object
DynamicDataSource
+DynamicDataSource(default, targets)
#determineCurrentLookupKey() : Object
DynamicDataSourceContextHolder
-ThreadLocal<String> CONTEXT_HOLDER
+setDataSourceType(String)
+getDataSourceType() : String
+clearDataSourceType()
DruidConfig
+masterDataSource() : DataSource
+slaveDataSource() : DataSource
+dataSource() : DynamicDataSource
DatabaseProperties
+getJdbcUrl() : String
+getDriverClassName() : String
+getUsername() : String
+getPassword() : String

工作原理
java 复制代码
// 1. 创建主库(连接信息来自 DatabaseProperties)
@Bean
public DataSource masterDataSource(DruidProperties druidProperties) {
    DruidDataSource ds = new DruidDataSource();
    ds.setDriverClassName(databaseProperties.getDriverClassName());
    ds.setUrl(databaseProperties.getJdbcUrl());        // ← 连接信息来源
    ds.setUsername(databaseProperties.getUsername());
    ds.setPassword(databaseProperties.getPassword());
    return druidProperties.dataSource(ds);             // ← 套用连接池参数
}

// 2. 从库:按需装配(默认关闭)
@Bean
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
public DataSource slaveDataSource(DruidProperties druidProperties) { ... }

// 3. 组装路由数据源(@Primary = 全局唯一数据源)
@Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dataSource(DataSource masterDataSource) {
    Map<Object, Object> targets = new HashMap<>();
    targets.put(DataSourceType.MASTER.name(), masterDataSource);
    setDataSource(targets, DataSourceType.SLAVE.name(), "slaveDataSource");
    return new DynamicDataSource(masterDataSource, targets);
}

// 4. 路由数据源:每次取连接前问 ContextHolder "现在用哪个?"
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

📌 实现细节 :主/从库的「连接信息(url/账号/密码)」由 DruidConfig 手动从 DatabaseProperties 创建,而「连接池参数(大小/超时/检测)」由 DruidPropertiesapplication-druid.yml 注入后统一套用。application-druid.ymlspring.datasource.druid.master.* 段为兼容保留,实际连接信息以 application-db-*.yml 为准。

连接池参数来源对照
参数类别 来源文件 注入方式
url / username / password / driver application-db-<type>.yml DatabasePropertiesDruidConfig 手动 set
initialSize / minIdle / maxActive / maxWait / 超时 / 检测 application-druid.yml DruidProperties @Value → dataSource() 方法
监控台 / 慢SQL / 防火墙 application-druid.yml Druid 自动配置

4.4 SQL 方言层:MyBatis + PageHelper

Mapper 按「数据库类型」分目录组织
复制代码
base-multidb-system/src/main/resources/mapper/
├── mysql/system/        ← 19 个 Mapper (database.type=mysql 时加载)
├── postgresql/system/   ← 17 个
├── opengauss/system/    ← 17 个
└── dm/system/           ← 达梦

同一套 Java Service / Mapper 接口,切换 database.type 后自动匹配到该库的 SQL 方言实现------业务零改动

两处方言自动切换
yaml 复制代码
mybatis:
  mapperLocations: classpath*:mapper/${database.type}/**/*Mapper.xml   # → mapper/mysql/**

pagehelper:
  helperDialect: ${database.dialect.${database.type}:mysql}            # → mysql

五、两个层面的「切换」(重点辨析)

这是面试/评审最易被追问的点,务必区分清楚。

维度 数据库类型切换 主从读写分离
触发方式 database.type 配置 方法/类标注 @DataSource(SLAVE)
发生时机 应用启动期(一次性装配) 运行期(每次方法调用)
切换粒度 整个应用换一套库(MySQL↔openGauss) 同一应用内 主库↔从库
核心技术 Profile 分组 + 占位符 + 路由数据源 AOP 切面 + ThreadLocal
是否需重启
典型场景 国产化交付、多环境部署 读写分离、负载分担
作用范围 全局唯一数据源 当前线程

5.1 数据库类型切换(启动期,本篇主线)

第三章。一句话:改配置 → 重启 → 自动换库

5.2 主从读写分离(运行期 @DataSource + AOP)

利用 Spring AOP + AbstractRoutingDataSource + ThreadLocal 实现:
TLS 从库 主库 DynamicDataSource ContextHolder(ThreadLocal) DataSourceAspect 业务调用 TLS 从库 主库 DynamicDataSource ContextHolder(ThreadLocal) DataSourceAspect 业务调用 #mermaid-svg-1uEc9h6ShCXT7vLD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1uEc9h6ShCXT7vLD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1uEc9h6ShCXT7vLD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1uEc9h6ShCXT7vLD .error-icon{fill:#552222;}#mermaid-svg-1uEc9h6ShCXT7vLD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1uEc9h6ShCXT7vLD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1uEc9h6ShCXT7vLD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1uEc9h6ShCXT7vLD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1uEc9h6ShCXT7vLD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1uEc9h6ShCXT7vLD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1uEc9h6ShCXT7vLD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1uEc9h6ShCXT7vLD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1uEc9h6ShCXT7vLD .marker.cross{stroke:#333333;}#mermaid-svg-1uEc9h6ShCXT7vLD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1uEc9h6ShCXT7vLD p{margin:0;}#mermaid-svg-1uEc9h6ShCXT7vLD .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-1uEc9h6ShCXT7vLD text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-1uEc9h6ShCXT7vLD .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-1uEc9h6ShCXT7vLD .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-1uEc9h6ShCXT7vLD .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-1uEc9h6ShCXT7vLD .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-1uEc9h6ShCXT7vLD #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-1uEc9h6ShCXT7vLD .sequenceNumber{fill:white;}#mermaid-svg-1uEc9h6ShCXT7vLD #sequencenumber{fill:#333;}#mermaid-svg-1uEc9h6ShCXT7vLD #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-1uEc9h6ShCXT7vLD .messageText{fill:#333;stroke:none;}#mermaid-svg-1uEc9h6ShCXT7vLD .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-1uEc9h6ShCXT7vLD .labelText,#mermaid-svg-1uEc9h6ShCXT7vLD .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-1uEc9h6ShCXT7vLD .loopText,#mermaid-svg-1uEc9h6ShCXT7vLD .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-1uEc9h6ShCXT7vLD .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-1uEc9h6ShCXT7vLD .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-1uEc9h6ShCXT7vLD .noteText,#mermaid-svg-1uEc9h6ShCXT7vLD .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-1uEc9h6ShCXT7vLD .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-1uEc9h6ShCXT7vLD .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-1uEc9h6ShCXT7vLD .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-1uEc9h6ShCXT7vLD .actorPopupMenu{position:absolute;}#mermaid-svg-1uEc9h6ShCXT7vLD .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-1uEc9h6ShCXT7vLD .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-1uEc9h6ShCXT7vLD .actor-man circle,#mermaid-svg-1uEc9h6ShCXT7vLD line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-1uEc9h6ShCXT7vLD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} try { proceed() } finally { clearDataSourceType() } 调用 @DataSource(SLAVE) 方法 1 setDataSourceType("SLAVE") 2 方法内执行SQL,请求数据源 3 determineCurrentLookupKey() 4 "SLAVE" 5 路由到从库执行查询 6 结果 7 结果 8 remove() (防止线程复用串库) 9 返回结果 10

切面源码要点
java 复制代码
@Aspect @Order(1) @Component
public class DataSourceAspect {
    @Around("dsPointCut()")   // 拦截 @DataSource 注解
    public Object around(ProceedingJoinPoint point) throws Throwable {
        DataSource ds = getDataSource(point);
        if (ds != null)
            DynamicDataSourceContextHolder.setDataSourceType(ds.value().name()); // 进
        try { return point.proceed(); }                                          // 执行
        finally { DynamicDataSourceContextHolder.clearDataSourceType(); }        // 出(必清!)
    }
}
优先级规则

@DataSource 可标注在方法 上,采用 先方法后类、方法覆盖类 的策略:
#mermaid-svg-Ma9CuP5TXfytIIlE{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Ma9CuP5TXfytIIlE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Ma9CuP5TXfytIIlE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Ma9CuP5TXfytIIlE .error-icon{fill:#552222;}#mermaid-svg-Ma9CuP5TXfytIIlE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ma9CuP5TXfytIIlE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Ma9CuP5TXfytIIlE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ma9CuP5TXfytIIlE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ma9CuP5TXfytIIlE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Ma9CuP5TXfytIIlE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ma9CuP5TXfytIIlE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ma9CuP5TXfytIIlE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ma9CuP5TXfytIIlE .marker.cross{stroke:#333333;}#mermaid-svg-Ma9CuP5TXfytIIlE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ma9CuP5TXfytIIlE p{margin:0;}#mermaid-svg-Ma9CuP5TXfytIIlE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Ma9CuP5TXfytIIlE .cluster-label text{fill:#333;}#mermaid-svg-Ma9CuP5TXfytIIlE .cluster-label span{color:#333;}#mermaid-svg-Ma9CuP5TXfytIIlE .cluster-label span p{background-color:transparent;}#mermaid-svg-Ma9CuP5TXfytIIlE .label text,#mermaid-svg-Ma9CuP5TXfytIIlE span{fill:#333;color:#333;}#mermaid-svg-Ma9CuP5TXfytIIlE .node rect,#mermaid-svg-Ma9CuP5TXfytIIlE .node circle,#mermaid-svg-Ma9CuP5TXfytIIlE .node ellipse,#mermaid-svg-Ma9CuP5TXfytIIlE .node polygon,#mermaid-svg-Ma9CuP5TXfytIIlE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Ma9CuP5TXfytIIlE .rough-node .label text,#mermaid-svg-Ma9CuP5TXfytIIlE .node .label text,#mermaid-svg-Ma9CuP5TXfytIIlE .image-shape .label,#mermaid-svg-Ma9CuP5TXfytIIlE .icon-shape .label{text-anchor:middle;}#mermaid-svg-Ma9CuP5TXfytIIlE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Ma9CuP5TXfytIIlE .rough-node .label,#mermaid-svg-Ma9CuP5TXfytIIlE .node .label,#mermaid-svg-Ma9CuP5TXfytIIlE .image-shape .label,#mermaid-svg-Ma9CuP5TXfytIIlE .icon-shape .label{text-align:center;}#mermaid-svg-Ma9CuP5TXfytIIlE .node.clickable{cursor:pointer;}#mermaid-svg-Ma9CuP5TXfytIIlE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Ma9CuP5TXfytIIlE .arrowheadPath{fill:#333333;}#mermaid-svg-Ma9CuP5TXfytIIlE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Ma9CuP5TXfytIIlE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Ma9CuP5TXfytIIlE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ma9CuP5TXfytIIlE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Ma9CuP5TXfytIIlE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ma9CuP5TXfytIIlE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Ma9CuP5TXfytIIlE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Ma9CuP5TXfytIIlE .cluster text{fill:#333;}#mermaid-svg-Ma9CuP5TXfytIIlE .cluster span{color:#333;}#mermaid-svg-Ma9CuP5TXfytIIlE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Ma9CuP5TXfytIIlE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Ma9CuP5TXfytIIlE rect.text{fill:none;stroke-width:0;}#mermaid-svg-Ma9CuP5TXfytIIlE .icon-shape,#mermaid-svg-Ma9CuP5TXfytIIlE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ma9CuP5TXfytIIlE .icon-shape p,#mermaid-svg-Ma9CuP5TXfytIIlE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Ma9CuP5TXfytIIlE .icon-shape .label rect,#mermaid-svg-Ma9CuP5TXfytIIlE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ma9CuP5TXfytIIlE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Ma9CuP5TXfytIIlE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Ma9CuP5TXfytIIlE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 有



方法上有

@DataSource?
采用方法上的类型
类上有

@DataSource?
采用类上的类型
默认 MASTER

⚠️ finally 中务必 clearDataSourceType():Tomcat 使用线程池复用线程,若不清除,下个请求会沿用上一个线程残留的数据源 Key,导致串库


六、知识全景思维导图

#mermaid-svg-N8ctKYt7cz05zBZ6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-N8ctKYt7cz05zBZ6 .error-icon{fill:#552222;}#mermaid-svg-N8ctKYt7cz05zBZ6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-N8ctKYt7cz05zBZ6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-N8ctKYt7cz05zBZ6 .marker.cross{stroke:#333333;}#mermaid-svg-N8ctKYt7cz05zBZ6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-N8ctKYt7cz05zBZ6 p{margin:0;}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge{stroke-width:3;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section--1 rect,#mermaid-svg-N8ctKYt7cz05zBZ6 .section--1 path,#mermaid-svg-N8ctKYt7cz05zBZ6 .section--1 circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .section--1 polygon,#mermaid-svg-N8ctKYt7cz05zBZ6 .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .section--1 text{fill:#ffffff;}#mermaid-svg-N8ctKYt7cz05zBZ6 .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-depth--1{stroke-width:17;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:lightgray;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:#efefef;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-0 rect,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-0 path,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-0 circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-0 polygon,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-0 text{fill:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .node-icon-0{font-size:40px;color:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-depth-0{stroke-width:14;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:lightgray;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:#efefef;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-1 rect,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-1 path,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-1 circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-1 polygon,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-1 text{fill:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .node-icon-1{font-size:40px;color:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-depth-1{stroke-width:11;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:lightgray;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:#efefef;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-2 rect,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-2 path,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-2 circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-2 polygon,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-2 text{fill:#ffffff;}#mermaid-svg-N8ctKYt7cz05zBZ6 .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-depth-2{stroke-width:8;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:lightgray;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:#efefef;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-3 rect,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-3 path,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-3 circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-3 polygon,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-3 text{fill:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .node-icon-3{font-size:40px;color:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-depth-3{stroke-width:5;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:lightgray;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:#efefef;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-4 rect,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-4 path,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-4 circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-4 polygon,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-4 text{fill:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .node-icon-4{font-size:40px;color:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-depth-4{stroke-width:2;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:lightgray;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:#efefef;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-5 rect,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-5 path,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-5 circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-5 polygon,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-5 text{fill:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .node-icon-5{font-size:40px;color:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-depth-5{stroke-width:-1;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:lightgray;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:#efefef;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-6 rect,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-6 path,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-6 circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-6 polygon,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-6 text{fill:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .node-icon-6{font-size:40px;color:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-depth-6{stroke-width:-4;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:lightgray;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:#efefef;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-7 rect,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-7 path,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-7 circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-7 polygon,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-7 text{fill:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .node-icon-7{font-size:40px;color:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-depth-7{stroke-width:-7;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:lightgray;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:#efefef;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-8 rect,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-8 path,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-8 circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-8 polygon,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-8 text{fill:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .node-icon-8{font-size:40px;color:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-depth-8{stroke-width:-10;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:lightgray;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:#efefef;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-9 rect,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-9 path,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-9 circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-9 polygon,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-9 text{fill:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .node-icon-9{font-size:40px;color:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-depth-9{stroke-width:-13;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:lightgray;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:#efefef;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-10 rect,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-10 path,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-10 circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-10 polygon,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-10 text{fill:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .node-icon-10{font-size:40px;color:black;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge-depth-10{stroke-width:-16;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:lightgray;}#mermaid-svg-N8ctKYt7cz05zBZ6 .disabled text{fill:#efefef;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-root rect,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-root path,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-root circle,#mermaid-svg-N8ctKYt7cz05zBZ6 .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-root text{fill:#ffffff;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-root span{color:#ffffff;}#mermaid-svg-N8ctKYt7cz05zBZ6 .section-2 span{color:#ffffff;}#mermaid-svg-N8ctKYt7cz05zBZ6 .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-N8ctKYt7cz05zBZ6 .edge{fill:none;}#mermaid-svg-N8ctKYt7cz05zBZ6 .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-N8ctKYt7cz05zBZ6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数据源切换架构
配置层
唯一开关 database.type
Profile 分组 mysql映射db-mysql
占位符复用
三个YML分工
属性绑定层
DatabaseType枚举 元数据中心
DatabaseProperties 统一出口
自动推导 驱动方言端口
两种URL模式
数据源装配层
DruidConfig 手动装配
DruidProperties 池参数
DynamicDataSource 路由
AbstractRoutingDataSource
ThreadLocal上下文
SQL方言层
MyBatis 按库分目录Mapper
PageHelper 嵌套占位符方言
业务零改动
运行期AOP
DataSource注解
DataSourceAspect切面
主从读写分离
先方法后类优先级
两个层面辨析
类型切换 启动期
主从切换 运行期
安全与扩展
Jasypt密码加密
新增数据库步骤
国产化适配经验


七、实操:从 MySQL 切换到 openGauss

7.1 操作步骤

步骤 操作 说明
1 修改 application.ymldatabase.type: opengauss 唯一必改项
2 确认 application-db-opengauss.yml 中连接信息正确 host/port/账号/密码
3 (可选)密码用 ENC(密文) 形式 Jasypt 自动解密
4 重启应用 Profile/Mapper/方言 自动重装配

7.2 切换前后对照

配置/资源 MySQL 模式 openGauss 模式
database.type mysql opengauss
激活 Profile druid,mysql → db-mysql druid,opengauss → db-opengauss
加载连接文件 application-db-mysql.yml application-db-opengauss.yml
JDBC 驱动 com.mysql.cj.jdbc.Driver org.postgresql.Driver
JDBC URL jdbc:mysql://192.168.1.103:3306/test?... jdbc:opengauss://192.168.1.189:54321/test?...
Mapper 目录 mapper/mysql/**(19 个) mapper/opengauss/**(17 个)
PageHelper 方言 mysql postgresql(经 database.dialect 映射)
业务代码 不变 不变

7.3 切换决策树

#mermaid-svg-az8wsaRRCf9lT4ne{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-az8wsaRRCf9lT4ne .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-az8wsaRRCf9lT4ne .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-az8wsaRRCf9lT4ne .error-icon{fill:#552222;}#mermaid-svg-az8wsaRRCf9lT4ne .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-az8wsaRRCf9lT4ne .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-az8wsaRRCf9lT4ne .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-az8wsaRRCf9lT4ne .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-az8wsaRRCf9lT4ne .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-az8wsaRRCf9lT4ne .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-az8wsaRRCf9lT4ne .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-az8wsaRRCf9lT4ne .marker{fill:#333333;stroke:#333333;}#mermaid-svg-az8wsaRRCf9lT4ne .marker.cross{stroke:#333333;}#mermaid-svg-az8wsaRRCf9lT4ne svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-az8wsaRRCf9lT4ne p{margin:0;}#mermaid-svg-az8wsaRRCf9lT4ne .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-az8wsaRRCf9lT4ne .cluster-label text{fill:#333;}#mermaid-svg-az8wsaRRCf9lT4ne .cluster-label span{color:#333;}#mermaid-svg-az8wsaRRCf9lT4ne .cluster-label span p{background-color:transparent;}#mermaid-svg-az8wsaRRCf9lT4ne .label text,#mermaid-svg-az8wsaRRCf9lT4ne span{fill:#333;color:#333;}#mermaid-svg-az8wsaRRCf9lT4ne .node rect,#mermaid-svg-az8wsaRRCf9lT4ne .node circle,#mermaid-svg-az8wsaRRCf9lT4ne .node ellipse,#mermaid-svg-az8wsaRRCf9lT4ne .node polygon,#mermaid-svg-az8wsaRRCf9lT4ne .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-az8wsaRRCf9lT4ne .rough-node .label text,#mermaid-svg-az8wsaRRCf9lT4ne .node .label text,#mermaid-svg-az8wsaRRCf9lT4ne .image-shape .label,#mermaid-svg-az8wsaRRCf9lT4ne .icon-shape .label{text-anchor:middle;}#mermaid-svg-az8wsaRRCf9lT4ne .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-az8wsaRRCf9lT4ne .rough-node .label,#mermaid-svg-az8wsaRRCf9lT4ne .node .label,#mermaid-svg-az8wsaRRCf9lT4ne .image-shape .label,#mermaid-svg-az8wsaRRCf9lT4ne .icon-shape .label{text-align:center;}#mermaid-svg-az8wsaRRCf9lT4ne .node.clickable{cursor:pointer;}#mermaid-svg-az8wsaRRCf9lT4ne .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-az8wsaRRCf9lT4ne .arrowheadPath{fill:#333333;}#mermaid-svg-az8wsaRRCf9lT4ne .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-az8wsaRRCf9lT4ne .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-az8wsaRRCf9lT4ne .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-az8wsaRRCf9lT4ne .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-az8wsaRRCf9lT4ne .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-az8wsaRRCf9lT4ne .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-az8wsaRRCf9lT4ne .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-az8wsaRRCf9lT4ne .cluster text{fill:#333;}#mermaid-svg-az8wsaRRCf9lT4ne .cluster span{color:#333;}#mermaid-svg-az8wsaRRCf9lT4ne div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-az8wsaRRCf9lT4ne .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-az8wsaRRCf9lT4ne rect.text{fill:none;stroke-width:0;}#mermaid-svg-az8wsaRRCf9lT4ne .icon-shape,#mermaid-svg-az8wsaRRCf9lT4ne .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-az8wsaRRCf9lT4ne .icon-shape p,#mermaid-svg-az8wsaRRCf9lT4ne .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-az8wsaRRCf9lT4ne .icon-shape .label rect,#mermaid-svg-az8wsaRRCf9lT4ne .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-az8wsaRRCf9lT4ne .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-az8wsaRRCf9lT4ne .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-az8wsaRRCf9lT4ne :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否

MySQL
openGauss
PostgreSQL
达梦/其他
需要切换数据库?
保持 database.type 不变

直接启动
目标库类型
type: mysql
type: opengauss
type: postgresql
type: dm

(需补充 db-dm.yml + mapper/dm)
核对 application-db-*.yml

连接信息 + 加密密码
重启应用

✅ 自动完成全部切换


八、扩展:新增一种数据库的步骤

得益于枚举化 + 占位符设计,新增一种数据库(如 OceanBase)是机械化的:
#mermaid-svg-VMpqBNL364wFeHFE{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VMpqBNL364wFeHFE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VMpqBNL364wFeHFE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VMpqBNL364wFeHFE .error-icon{fill:#552222;}#mermaid-svg-VMpqBNL364wFeHFE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VMpqBNL364wFeHFE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VMpqBNL364wFeHFE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VMpqBNL364wFeHFE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VMpqBNL364wFeHFE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VMpqBNL364wFeHFE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VMpqBNL364wFeHFE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VMpqBNL364wFeHFE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VMpqBNL364wFeHFE .marker.cross{stroke:#333333;}#mermaid-svg-VMpqBNL364wFeHFE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VMpqBNL364wFeHFE p{margin:0;}#mermaid-svg-VMpqBNL364wFeHFE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VMpqBNL364wFeHFE .cluster-label text{fill:#333;}#mermaid-svg-VMpqBNL364wFeHFE .cluster-label span{color:#333;}#mermaid-svg-VMpqBNL364wFeHFE .cluster-label span p{background-color:transparent;}#mermaid-svg-VMpqBNL364wFeHFE .label text,#mermaid-svg-VMpqBNL364wFeHFE span{fill:#333;color:#333;}#mermaid-svg-VMpqBNL364wFeHFE .node rect,#mermaid-svg-VMpqBNL364wFeHFE .node circle,#mermaid-svg-VMpqBNL364wFeHFE .node ellipse,#mermaid-svg-VMpqBNL364wFeHFE .node polygon,#mermaid-svg-VMpqBNL364wFeHFE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VMpqBNL364wFeHFE .rough-node .label text,#mermaid-svg-VMpqBNL364wFeHFE .node .label text,#mermaid-svg-VMpqBNL364wFeHFE .image-shape .label,#mermaid-svg-VMpqBNL364wFeHFE .icon-shape .label{text-anchor:middle;}#mermaid-svg-VMpqBNL364wFeHFE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VMpqBNL364wFeHFE .rough-node .label,#mermaid-svg-VMpqBNL364wFeHFE .node .label,#mermaid-svg-VMpqBNL364wFeHFE .image-shape .label,#mermaid-svg-VMpqBNL364wFeHFE .icon-shape .label{text-align:center;}#mermaid-svg-VMpqBNL364wFeHFE .node.clickable{cursor:pointer;}#mermaid-svg-VMpqBNL364wFeHFE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VMpqBNL364wFeHFE .arrowheadPath{fill:#333333;}#mermaid-svg-VMpqBNL364wFeHFE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VMpqBNL364wFeHFE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VMpqBNL364wFeHFE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VMpqBNL364wFeHFE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VMpqBNL364wFeHFE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VMpqBNL364wFeHFE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VMpqBNL364wFeHFE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VMpqBNL364wFeHFE .cluster text{fill:#333;}#mermaid-svg-VMpqBNL364wFeHFE .cluster span{color:#333;}#mermaid-svg-VMpqBNL364wFeHFE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VMpqBNL364wFeHFE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VMpqBNL364wFeHFE rect.text{fill:none;stroke-width:0;}#mermaid-svg-VMpqBNL364wFeHFE .icon-shape,#mermaid-svg-VMpqBNL364wFeHFE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VMpqBNL364wFeHFE .icon-shape p,#mermaid-svg-VMpqBNL364wFeHFE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VMpqBNL364wFeHFE .icon-shape .label rect,#mermaid-svg-VMpqBNL364wFeHFE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VMpqBNL364wFeHFE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VMpqBNL364wFeHFE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VMpqBNL364wFeHFE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1.DatabaseType

新增枚举常量

(驱动/URL模板/方言)
2.新增

application-db-oceanbase.yml

(连接信息)
3.application.yml

profiles.group 增加映射

  • database.dialect 增加映射
    4.新增 mapper/oceanbase/

目录的 Mapper XML
5.放建表脚本

(可选 quartz 脚本)
✅ 切换 database.type=oceanbase 即可启用

步骤 文件 改动内容
1 DatabaseType.java 加一行枚举常量(驱动/URL模板/验证SQL/方言)
2 application-db-oceanbase.yml 新建,填 host/port/url/账号/密码
3 application.yml group 加 oceanbase: db-oceanbase;dialect 加映射
4 mapper/oceanbase/** 复制现有 Mapper XML,改写为该库方言 SQL
5 建表脚本 提供初始化 SQL(参考现有 sql/ 目录)

业务 Service / Controller / Mapper 接口零改动------这是该架构最大的价值。


九、关键源码走读(索引)

关注点 文件路径 关键点
唯一开关 admin/.../application.yml:52 database.type
Profile 分组 admin/.../application.yml:79-86 profiles.active + group
Mapper 占位符 admin/.../application.yml:137 mapper/${database.type}/**
方言嵌套占位符 admin/.../application.yml:144 ${database.dialect.${database.type}}
各库连接信息 admin/.../application-db-*.yml host/port/url/账号/密码
连接池参数 admin/.../application-druid.yml initialSize/maxActive/监控台
类型元数据 common/.../enums/DatabaseType.java 枚举集中封装
统一属性出口 framework/.../config/properties/DatabaseProperties.java 绑定 + 自动推导
数据源装配 framework/.../config/DruidConfig.java master/slave/dynamic 三个 Bean
路由数据源 framework/.../datasource/DynamicDataSource.java determineCurrentLookupKey
线程上下文 framework/.../datasource/DynamicDataSourceContextHolder.java ThreadLocal
主从切面 framework/.../aspectj/DataSourceAspect.java @Around + finally 清理
切换注解 common/.../annotation/DataSource.java MASTER/SLAVE
密码加密 admin/.../config/JasyptConfig.java ENC() 解密

十、设计亮点与小结

10.1 架构亮点

#mermaid-svg-slEmvNUVJXuUaibE{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-slEmvNUVJXuUaibE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-slEmvNUVJXuUaibE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-slEmvNUVJXuUaibE .error-icon{fill:#552222;}#mermaid-svg-slEmvNUVJXuUaibE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-slEmvNUVJXuUaibE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-slEmvNUVJXuUaibE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-slEmvNUVJXuUaibE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-slEmvNUVJXuUaibE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-slEmvNUVJXuUaibE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-slEmvNUVJXuUaibE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-slEmvNUVJXuUaibE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-slEmvNUVJXuUaibE .marker.cross{stroke:#333333;}#mermaid-svg-slEmvNUVJXuUaibE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-slEmvNUVJXuUaibE p{margin:0;}#mermaid-svg-slEmvNUVJXuUaibE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-slEmvNUVJXuUaibE .cluster-label text{fill:#333;}#mermaid-svg-slEmvNUVJXuUaibE .cluster-label span{color:#333;}#mermaid-svg-slEmvNUVJXuUaibE .cluster-label span p{background-color:transparent;}#mermaid-svg-slEmvNUVJXuUaibE .label text,#mermaid-svg-slEmvNUVJXuUaibE span{fill:#333;color:#333;}#mermaid-svg-slEmvNUVJXuUaibE .node rect,#mermaid-svg-slEmvNUVJXuUaibE .node circle,#mermaid-svg-slEmvNUVJXuUaibE .node ellipse,#mermaid-svg-slEmvNUVJXuUaibE .node polygon,#mermaid-svg-slEmvNUVJXuUaibE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-slEmvNUVJXuUaibE .rough-node .label text,#mermaid-svg-slEmvNUVJXuUaibE .node .label text,#mermaid-svg-slEmvNUVJXuUaibE .image-shape .label,#mermaid-svg-slEmvNUVJXuUaibE .icon-shape .label{text-anchor:middle;}#mermaid-svg-slEmvNUVJXuUaibE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-slEmvNUVJXuUaibE .rough-node .label,#mermaid-svg-slEmvNUVJXuUaibE .node .label,#mermaid-svg-slEmvNUVJXuUaibE .image-shape .label,#mermaid-svg-slEmvNUVJXuUaibE .icon-shape .label{text-align:center;}#mermaid-svg-slEmvNUVJXuUaibE .node.clickable{cursor:pointer;}#mermaid-svg-slEmvNUVJXuUaibE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-slEmvNUVJXuUaibE .arrowheadPath{fill:#333333;}#mermaid-svg-slEmvNUVJXuUaibE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-slEmvNUVJXuUaibE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-slEmvNUVJXuUaibE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-slEmvNUVJXuUaibE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-slEmvNUVJXuUaibE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-slEmvNUVJXuUaibE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-slEmvNUVJXuUaibE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-slEmvNUVJXuUaibE .cluster text{fill:#333;}#mermaid-svg-slEmvNUVJXuUaibE .cluster span{color:#333;}#mermaid-svg-slEmvNUVJXuUaibE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-slEmvNUVJXuUaibE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-slEmvNUVJXuUaibE rect.text{fill:none;stroke-width:0;}#mermaid-svg-slEmvNUVJXuUaibE .icon-shape,#mermaid-svg-slEmvNUVJXuUaibE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-slEmvNUVJXuUaibE .icon-shape p,#mermaid-svg-slEmvNUVJXuUaibE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-slEmvNUVJXuUaibE .icon-shape .label rect,#mermaid-svg-slEmvNUVJXuUaibE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-slEmvNUVJXuUaibE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-slEmvNUVJXuUaibE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-slEmvNUVJXuUaibE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ✅ 单一开关

database.type 一处修改
✅ 配置即路由

Profile+占位符 零硬编码
✅ 元数据集中

枚举即数据库字典
✅ 业务零侵入

Service/Controller 不感知库类型
✅ 可扩展

新增库=加枚举+加文件
✅ 双层切换

类型切换+主从分离 各司其职

10.2 核心总结

  1. 一个开关database.type 是全项目唯一的数据库切换入口。
  2. 两个桥梁Profile 分组 (业务名↔物理文件)、属性占位符(配置↔代码)把开关值传导到各处。
  3. 三层抽象DatabaseType(元数据)→ DatabaseProperties(配置出口)→ DynamicDataSource(运行路由)。
  4. 四类资源自动切换:连接信息、Mapper XML、分页方言、(可选)建表脚本。
  5. 两层切换分离:启动期「类型切换」与运行期「主从分离」职责清晰,互不耦合。
  6. 国产化友好:openGauss/Kingbase 复用 PG 方言、达梦复用 Oracle 方言,适配成本极低。

一句话概括 :以 database.type 为单一事实源,用 Spring Profile 分组与属性占位符做「配置路由」,用 AbstractRoutingDataSource 做「连接路由」,用分目录 Mapper + PageHelper 方言做「SQL 路由」------三路由合一,实现「一处配置,多库通用」。