Hibernate中实现记录插入或更新的正确方式及代码合理性验证
JPA EntityManager 插入或更新功能的代码分析
同事在事务中实现了实体的“插入或更新”功能(记录存在则更新,不存在则创建),原代码如下:
EntityManager em = ...; public void saveMyEntity (MyEntity entity) { em.persist(em.contains(entity) ? entity : em.merge(entity)); }
原代码存在的问题
- 重复执行存在性检查:
em.contains()的逻辑和merge()内部的存在检查重复,没必要额外判断 - 持久化已托管状态实体无意义:如果
EntityManager已经包含该实体(即处于托管状态),调用persist()完全多余,托管状态实体在事务提交时会自动同步到数据库 - merge后调用persist毫无作用:
merge()会返回托管状态的实体实例,此时再对其调用persist()没有任何效果,甚至可能引发异常 - 未覆盖数据库中存在但EntityManager未托管的情况:
em.contains()只检查当前EntityManager上下文是否存在该实体,无法检测数据库中已有但未被加载到当前上下文的同ID实体 - 未返回合并后的实例:
merge()返回的是托管状态的实例,原代码没有返回该实例,调用方无法获取正确的托管对象进行后续操作
优化后的代码验证
针对需求,以下代码完全可以实现相同的“插入或更新”功能:
public MyEntity saveMyEntity (MyEntity entity) { return em.merge(entity); }
结合补充场景说明:
- 对于通过
em.createNativeQuery查询后更新的实体:这类实体如果是托管状态,merge()会直接返回该实体;如果是游离状态(比如查询时未设置结果为托管),merge()会将其状态同步到数据库,同时返回托管状态实例 - 对于新创建的
MyEntity实例:如果实例未设置ID(或ID不存在于数据库),merge()会执行插入操作;如果设置了已存在的ID,则执行更新操作
内容的提问来源于stack exchange,提问作者Florian F
相关产品推荐
相关产品推荐

