2026/8/14
场景说明
一个项目有两套实现:一套是 Java 微服务版(MyBatis-Plus + MySQL),另一套是 Go 单体版(database/sql + PostgreSQL)。两套系统业务逻辑相同,但数据库层的设计思路完全不同。
这种差异恰好提供了一个观察窗口:同一个业务需求,在"工程效率优先"和"安全性优先"两个方向上的决策差异。
一、核心差异
| 维度 | Java 版 | Go 版 |
|---|---|---|
| 数据库 | MySQL 5.7 | PostgreSQL 16 |
| 数据访问层 | MyBatis-Plus (ORM) | database/sql (手写 SQL) |
| 查询方式 | 自动生成 CRUD + 手写 XML | 全部手写 $1 参数化查询 |
| 迁移工具 | Flyway | 无(手动跑 SQL) |
| 类型安全 | 弱(${} 字符串拼接) |
强(只有 $N 参数化) |
二、安全的本质差异
Go 的 database/sql 只提供 $1、$2 参数化查询这一种传参方式,没有 ${} 拼接的逃生门 。开发者不可能写出 SQL 注入代码------不是因为开发者更小心,而是因为没有不安全的选项。
MyBatis 提供两种参数绑定:
-
#{}--- 安全,参数化 -
${}--- 危险,裸字符串拼接,用于动态表名/ORDER BY
Java 版代码里的确存在 ${} 拼接写法,虽然量少,但证明了:框架给了刀,就有人会用来砍人。
一个有意思的对比:MySQL 8.0 引入的 GROUP BY 顺序排列能避免 ORDER BY 注入,Go 的 database/sql 用参数化查询避免注入。两条路都能走到安全,但 Go 是"不留别的路",MySQL 是"告诉你哪条路危险"。两种策略都有效,但适用场景不同。
一个值得思考的问题: 如果 Go 的 database/sql 也提供一个 ExecRaw 方法允许裸字符串拼接,有多少开发者会在 ORDER BY 场景下使用它?会不会慢慢变成类似 MyBatis ${} 的使用习惯?这是语言设计层面的取舍------Go 选择不给这把刀,MyBatis 选择给了但告诉你小心用。
三、工程效率的取舍
MyBatis-Plus 的优势
-
自动生成 CRUD :
insert(T)、updateById(T)、selectList(Wrapper)零代码 -
Lambda 查询构造器 :
lambdaQuery().eq(User::getName, "test").list() -
分页插件:一行代码搞定分页
-
代码生成器:表结构 → Entity/Mapper/Service/Controller 全自动
Go database/sql 的代价
-
模板代码多 :每个查询都要写
rows.Scan(&a, &b, &c) -
无自动映射:struct 字段和 SQL 列必须手动对应
-
分页手写 :
LIMIT $1 OFFSET $2每页都要写
量化对比
对一个典型的 CRUD 业务(5 张表,每表 10 个查询):
| 活动 | MyBatis-Plus | Go database/sql |
|---|---|---|
| 建表 → 跑起来 | 30 分钟(代码生成器) | 2 小时(手写全部) |
| 加一个字段 | 5 分钟 | 5 分钟 |
| 排查 SQL 问题 | 快(XML 集中) | 中(SQL 散在代码里) |
| 新人上手 | 慢(要学 MyBatis 概念) | 快(就是 SQL) |
四、为什么 Java 版用 MyBatis-Plus?
这里有一个值得观察的现象:国内 Java 后端项目中 MyBatis-Plus 的普及率极高。但这不完全是技术选择,更多是生态选择。
-
国内 Java 社区的习惯:MyBatis-Plus 在国内 Java 开发中的普及率接近 100%,招人成本最低
-
微服务架构的写法统一:8 个服务,每个服务独立数据库,用 ORM 统一 CRUD 写法
-
历史惯性:项目从单体到微服务一路用 MyBatis,没有换的理由
Java 版的 shardingsphere-jdbc 分库分表方案目前只支持 MyBatis 和 JPA 等主流 ORM,Go 版用的是 pgx 原生驱动 + 手动路由,分库分表逻辑都在应用层。两者架构层面的差异直接决定了数据访问层的设计取舍------如果用 Go 版做分库分表,需要额外实现路由逻辑,这是手写 SQL 方案的额外成本。
这不是 Java 比 Go 好,是国内 Java 的轮子已经成熟到形成生态了。
五、对 Go 版的务实建议
必需
| 项 | 建议 |
|---|---|
| 迁移工具 | 加 golang-migrate,启动时自动跑未执行的 SQL |
暂不建议
| 项 | 理由 |
|---|---|
| 换 ORM(GORM) | 项目 27K 行,全部手写 SQL,换 ORM 等于重写 |
| 加 sqlx | 薄封装,省 rows.Scan 模板代码。可加但非必需 |
| 加 query builder | 手写 SQL 就是最好的 DSL |
手写 SQL + 参数化查询 = 安全 + 清晰,当前架构没有换的理由。
六、总结
| 方案 | 适合场景 | 代价 |
|---|---|---|
| MyBatis-Plus | 快速交付、团队熟悉 ORM、微服务多表 | ${} 的安全隐患、学习成本 |
database/sql |
安全敏感、SQL 精细控制、团队熟悉 SQL | 模板代码多、无自动映射 |
两种选择都没有对错,是不同约束条件下的合理选择。
数据访问层的核心目标不是"用什么框架",而是安全读写数据、可维护、让开发者舒服地写业务。不同的团队、不同的项目阶段、不同的安全要求,会指向不同的答案。