学习 ORM(JPA/Hibernate)的“收益”

📅 2026/6/23 12:49:21
学习 ORM(JPA/Hibernate)的“收益”
你学了整套 ORM 体系换来了什么ORM 的宣传口径是你只需要操作 Java 对象框架自动帮你生成 SQL、管理关系、处理缓存。你不需要写 SQL不需要关心数据库细节。这套说辞听起来很美但代价是什么下面这张表列出了你为了“不用写 SQL”而需要学习的全部内容。一、为了“把对象映射成表”你学到的收益ORM 概念它的作用Entity告诉框架这个类是实体Table指定对应的数据库表名Column指定字段映射列名、长度、是否可空Id标记主键字段GeneratedValue指定主键生成策略AUTO、IDENTITY、SEQUENCE、TABLETransient标记非持久化字段Enumerated枚举类型映射ORDINAL / STRINGTemporal日期类型映射DATE / TIME / TIMESTAMPLob大字段映射BLOB / CLOBVersion乐观锁版本号字段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关联预加载为了解决 N1WHERE 对象属性路径条件查询用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查询构建器入口CriteriaQueryT定义查询返回类型RootT查询根节点JoinZ, X关联查询Predicate条件组合AND / OR / NOTPath属性路径访问Order排序条件Expression表达式算术运算、函数调用Subquery子查询构造Metamodel静态元模型需要代码生成器你学完这套 Criteria API写出来的代码长这样CriteriaBuildercbentityManager.getCriteriaBuilder();CriteriaQueryUserquerycb.createQuery(User.class);RootUseruserquery.from(User.class);JoinUser,Orderorderuser.join(orders);Predicatepredicatecb.and(cb.equal(user.get(name),张三),cb.greaterThan(order.get(amount),100));query.select(user).where(predicate).orderBy(cb.desc(user.get(createTime)));ListUserresultentityManager.createQuery(query).getResultList();五、为了“理解框架行为”你学到的收益ORM 概念它的作用一级缓存Persistence Context同一个 EntityManager 内的对象缓存自动管理二级缓存跨 EntityManager 的缓存需额外配置查询缓存查询结果缓存需配合二级缓存使用脏检查机制框架自动检测托管对象的变化并生成 UPDATE延迟加载访问关联对象时才触发查询否则是代理对象关联加载策略LAZY vs EAGER选错了就是 N1会话管理什么时候打开 EntityManager什么时候关闭托管状态 / 游离状态 / 分离状态 / 删除状态四种状态的区分以及状态转换规则乐观锁实现机制Version注解 版本号检查事务传播行为需要配合 Spring 事务管理Transactional的各种属性方言Dialect不同数据库的 SQL 方言差异需要选择正确的方言类六、为了“处理复杂场景”你学到的收益ORM 概念它的作用NamedQuery预定义 JPQL 查询启动时解析NamedNativeQuery预定义原生 SQL 查询SqlResultSetMapping原生 SQL 结果映射到实体ConstructorResult原生 SQL 结果映射到 VOEntityGraph动态控制关联加载图NamedEntityGraph预定义关联加载图QuerySpring Data JPA在 Repository 接口上写 JPQL 或原生 SQLModifying标记 UPDATE / DELETE 操作Transactional事务边界控制其实跟 JPA 无关是 Spring 的能力PersistenceUnitUtil工具类判断实体状态EntityManager.createNativeQuery()你终于退回了原生 SQL这些“收益”的最终结果是什么你学完了20 个注解10 个关联关系配置一套完整的查询语言JPQL/HQL一套复杂的 Criteria APIN1 问题的识别与解决方案四种实体状态及转换规则一级缓存 / 二级缓存 / 查询缓存的区别与用法方言配置你以为换来了“不用写 SQL”。但事实是复杂联表查询你写不出 JPQL → 退回到createNativeQuery()窗口函数、递归 CTE、UNION → JPA 不支持 → 退回到createNativeQuery()报表统计 3 张表 JOIN → JPQL 写起来比 SQL 还难懂 → 退回到createNativeQuery()需要调优 SQL 时框架生成的那坨 SQL 你自己都看不懂 → 退回到createNativeQuery()你学了一大套 ORM 体系最终在复杂场景下它只是你退回原生 SQL 路上的一个减速带。破MyBatis 至少还让你写 SQLJPA 连让你写 SQL 都要绕一圈MyBatis 的缺点是 XML 太啰嗦但它至少让你直接写 SQL。你的 SQL 能力是完整的。JPA 的问题是你学了一套框架但它主动阉割了你的 SQL 能力。你本来能写 SQL但框架告诉你“用 JPQL”你用了之后发现 JPQL 只有 SQL 1/3 的能力。剩下 2/3 的能力你只能退回原生 SQL——但这时你发现你学的那套 JPQL 语法在复杂场景下毫无用处。你学了一套专有语法结果它在你最需要的时候用不上。那学它的“收益”在哪立SimpleDAOSimpleDAO 不做对象关系映射不管理对象状态不制造缓存不生成 SQL。你要查数据写 SQL调用list()你要联表写 SQL调用page()你要复杂报表写 SQL调用list()你要调优复制 SQL 到数据库直接跑你不需要学 JPQL、Criteria API、EntityManager、缓存策略、懒加载配置、四种状态转换、N1 解决方案、方言配置、EntityGraph、二级缓存配置……你只需要会 SQL就会用 SimpleDAO。因为它的核心能力只有一条执行你写的 SQL把结果映射成 Java 对象。就这么简单。没有任何隐藏的“收益”也没有任何隐藏的成本。开源地址核心框架https://gitee.com/gao_zhenzhong/simple-dao系统底座https://gitee.com/gao_zhenzhong/simple-dao-starter代码生成器https://gitee.com/gao_zhenzhong/simple-dao-coder实战案例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就会用它。它把“你不用学什么”当作核心收益。哪一个收益是真的你自己算账。