数据库层设计的取舍:ORM 便利性与手写 SQL 的安全性权衡

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 的优势

  1. 自动生成 CRUDinsert(T)updateById(T)selectList(Wrapper) 零代码

  2. Lambda 查询构造器lambdaQuery().eq(User::getName, "test").list()

  3. 分页插件:一行代码搞定分页

  4. 代码生成器:表结构 → Entity/Mapper/Service/Controller 全自动

Go database/sql 的代价

  1. 模板代码多 :每个查询都要写 rows.Scan(&a, &b, &c)

  2. 无自动映射:struct 字段和 SQL 列必须手动对应

  3. 分页手写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 模板代码多、无自动映射

两种选择都没有对错,是不同约束条件下的合理选择。

数据访问层的核心目标不是"用什么框架",而是安全读写数据、可维护、让开发者舒服地写业务。不同的团队、不同的项目阶段、不同的安全要求,会指向不同的答案。

相关推荐
XuCoder17 分钟前
你更新的数据明明还在内存里,可 MySQL 重启后凭什么没丢?
数据库
小江的记录本19 分钟前
【ORM 框架】MyBatis-Plus 核心特性、条件构造器、分页插件、乐观锁插件(附《思维导图》、《问题排查与实践清单》和《面试高频考点汇总》)
java·windows·spring boot·python·spring·面试·mybatis
clorinda20 分钟前
SQL 快速入门:题目单知识点精炼总结
java·数据库·sql
AI人工智能+电脑小能手30 分钟前
大白话说Java设计模式-40-备忘录模式(业务实战篇)
java·设计模式·事务回滚·备忘录模式·状态保存·spring transactional·购物车快照
SimonKing30 分钟前
白嫖国产多模态大模型:商汤 SenseNova 接入指南
java·后端·程序员
小江的记录本30 分钟前
【ORM框架】MyBatis核心原理、ORM思想、MyBatis vs JPA
java·数据库·后端·spring·spring cloud·oracle·mybatis
zgl_2005377936 分钟前
源代码:跨数据库通用“字段级”数据血缘解析与图形化(2/3:标注信息的拆解、检验、保存)
大数据·数据库·数据仓库·sql·数据挖掘·etl·嵌入式实时数据库
_明月42 分钟前
汇川技术股份有限公司面试--Java外包岗
java·面试·职场和发展
BD_Marathon43 分钟前
Hadoop常用端口号
java·大数据·hadoop