上周,开源中国一篇题为《放弃 MyBatis,拥抱新一代 Java 数据访问库》的文章火了。它推荐了一个名为 dbVisitor 的框架,号称能用“一套 API 访问所有数据库”。

看完我的第一反应是:又来?
并非 dbVisitor 设计得不好。它的思路清晰,技术实现扎实。但我不禁要问:我们真的还需要再造一个轮子吗?
越统一,越复杂
dbVisitor 的愿景看起来很美好:统一 MySQL、MongoDB、Elasticsearch、Redis 的访问方式。不再区分这是 ORM 还是 Client,所有数据访问都走一个入口。
听起来很棒,但仔细想想,这不就是另一个版本的“大一统”梦想吗?本意是为了省事,结果可能更累。
dbVisitor 本质上还是用一层抽象来屏蔽底层差异。但抽象是有代价的。当你需要使用 Elasticsearch 的聚合查询时,dbVisitor 的“统一 API”还能完美覆盖吗?当你需要 Redis 的原子操作时,恐怕还是得“穿透”到底层 SDK。作者自己也承认了这个问题:
“当统一 API 无法满足特殊需求时,dbVisitor 允许通过 unwrap 机制‘穿透’到底层驱动。”
所以这个“统一”其实是打了折扣的:简单场景可以统一,复杂场景还是得回到原点。
另一条路:把 ORM 做到极致
有人选择统一,有人选择深耕。
去年 Jimmer 这个框架挺火,号称“JVM 中最先进的 ORM”。它的思路和 dbVisitor 完全不同:不追求统一所有数据源,而是把关系型数据库的 ORM 做到极致。
Jimmer 确实尝试解决一些老大难问题。比如 N+1 查询?它用 SQL DSL 在编译期就规避了。对象图的保存?它能自动 diff,只更新变化的部分。实体类太臃肿?它用接口定义实体,编译时生成实现。
本来我和群里的大佬约了稿,想让他深入聊聊 Jimmer。但后来仔细想了想,觉得这个话题可能比较小众,毕竟是个新框架,用的人不多。所以就没继续写下去。
不过这不妨碍我们看看它的设计思路。
// Jimmer 的查询方式:编译期类型安全
List<Book> books = sqlClient
.createQuery(table)
.where(table.name().like("Java%"))
.select(
table.fetch(
Fetchers.BOOK_FETCHER
.allScalarFields()
.store(Fetchers.BOOK_STORE_FETCHER.name())
)
)
.execute();
Jimmer 解决的是“ORM 不够好”的问题,方案是“做一个更好的 ORM”。代价呢?你需要学习一套新的 DSL、一套新的注解、一套新的思维模型。得先花几天学会它,才能享受它的好处。
值不值?看情况。但我总觉得,方向可能值得商榷。
GraphQL 也是类似的路子。Spring 官方都出了 Spring for GraphQL,它解决的是“前端要什么数据,后端就给什么数据”的问题,实现精准投喂。
听起来很美好。但代价是什么?你得定义 Schema,写 Resolver,处理 N+1(对,GraphQL 也有 N+1 问题),还得学一套新的查询语法。前后端都得改造,工作量不小。
Jimmer 和 GraphQL,一个在 ORM 层做文章,一个在 API 层做文章。它们都在试图用更复杂的工具解决复杂的问题。
但我想问一个根本性问题:如果 SQL 本身不再是痛点了呢?
ORM 的老本,还能吃多久?
说句公道话,ORM 并非一无是处。
在工程化场景下,ORM 确实有它的价值。统一的 API 让团队协作更顺畅,代码风格一致,新人上手快。异构数据库切换?改个配置文件,理论上代码不用动。事务管理、连接池、缓存,这些脏活累活 ORM 都帮你包了。
十年前,这些都是实打实的优势。
但当 AI 抹平了 SQL 的编写门槛时,ORM 那些复杂的 API 映射,反而可能成了开发者和数据库之间多余的“中间商”。
- 工程化:AI 生成的 SQL 风格比人写的还统一。你让 Claude 写十条查询,它给你的代码风格一定是一致的。团队协作的问题?用 Skills 模板统一一下就行。
- 异构数据库切换:说实话,你这辈子换过几次数据库?从 MySQL 换到 PostgreSQL?大多数项目从立项到下线,数据库都没换过。为了一个“可能永远不会发生”的场景,背上 ORM 的学习成本和性能开销,值得吗?
- 事务和连接池:这些跟 ORM 本身关系不大。Spring Boot 的
@Transactional 注解配合 JdbcClient 一样能用,HikariCP 连接池也不挑客户端。
ORM 的传统优势,在 AI 时代正变得越来越不明显。
JdbcClient:轻量才是王道
说到这里,就不得不提 Spring Boot 3.2 引入的 JdbcClient。
JdbcClient 是 Spring 团队对数据访问做的一次“减法”尝试。它不是 ORM,甚至算不上框架,就是一个简洁的 JDBC 封装。来看个对比:
// MyBatis 方式:需要定义 Mapper 接口 + XML 或注解
@Mapper
public interface UserMapper {
@Select("SELECT * FROM sys_user WHERE username = #{username}")
SysUser findByUsername(String username);
}
// 调用
SysUser user = userMapper.findByUsername("lengleng");
// JdbcClient 方式:一行搞定
SysUser user = jdbcClient
.sql("SELECT * FROM sys_user WHERE username = :username")
.param("username", "lengleng")
.query(SysUser.class)
.optional()
.orElse(null);
看起来代码量差不多?差别在细节。
JdbcClient 不需要额外定义 Mapper 接口,少一个文件就少一份心智负担。SQL 直接写在调用的地方,不用在 XML 和 Java 代码之间跳来跳去。链式 API 写起来顺手,IDE 提示也跟得上。最关键的是——AI 生成的 SQL,直接贴进来就能跑。
在 AI 辅助编程(Vibe Coding)的场景下,这个优势太明显了。你让 AI 写一段查询逻辑,它给你的就是原生 SQL。用 JdbcClient?无缝接入。用 MyBatis?还得把 SQL 搬到 XML 里,再写个 Mapper 接口。
多了一道工序,开发体验就差了一截。
在 AI 时代,最好的 ORM 可能就是没有 ORM。 最好的代码不是写出来的,而是不用写的。

总结
无论是追求大一统的 dbVisitor,还是追求极致的 Jimmer,亦或是 API 层的 GraphQL,它们都在各自的维度上试图解决数据访问的复杂性。然而,当 AI 能够高效、准确地生成原生 SQL 时,这些复杂抽象层的价值就需要重新评估了。
JdbcClient 代表的是一种回归简洁的思路:把工具做轻,把选择权还给开发者。在 云栈社区 的许多技术讨论中,我们也看到越来越多的开发者开始重新审视“简单直接”的价值。或许,在 AI 赋能的新开发范式下,轻装上阵、直面 SQL,会是更高效的选择。你怎么看呢?