你学了整套 ORM 体系,换来了什么?

ORM 的宣传口径是:你只需要操作 Java 对象,框架自动帮你生成 SQL、管理关系、处理缓存。你不需要写 SQL,不需要关心数据库细节。

这套说辞听起来很美,但代价是什么?

下面这张表,列出了你为了“不用写 SQL”而需要学习的全部内容。

一、为了“把对象映射成表”,你学到的收益

ORM 概念它的作用
@Entity告诉框架这个类是实体
@Table指定对应的数据库表名
@Column指定字段映射(列名、长度、是否可空)
@Id标记主键字段
@GeneratedValue指定主键生成策略(AUTO、IDENTITY、SEQUENCE、TABLE)
@Transient标记非持久化字段
@Enumerated枚举类型映射(ORDINAL / STRING)
@Temporal日期类型映射(DATE / TIME / TIMESTAMP)
@Lob大字段映射(BLOB / CLOB)
@Version乐观锁版本号字段
@Embeddable / @Embedded嵌入对象映射
@EmbeddedId复合主键
@MappedSuperclass父类字段继承

二、为了“关联其他表”,你学到的收益

ORM 概念它的作用
@OneToOne一对一关联
@OneToMany一对多关联
@ManyToOne多对一关联
@ManyToMany多对多关联
@JoinColumn指定外键列名
@JoinTable多对多中间表配置
@OrderBy关联集合的排序方式
mappedBy双向关联的对方字段
fetch = LAZY / EAGER关联加载时机
cascade = ALL / PERSIST / MERGE / REMOVE / REFRESH / DETACH级联操作类型
orphanRemoval = true / false孤儿对象删除策略

三、为了“操作数据”,你学到的收益

ORM 概念它的作用
EntityManagerJPA 核心操作接口
EntityManagerFactoryEntityManager 工厂
EntityTransaction事务管理
persist()保存实体
merge()合并实体(更新)
remove()删除实体
find()按主键查询
getReference()懒加载引用查询
flush()立即同步到数据库
detach()从持久上下文分离
clear()清空持久上下文
contains()判断是否托管

四、为了“查询数据”,你学到的收益

HQL / JPQL(一门全新的查询语言)
概念它的作用
SELECT ... FROM查询实体(注意:FROM 后面是实体名,不是表名)
JOIN FETCH关联预加载(为了解决 N+1)
WHERE + 对象属性路径条件查询(用 u.name 而不是 u.name 的 SQL 字段名)
GROUP BY / HAVING分组聚合
ORDER BY排序
NEW 构造器表达式直接构造 VO 对象
子查询支持但不完整
聚合函数支持 COUNT、SUM、AVG、MAX、MIN,但有限制
窗口函数不支持
递归 CTE不支持
UNION不支持
数据库特有函数(DATE_FORMAT、EXTRACT、JSON_EXTRACT 等)不支持,或需注册方言
Criteria API(一套更“类型安全”的查询构造器)
概念它的作用
CriteriaBuilder查询构建器入口
CriteriaQuery<T>定义查询返回类型
Root<T>查询根节点
Join<Z, X>关联查询
Predicate条件组合(AND / OR / NOT)
Path属性路径访问
Order排序条件
Expression表达式(算术运算、函数调用)
Subquery子查询构造
Metamodel静态元模型(需要代码生成器)

你学完这套 Criteria API,写出来的代码长这样:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> user = query.from(User.class);
Join<User, Order> order = user.join("orders");
Predicate predicate = cb.and(
    cb.equal(user.get("name"), "张三"),
    cb.greaterThan(order.get("amount"), 100)
);
query.select(user).where(predicate).orderBy(cb.desc(user.get("createTime")));
List<User> result = entityManager.createQuery(query).getResultList();

五、为了“理解框架行为”,你学到的收益

ORM 概念它的作用
一级缓存(Persistence Context)同一个 EntityManager 内的对象缓存,自动管理
二级缓存跨 EntityManager 的缓存,需额外配置
查询缓存查询结果缓存,需配合二级缓存使用
脏检查机制框架自动检测托管对象的变化并生成 UPDATE
延迟加载访问关联对象时才触发查询,否则是代理对象
关联加载策略LAZY vs EAGER,选错了就是 N+1
会话管理什么时候打开 EntityManager,什么时候关闭
托管状态 / 游离状态 / 分离状态 / 删除状态四种状态的区分,以及状态转换规则
乐观锁实现机制@Version 注解 + 版本号检查
事务传播行为需要配合 Spring 事务管理(@Transactional 的各种属性)
方言(Dialect)不同数据库的 SQL 方言差异,需要选择正确的方言类

六、为了“处理复杂场景”,你学到的收益

ORM 概念它的作用
@NamedQuery预定义 JPQL 查询,启动时解析
@NamedNativeQuery预定义原生 SQL 查询
@SqlResultSetMapping原生 SQL 结果映射到实体
@ConstructorResult原生 SQL 结果映射到 VO
@EntityGraph动态控制关联加载图
@NamedEntityGraph预定义关联加载图
@Query(Spring Data JPA)在 Repository 接口上写 JPQL 或原生 SQL
@Modifying标记 UPDATE / DELETE 操作
@Transactional事务边界控制(其实跟 JPA 无关,是 Spring 的能力)
PersistenceUnitUtil工具类,判断实体状态
EntityManager.createNativeQuery()你终于退回了原生 SQL

这些“收益”的最终结果是什么?

你学完了:

  • 20+ 个注解
  • 10+ 个关联关系配置
  • 一套完整的查询语言(JPQL/HQL)
  • 一套复杂的 Criteria API
  • N+1 问题的识别与解决方案
  • 四种实体状态及转换规则
  • 一级缓存 / 二级缓存 / 查询缓存的区别与用法
  • 方言配置

你以为换来了“不用写 SQL”。

但事实是:

  • 复杂联表查询你写不出 JPQL → 退回到 createNativeQuery()
  • 窗口函数、递归 CTE、UNION → JPA 不支持 → 退回到 createNativeQuery()
  • 报表统计 > 3 张表 JOIN → JPQL 写起来比 SQL 还难懂 → 退回到 createNativeQuery()
  • 需要调优 SQL 时,框架生成的那坨 SQL 你自己都看不懂 → 退回到 createNativeQuery()

你学了一大套 ORM 体系,最终在复杂场景下,它只是你退回原生 SQL 路上的一个减速带。

破:MyBatis 至少还让你写 SQL,JPA 连让你写 SQL 都要绕一圈

MyBatis 的缺点是 XML 太啰嗦,但它至少让你直接写 SQL。你的 SQL 能力是完整的。

JPA 的问题是:你学了一套框架,但它主动阉割了你的 SQL 能力。你本来能写 SQL,但框架告诉你“用 JPQL”,你用了之后发现 JPQL 只有 SQL 1/3 的能力。剩下 2/3 的能力,你只能退回原生 SQL——但这时你发现,你学的那套 JPQL 语法在复杂场景下毫无用处。

你学了一套专有语法,结果它在你最需要的时候用不上。那学它的“收益”在哪?

立:SimpleDAO

SimpleDAO 不做对象关系映射,不管理对象状态,不制造缓存,不生成 SQL。

  • 你要查数据?写 SQL,调用 list()
  • 你要联表?写 SQL,调用 page()
  • 你要复杂报表?写 SQL,调用 list()
  • 你要调优?复制 SQL 到数据库直接跑

你不需要学 JPQL、Criteria API、EntityManager、缓存策略、懒加载配置、四种状态转换、N+1 解决方案、方言配置、EntityGraph、二级缓存配置……

你只需要会 SQL,就会用 SimpleDAO。

因为它的核心能力只有一条:执行你写的 SQL,把结果映射成 Java 对象。

就这么简单。没有任何隐藏的“收益”,也没有任何隐藏的成本。

开源地址

  1. 核心框架:https://gitee.com/gao_zhenzhong/simple-dao
  2. 系统底座:https://gitee.com/gao_zhenzhong/simple-dao-starter
  3. 代码生成器:https://gitee.com/gao_zhenzhong/simple-dao-coder
  4. 实战案例:https://gitee.com/gao_zhenzhong/simple-dao-demo

写在最后

MyBatis 让你学了几十个标签、上百个属性、十几个注解、31 个异常,最终换来的是“在 XML 里拼一个字符串”。

JPA 让你学了 20+ 个注解、10+ 个关联关系、一套 JPQL 语法、一套 Criteria API、四种状态转换、三种缓存策略、一套方言体系……最终换来的是“写了复杂查询时,还得退回到原生 SQL”。

但是,我们也要承认一个事实:单表对象化确实是一个真实的收益。

在单表 CRUD 的场景下,JPA 和 Hibernate 确实能帮你省掉写 INSERT、UPDATE、DELETE 和按主键 SELECT 的 SQL。你不用写 INSERT INTO user (name, age) VALUES (?, ?),直接 user.setName(“张三”) 然后 entityManager.persist(user) 就行。

这个收益是真实的,不是带引号的。

问题是:单表 CRUD 只是企业持久层开发的一小部分——远不到 10%。

剩下的 90%(多表联查、子查询、聚合报表、批量更新、跨表条件筛选……),才是你真正要面对的业务场景。而这些场景下,JPA 帮不了你,你只能退回到原生 SQL。

所以,单表对象化是一个真实的收益,但它覆盖的只是业务中很小的一部分。用一个覆盖 10% 场景的收益,换取 90% 场景下的复杂度和限制,这笔账怎么算,你自己权衡。

两个方向,殊途同归。

SimpleDAO 让你什么都不用多学,你只需要会 SQL,就会用它。

它把“你不用学什么”当作核心收益。

哪一个收益是真的,你自己算账。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐