防止 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.station 和 location.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.LAZY
on 位置的组属性,因为我需要获取组。但是,我不想获取该组的电台。
弗拉德刚刚给出了答案。以上是关于防止 Hibernate 加载懒惰的 ManyToOne的主要内容,如果未能解决你的问题,请参考以下文章
Hibernate 在从相关实体中删除时加载 LOBS(不应该是懒惰的)吗?
是否将Lazily提取的对象附加到Hibernate会话(由Hibernate支持的Spring Data)?
使用 FetchType.LAZY 防止 JPA/Hibernate 中的延迟加载(尤其是在使用 @Transactional 时)?
org.hibernate.LazyInitializationException:懒惰初始化角色集合失败(Hibernate + Spring)