spring-data - 加载 OneToOne 关系的嵌套对象
问题描述
我有一堂课
public class ToolSet {
...
@OneToOne(fetch = FetchType.EAGER)
private Contractor assignedTo;
...
}
public class Contractor {
...
@ManyToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
@JoinTable(
name = "contractors_docs_photos",
joinColumns = @JoinColumn(
name = "contractor_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(
name = "image_id", referencedColumnName = "id"))
private Set<Image> idPhoto;
...
}
现在,当我查询工具集时,我得到以下信息
"toolset": [
{
...
"_embedded": {
"assignedTo": {
"firstName": "firstName",
}
}
}
我如何在 Contractor(照片)中加载嵌套对象?因此,当我加载工具集时,我会看到承包商,而在承包商内部,我会看到它的照片
解决方案
正确的@ManyToMany 关系
没有 FetchType.EAGER
首先,我们应该看看你的@ManyToMany
关系本身的实现。正如 Vlad Mihalcea 在本文中非常详细地解释的那样,这FetchType.EAGER
不是一个好的选择。它会导致大量不必要的数据库请求。
没有 CascadeType.REMOVE
此外,级联所有更改可能会导致严重问题。删除特定对象可以自动级联以删除许多其他对象。如本文所述(向下滚动一点),CascadeType.REMOVE
应避免用于@ManyToMany
关系。CascadeType.ALL
包括REMOVE
. 相反,您应该使用@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
在这个 Stackoverflow 答案@ManyToMany
中可以找到另一个关系示例。
初始化惰性关系
@ManyToMany
Hibernate(Spring Data 自动使用的 ORM)不初始化惰性关系(如提到的“好” )。在相应的变量中只能找到具有相关主键的代理。当有人试图访问变量时,完整的对象会自动从数据库中加载。这会导致额外的数据库请求。
如果您事先已经知道需要该对象(访问的变量),您应该在从数据库请求父对象时初始化关系(当您ToolSet
从数据库请求 a 时,它idPhoto
也应该被加载。
在 SQL 中,这意味着这些不同表之间的连接。在 JPA 中,连接并不自动意味着数据已加载。因此,您应该使用 fetch join 或实体图来初始化惰性注释。本文很好地概述了如何使用这些不同的初始化方式。
标准 API 示例
在 JPA 中有不同的方式来实现数据库请求:JPQL 作为一种查询语言和 Criteria API(更容易重构,更不容易出错)。由于我是 Criteria API 的粉丝,我将向您展示如何使用它初始化关系以及实体图的用法的示例。
public Optional<ToolSet> findById(long id) {
EntityGraph<ToolSet> graph = entityManager.createEntityGraph(ToolSet.class);
graph.addAttributeNodes("assignedTo");
Subgraph<Contractor> subgraph = graph.addSubgraph("assignedTo");
subgraph.addAttributeNodes("idPhoto");
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<ToolSet> cq = cb.createQuery(ToolSet.class);
Root<ToolSet> root = cq.from(ToolSet.class);
cq.where(cb.equal(root.get("id"), id)); // Assumption of id as PK
TypedQuery<ToolSet> typedQuery = entityManager.createQuery(cq);
typedQuery.setHint("javax.persistence.fetchgraph", graph);
ToolSet response = null;
try {
response = typedQuery.getSingleResult();
}
catch(NoResultException nre) {}
return Optional.ofNullable(response);
}
在此示例中,我假设您的主键ToolSet
为id
. 要在编译时更好地查看错误,请查看 hibernate-jpamodelgen。然后,您可以编写类似cq.where(cb.equal(root.get(ToolSet_.ID), id))
.
上面,我用Optional
. 它不必使用。无论如何,您可以研究使用它的优点。
推荐阅读
- sql - 我的子查询在 CASE 语句中返回了超过 1 个值
- python - 我的代码 python web 抓取 Beautiful Soup 有什么问题
- maven - 线程“主”java.lang.NoSuchMethodError 中的异常:org.springframework.boot.builder.SpringApplicationBuilder。
([Ljava/lang/类;) - python - 使用 pandas 导出到 excel 时的自定义格式
- sql - SAS SQL 条件分组依据
- tensorflow - model.evaluate_generator 给出错误的准确性
- scala - 制作 sbt scala 构建文件引入 apache spark
- javascript - 我需要帮助在 javascript 中切换文本
- spring-cloud-gateway - java - 如何将spring cloud gateway中的IgnoredPatterns作为zuul?
- django - 如何将我的搜索字段放在我想要的位置