【问题标题】:Loading nested object of OneToOne relationship加载 OneToOne 关系的嵌套对象
【发布时间】:2019-10-04 14:30:25
【问题描述】:

我有课

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(照片)中加载嵌套对象?这样当我加载工具集时,我会看到承包商,而在承包商内部,我会看到它的照片

【问题讨论】:

    标签: spring-data spring-data-rest


    【解决方案1】:

    正确的@ManyToMany 关系

    没有 FetchType.EAGER

    首先,我们应该看看你的@ManyToMany 关系本身的实现。正如 Vlad Mihalcea 在this article 中详细解释的那样,FetchType.EAGER 不是一个好的选择。它会导致大量不必要的数据库请求。

    没有 CascadeType.REMOVE

    此外,级联所有更改可能会导致严重问题。删除特定对象可以自动级联以删除许多其他对象。作为described in this article(向下滚动一点),CascadeType.REMOVE 应避免与@ManyToMany 关系。 CascadeType.ALL 包括 REMOVE。相反,您应该使用@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})

    @ManyToMany 关系的另一个示例可以在 this Stackoverflow answer 中找到。

    初始化惰性关系

    Hibernate(Spring Data 自动使用的 ORM)不会初始化惰性关系(如提到的“好”@ManyToMany)。在相应的变量中只能找到具有相关主键的代理。当有人试图访问变量时,完整的对象会自动从数据库中加载。这会导致额外的数据库请求。

    如果您事先已经知道需要该对象(访问的变量),则应在从数据库请求父对象时初始化关系(当您从数据库请求ToolSet 时,其idPhoto 应该是也加载了。

    在 SQL 中,这意味着这些不同表之间的连接。在 JPA 中,连接并不自动意味着数据已加载。因此,您应该使用 fetch join 或实体图来初始化惰性注释。 This article 很好地概述了如何使用这些不同的初始化方式。

    标准 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。它不必使用。不管怎样,你可以研究一下使用它的好处。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-14
      • 2023-01-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多