防止 Hibernate 加载懒惰的 ManyToOne

Posted

技术标签:

【中文标题】防止 Hibernate 加载懒惰的 ManyToOne【英文标题】:Prevent Hibernate from loading lazy ManyToOne 【发布时间】:2014-07-15 12:39:58 【问题描述】:

我将模型中的问题减少到 3 个实体:

车站。 位置(每个站点多个)。 位置组(每个站点多个)。

Location 类:

@Entity
@Table(name = "***")
public class Location 

   @ManyToOne
   @JoinColumn(name = "s_id")
   private Station s;

   @ManyToOne
   @JoinColumn(name = "g_id")
   private GroupOfLocations group;      

 

还有GroupOfLocation 的类:

@Entity
@Table(name = "***")
public class GroupOfLocation 

  @ManyToOne(fetch = FetchType.LAZY) //I do not want the Station to be loaded
  @JoinColumn(name = "s_id")
  private Station s;


当我通过 ID 获得位置时:

    其站点已加载 位置组已加载 群组的站已加载我不需要它。

问题:该组包含该站,但我不希望它被加载。我预计 fetch = FetchType.LAZY 会阻止 Station 完全加载,但它不起作用。

我在 SO 上进行了搜索,有时,问题来自声明为 final 的类,但此模型中没有最终类。

有什么想法吗?

这是通过 ID 搜索实体 id 的方式:

public Location getById(Integer id) 
  CriteriaBuilder cb = em.getCriteriaBuilder();
  CriteriaQuery<Location> query = cb.createQuery(Location.class);
  Root<Location> entity = query.from(Location.class);
  Predicate whereClause = cb.equal(entity.get(Location_.id), id);
  query.where(whereClause); 
  return em.createQuery(query).getSingleResult();

【问题讨论】:

【参考方案1】:

任何 JPQL 或 Criteria 查询都将转到 override the default 获取计划(您在实体映射中定义的计划)。

默认查询计划仅在使用 find 或 getReference 加载实体时有效

entityManager.find(GroupOfLocation.class, 1L);

默认的 @ManyToOne 提取是 EAGER,但由于您尚未发出连接提取,因此将通过一些额外的选择来提取这些提取。

location.group.station 不会通过额外的选择来获取,但代理仍然存在。

如果 location.station 恰好与 location.group.station 匹配,那么 Hibernate 将使用已经加载的 location.station,因为在 Hibernate Session 对象内部相等匹配实例也相等(意味着只能有一个具有给定实体标识符的 Entity 类型的对象)。

因此,如果 location.group.stationlocation.station 引用相同的实体,您将看到已初始化的代理,否则代理将保持未初始化状态。

如果会话已关闭且代理未初始化,您将在访问它时收到延迟异常。

【讨论】:

非常完整和有趣的答案。我尝试使用/不使用em.find() 并使用相同/不同的站 ID(即 location.station.id != location.group.station.id)。当 station.id 不同时,永远不会加载 location.group.station(似乎在这两种情况下都尊重 FetchType。)。我尝试查看使用 em.find 是否制定了 2 个不同的获取计划。你知道是否有办法告诉hibernate为第二站显式设置null? (我将模型转换为 JSON 格式,这使得该站被复制。) 在数据库中存在实际关联行的情况下,无法强制为 null。我将加载实体并创建一个替代 DTO 结构,它将成为您的 JSON 模型。这样,您也可以将 JPA 模型与 JSON 视图模型分开。它既干净又可以轻松处理循环依赖,即使您必须做更多工作才能完成此任务。 那么实际的答案是什么...?【参考方案2】:

但是您没有在位置的 s & group 属性上声明 fetch = FetchType.LAZY。

【讨论】:

我不明白你的意思。我没有声明fetch = FetchType.LAZYon 位置的组属性,因为我需要获取组。但是,我不想获取该组的电台。 弗拉德刚刚给出了答案。

以上是关于防止 Hibernate 加载懒惰的 ManyToOne的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate 在从相关实体中删除时加载 LOBS(不应该是懒惰的)吗?

是否将Lazily提取的对象附加到Hibernate会话(由Hibernate支持的Spring Data)?

使用 FetchType.LAZY 防止 JPA/Hibernate 中的延迟加载(尤其是在使用 @Transactional 时)?

org.hibernate.LazyInitializationException:懒惰初始化角色集合失败(Hibernate + Spring)

NHibernate有一个懒惰的负载

(懒惰)使用 Hibernate Criteria API 的 LEFT OUTER JOIN