H2与MySQL双数据源-演示与生产的切换艺术

07-H2与MySQL双数据源-演示与生产的切换艺术

上回书说到 URM Ultra 选了 Spring Boot 3 + JPA + 单体按域分包。这回咱们聊一个新手最容易被劝退、却又最见功力的细节:数据库怎么连。

一句话先剧透:本项目默认用 H2 内存库跑 Demo——你下载下来,啥数据库都不用装,点一下启动,表自动建好、演示数据自动灌入,马上就能调接口;等真要上线了,改改 application.yml、执行一份 SQL 脚本,无缝切到 MySQL 8。

这叫"演示零依赖、生产可平滑"。今天咱把这套"切换艺术"彻底拆明白。


一、为什么需要"两套数据源"这种东西

先想一个问题:你写了个给同事演示的 Demo,难道还要让他先去装个 MySQL、建库建表、导数据?那演示还没开始,对方已经睡着了。

所以工程里常见的一种套路是:开发/演示用轻量数据库,生产用正经数据库。这就是"双数据源"的由来——不是一个程序同时连两个库,而是同一套代码,通过配置切换用哪个库。

本项目做了这样的角色分工:

场景用的库特点
本地跑、看效果、写接口H2 内存库零安装、启动即建表、自带演示数据
真上线、有持久化需求MySQL 8稳定、可多端共享、数据不丢

二、H2 是什么神仙数据库

H2 是一个纯 Java 写的关系型数据库,可以跑在内存里(memory),也可以落盘成文件。它最大的优点:不用单独安装一个数据库服务。Spring Boot 引入一个依赖,启动时就自带了一个数据库引擎。

对于 Demo 来说,这简直是天降福音——省去了"配环境两小时,写代码五分钟"的尴尬。


三、看懂那串"鬼画符"一样的连接串

打开 application.yml,数据源是这样配的(项目源码):

# 项目源码:application.yml(节选)
spring:
  datasource:
    url: jdbc:h2:mem:urm_ultra;MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;DB_CLOSE_DELAY=-1
    driver-class-name: org.h2.Driver
    username: sa
    password: ""

那一长串 url 看着吓人,其实每个分号后面都是一个小开关。我逐个翻译成人话:

片段含义为什么要写
jdbc:h2:用 H2 的 JDBC 驱动指定数据库类型
mem:urm_ultra内存模式,库名叫 urm_ultra数据存在内存,重启即清空
MODE=MySQL以 MySQL 兼容模式运行让 H2 的 SQL 语法、类型尽量贴近 MySQL,避免建表/查询出错
DATABASE_TO_LOWER=TRUE库名、表名转小写MySQL 在 Linux 下默认大小写敏感,统一小写防坑
CASE_INSENSITIVE_IDENTIFIERS=TRUE标识符大小写不敏感配合上一条,进一步减少跨库差异
DB_CLOSE_DELAY=-1连接全关也不销毁库防止测试时库被提前清空

重点圈一下 MODE=MySQL。这是这套"切换艺术"的灵魂开关——H2 故意装成 MySQL 的口音说话,这样 JPA 生成的建表语句、查询语句,在 H2 和 MySQL 上都能跑得通,切换时少踩语法差异的坑。

那内存库的"重启即清空"怎么办? 别慌,下面第四节的"自动建表 + 自动灌数据"就是答案。


四、ddl-auto=update:不用手写建表语句

传统开发,你得先 CREATE TABLE 把表建好。但 JPA 有个贴心设定——它可以根据实体类自动建表。配置长这样(项目源码):

# 项目源码:application.yml(节选)
  jpa:
    hibernate.ddl-auto: update
    open-in-view: false
    show-sql: false
    properties.hibernate.default_schema: PUBLIC

ddl-auto: update 的意思是:启动时,Hibernate 拿着你的 @Entity 类,比对数据库里有没有对应的表,没有就建,字段对不上就补。你改了实体加了个字段,重启它自动给你加上——全程不用手写一条 DDL。

常用取值我们对比一下:

取值行为适用
none不碰表结构生产常用(表由脚本管)
update有则改、无则建本项目演示用
create每次启动删了重建纯测试,数据会没
create-drop启动建、关闭删单元测试
validate只校验不改动怕表结构漂变的场景

警告:生产环境千万不要用 update。你可不希望哪天 JPA 自作主张把生产表字段改了。生产该用 none,表结构由 SQL 迁移脚本严格管起来(这点二次开发文档里强调过)。

open-in-view: false 这行也提一嘴:它关掉了"视图层还开着事务"的默认行为,能避免懒加载引发的 N+1 查询和长事务,是个好习惯开关。


五、DataInitializer:让 Demo 永远"有米下锅"

表是自动建了,但空库一个商品都没有,演示啥?这时候 DataInitializer 登场。它实现了 CommandLineRunner,意思是"Spring 容器起来后,自动跑一段初始化代码"。

项目里它的逻辑是(项目源码):

// 项目源码:如果商品表已非空,就不灌数据(避免重启重复)
if (productRepository.count() > 0) return;

然后它会一股脑塞进这些演示数据:

数据类别内容举例用途
类目饮料 / 零食 / 日用品(3 条)商品分类展示
商品可乐 8 元、矿泉水 3 元、薯片 7 元…(7 条,编码 P001~P007)货道售卖
设备DEV-0001 / DEV-0002 双门智能柜售货机本体
货道绑定10 条 slot 与商品、库存的绑定模拟真实货道
无人车VEH-0001/0002,电量 85/60物流执行
机械臂RBT-0001/0002,六轴上货/分拣
前置仓WH-0001 中央前置仓补货起点

也就是说,你啥都不用干,启动完直接 GET /app-api/device/list 就能拉到两台设备、七件商品。这才是"开箱即跑"的真实含义。

小坑提示:项目里判断"要不要初始化"只看了 Product 表是否为空。如果你手动把商品删了但没删其他表,可能出现数据不一致。Demo 阶段无所谓,生产化时要注意做整体校验。

顺带一提,H2 还贴心地开了个网页控制台:

  h2.console.enabled: true   # 访问路径 /h2-console

你本地启动后,浏览器开 http://localhost:8080/h2-console,用配置里的 sa 空密码就能连进去,直观地看表、查数据——调试神器。


六、切到 MySQL:就两步

轮子造好了,生产上车怎么办?其实就两步走。

第一步:执行建表脚本

项目里提供了一份 sql/urm_ultra.sql,共 15 张表,按域前缀划分(pms_ 商品、dms_ 设备、tos_ 订单、pss_ 支付、vec_ 车辆、rbt_ 机械臂、rep_ 补货配送)。风格是 InnoDB + utf8mb4 + 中文 COMMENT + 唯一键 uk_* 和关联索引 idx_*。

在 MySQL 里建好库、跑这份脚本,表结构就齐了(脚本不含种子数据,演示数据那套交给 H2,生产你自己导)。

第二步:改 application.yml

把 H2 那段换成 MySQL 的数据源(项目配置中预留了切换注释),核心就是把 url 指向你的 MySQL 实例,并切换驱动与方言相关项:

# 生产切换示意(项目源码注释形式提供)
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/urm_ultra?useUnicode=true&characterEncoding=utf8mb8&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: 你的账号
    password: 你的密码
  jpa:
    hibernate.ddl-auto: none   # 生产关掉自动建表!

改完重启,代码一行不改,底层库就从 H2 换成了 MySQL。这就是"双数据源切换艺术"——同一套 JPA 实体和 Repository,靠配置换引擎。


七、为什么要这么折腾,而不是直接用 MySQL

有同学问:反正最后都要 MySQL,一开始就用它不完了?

不行,原因有三:

  1. 降低他人上手门槛。别人想看你的 Demo,总不能先让他装 MySQL 吧?H2 让"克隆即运行"成为可能。
  2. 隔离演示数据与脏数据。内存库重启即空,你随便造数据、随便测,不污染任何东西。
  3. 验证"可移植性"。既然 H2 用 MySQL 兼容模式都能跑,说明你的表设计、SQL 写法没踩数据库私货,切真库更稳。

八、一张表记住切换要点

环节H2 演示MySQL 生产
依赖h2 (runtime)mysql-connector-j (runtime)
驱动org.h2.Drivercom.mysql.cj.jdbc.Driver
url 关键MODE=MySQL真实连接串
ddl-autoupdate(自动建表)none(脚本管表)
演示数据DataInitializer 自动灌自行导入
控制台/h2-console 可看用 Navicat 等客户端

九、黒漂小结

数据库切换这件事,本质是**“让对的人用对的库”**:新人、演示、测试用 H2 省心;上线、共享、持久用 MySQL 稳当。而 JPA + MODE=MySQL + ddl-auto 这套组合,正是把"切换成本"压到几乎为零的巧劲儿。

下一篇,我们要升级难度——聊聊这套系统对外暴露的 三类 API 是怎么隔离的(/admin-api、/app-api、/device-api),以及那个贯穿全场的统一返回体 Result 和分页 PageResult。接口设计好了,前端和设备端才能各忙各的、互不打扰。

(本篇完。文中"项目源码"标注处均可在 urm-server 工程的 application.yml、DataInitializer 与 sql/urm_ultra.sql 中对照。)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值