迁移Hibernate至5.2改用EntityManager后session.get无法加载持久化对象
解决Hibernate 5.2迁移后EntityManager替代SessionFactory时关联数据加载失败的问题
哥们,我之前也踩过类似的迁移坑,结合你的场景给你梳理下可能的问题点和解决办法:
首先你的核心问题是:从Hibernate4+Spring4用SessionFactory+XML映射,迁移到Hibernate5+EntityManager+JPA注解后,保存Post和关联评论,再加载Post时拿不到评论数据。
1. 先检查JPA关联注解的配置
你从XML转注解的时候,很容易在关联关系上出错,Hibernate5对JPA注解的校验比4.x严格多了:
- 确保Post里的
@OneToMany注解的mappedBy属性准确对应PostComment里的关联属性名(比如post),大小写不能错; - 原XML里的
cascade、fetch策略要完全转成注解,比如原XML是级联保存删除,那注解里要加cascade = CascadeType.ALL,如果原先是懒加载,fetch = FetchType.LAZY(默认就是懒加载,但最好显式写清楚); - 别忘了维护双向关联的关系!比如Post里要加
addComment方法,把评论的post属性设为当前Post,不然关联关系可能没持久化到数据库。
2. 事务和持久化上下文的坑
迁移到EntityManager后,一定要注意事务边界:
- 如果你的
save方法和saveAndGetDetailsCount方法不在同一个事务里,save之后数据虽然写到DB,但另一个事务里用session.get(或者entityManager.find)可能拿到的是一级缓存里的旧对象,或者因为懒加载没触发加载; - 解决办法要么把这两个方法放到同一个事务里(给上层调用方法加
@Transactional),要么在加载前手动刷新持久化上下文:entityManager.flush(); // 把缓存里的变更刷到DB entityManager.clear(); // 清空一级缓存,强制从DB重新加载 Post post = entityManager.find(Post.class, postId); - 另外,尽量别混用Session和EntityManager的API!虽然EntityManager底层还是Session,但混用容易导致不同持久化上下文的问题,直接用
entityManager.find()替代session.get()更稳妥。
3. 懒加载和缓存的问题
Hibernate5的一级缓存机制有点变化,保存后的对象在缓存里,但关联的懒加载集合还没初始化:
- 如果想立即拿到评论数,要么用
JOIN FETCH的JPQL主动加载关联数据:Post post = entityManager.createQuery( "SELECT p FROM Post p JOIN FETCH p.comments WHERE p.id = :id", Post.class ).setParameter("id", postId).getSingleResult(); - 要么在事务内手动初始化懒加载集合:
Hibernate.initialize(post.getComments());,这个方法需要导入Hibernate的核心包。
4. 版本兼容性要注意
你用的Spring4.3.1和Hibernate5.2.10虽然兼容,但有些配置要跟上:
- 确保Spring的
LocalContainerEntityManagerFactoryBean配置正确,指定了Hibernate5的方言(比如org.hibernate.dialect.PostgreSQLDialect,根据你的DB来); - 别随便开
hibernate.enable_lazy_load_no_trans,这个属性允许在事务外懒加载,但容易导致N+1问题和性能隐患,尽量在事务内处理所有需要加载关联数据的逻辑。
给你贴个我之前用过的正确配置示例,你可以对照着改:
Post实体
@Entity public class Post { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String title; @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true) private List<PostComment> comments = new ArrayList<>(); // 一定要加这个方法维护双向关联 public void addComment(PostComment comment) { comments.add(comment); comment.setPost(this); } // getter、setter省略 }
PostComment实体
@Entity public class PostComment { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String content; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "post_id") private Post post; // getter、setter省略 }
服务层代码
@Service @Transactional public class PostService { @PersistenceContext private EntityManager entityManager; public Long savePostWithComments() { Post post = new Post(); post.setTitle("测试帖子"); PostComment c1 = new PostComment(); c1.setContent("评论1"); post.addComment(c1); PostComment c2 = new PostComment(); c2.setContent("评论2"); post.addComment(c2); PostComment c3 = new PostComment(); c3.setContent("评论3"); post.addComment(c3); entityManager.persist(post); return post.getId(); } public int getCommentCount(Long postId) { // 用JOIN FETCH确保加载关联评论 Post post = entityManager.createQuery( "SELECT p FROM Post p JOIN FETCH p.comments WHERE p.id = :id", Post.class ).setParameter("id", postId).getSingleResult(); return post.getComments().size(); } }
按照这个思路排查,应该能解决你的问题。
内容的提问来源于stack exchange,提问作者Rasool Ghafari
相关产品推荐
相关产品推荐

