数据源切换架构详解
本篇深入剖析 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.type → DatabaseType 枚举 |
自动推导驱动类、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 时,用的是裸的
mysql(mapper/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创建,而「连接池参数(大小/超时/检测)」由DruidProperties从application-druid.yml注入后统一套用。application-druid.yml中spring.datasource.druid.master.*段为兼容保留,实际连接信息以application-db-*.yml为准。
连接池参数来源对照
| 参数类别 | 来源文件 | 注入方式 |
|---|---|---|
| url / username / password / driver | application-db-<type>.yml |
DatabaseProperties → DruidConfig 手动 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.yml:database.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 核心总结
- 一个开关 :
database.type是全项目唯一的数据库切换入口。 - 两个桥梁 :Profile 分组 (业务名↔物理文件)、属性占位符(配置↔代码)把开关值传导到各处。
- 三层抽象 :
DatabaseType(元数据)→DatabaseProperties(配置出口)→DynamicDataSource(运行路由)。 - 四类资源自动切换:连接信息、Mapper XML、分页方言、(可选)建表脚本。
- 两层切换分离:启动期「类型切换」与运行期「主从分离」职责清晰,互不耦合。
- 国产化友好:openGauss/Kingbase 复用 PG 方言、达梦复用 Oracle 方言,适配成本极低。
一句话概括 :以
database.type为单一事实源,用 Spring Profile 分组与属性占位符做「配置路由」,用AbstractRoutingDataSource做「连接路由」,用分目录 Mapper + PageHelper 方言做「SQL 路由」------三路由合一,实现「一处配置,多库通用」。