Hibernate Session.get()返回代理对象问题咨询
关于Hibernate Session.get()返回代理对象的疑惑解答
你遇到的这个问题其实是Hibernate缓存机制和代理生成逻辑的典型表现,很多刚接触Hibernate的开发者都会有这个认知误区——以为session.get()一定会返回真实的实体类对象,其实不然,咱们一步步拆解原因:
为什么get()会返回代理对象?
- 代理对象的提前生成:当你执行
session.get(Product.class, 1)时,因为Product的company关联配置了FetchType.LAZY,Hibernate不会立即去数据库加载对应的Company数据,而是生成一个Company的代理对象赋值给Product的company属性,同时把这个代理对象存入了Session的一级缓存(也就是当前会话的缓存)。 - Session缓存的唯一性原则:Hibernate的Session缓存有个核心规则——同一个Session中,同一个ID的实体只能存在一个实例。当你后续调用
session.get(Company.class, 1)时,Hibernate首先会检查一级缓存里有没有ID为1的Company实例,它找到了之前生成的代理对象,哪怕此时它会去数据库查询并把真实数据填充到代理对象中,也不会替换这个代理实例,而是直接返回它。 - 代理的初始化:你看到日志里Hibernate查询了数据库,是因为这个代理对象之前处于未初始化状态(没有真实数据),当
get()方法触发时,Hibernate会完成代理的初始化,把数据库中的Company数据加载到代理里,但代理对象的类型并不会改变。
如何确保get()返回真实对象?
这里有几种可行的方案,你可以根据业务场景选择:
- 清除缓存中的代理对象:在调用
session.get(Company.class, 1)之前,把缓存里的Company代理清除掉:// 先获取Product关联的Company代理 Company proxyCompany = product.getCompany(); // 从Session缓存中移除这个代理 session.evict(proxyCompany); // 再调用get()就会返回真实对象 Company realCompany = session.get(Company.class, 1); - 使用Hibernate.unproxy()获取真实对象:如果你已经拿到了代理对象,可以直接用这个方法解包:
这个方法很安全:如果传入的是真实对象,会直接返回;如果是代理,会先初始化(加载数据)再返回真实实例。Company company = session.get(Company.class, 1); // 解包代理,得到真实实体对象 Company realCompany = Hibernate.unproxy(company); - 通过JPQL查询获取真实对象:避开Session缓存的直接命中,用JPQL查询:
Company realCompany = session.createQuery("SELECT c FROM Company c WHERE c.id = :id", Company.class) .setParameter("id", 1) .uniqueResult(); - 修改关联的FetchType(谨慎使用):把Product中
company关联的FetchType.LAZY改成FetchType.EAGER,这样获取Product时会直接加载真实的Company对象存入缓存,后续get()就会返回真实对象。但这种方法会增加查询开销,不适合不需要频繁加载Company的场景。
总结
你的理解确实存在偏差:session.get()并不是绝对返回真实对象,它的优先级是先从Session缓存取已存在的实例(不管是代理还是真实对象),再去数据库查询。Hibernate这么设计是为了保证Session中实体的唯一性,避免同一个ID出现多个实例导致的数据不一致问题。
内容的提问来源于stack exchange,提问作者bharathp
相关产品推荐
相关产品推荐

