JPA:如何为 3 个级别定义 @NamedEntityGraph?

JPA : How to define @NamedEntityGraph for 3 levels?

我有 3 个实体。分支,主题,主题。 Branch 有主题列表,Subject 有主题列表。还 subjectList 和 topicList 都是惰性的。我想获取所有分支 在单个查询中包含其主题。

1.

@Entity
public class Branch implements Serializable 
{
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    private String name;

    @OneToMany(mappedBy = "branch")
    private List<Subject> subjectList;
    //Getters and Setters
}

2.

@Entity
public class Subject implements Serializable 
{
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String name;

    @ManyToOne()
    private Branch branch;

    @OneToMany(mappedBy = "subject")
    private List<Topic> topicList;
    //Getters and Setters       
}

3.

@Entity
public class Topic implements Serializable 
{
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    private String name;

    @ManyToOne()
    private Subject subject;
    //Getters and Setters
}

我尝试了下面的方法,但是没有用。

@NamedEntityGraph(name="branch_subject", 
attributeNodes = {
    @NamedAttributeNode(value="name"),
    @NamedAttributeNode(value="subjectList", subgraph = "subjectListGraph")
},
subgraphs = {
    @NamedSubgraph(name="subjectListGraph",
            attributeNodes = {
                @NamedAttributeNode(value="name"),
                @NamedAttributeNode(value = "topicList", subgraph = "topicListGraph")
            }
    ),
    @NamedSubgraph(name="topicListGraph",
            attributeNodes = {
                    @NamedAttributeNode("name")
            }
    )
}
)

下面的代码也用于从 数据库,我正在使用 JPQL 如下

    EntityGraph branchEntityGraph = entityManager
                .getEntityGraph("branch_subject");

        Branch branch = entityManager
                .createQuery("SELECT b from Branch b WHERE b.id=:ID",
                        Branch.class)
                .setHint("javax.persistence.loadgraph", branchEntityGraph)
                .setParameter("ID", branch1.getId()).getResultList().get(0);

这给出了以下例外情况

org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags

Hibernate 不允许您获取多个包,因为它最终会获取笛卡尔积。

M → N → P 一对多或多对多关系

对于多嵌套层次结构,您可以在多个集合上使用 JOIN FETCH,只要您的列表映射为 Set

M → N 和M → P 一对多或多对多关系

对于同级集合,如 M → N 和 M → P,不要切换到使用 Set 而不是 List

使用 Set 而不是 List 来避免 MultipleBagFetchException is a very bad idea since you will still end up with a Cartesian Product,这将导致性能问题,因为您将要获取 M x N x P 记录。

在这种情况下,更好的方法是使用第一个查询获取一个集合,并对其余集合使用其他查询:

List<Post> _posts = entityManager.createQuery("""
    select distinct p
    from Post p
    left join fetch p.comments
    where p.id between :minId and :maxId
    """, Post.class)
.setParameter("minId", 1L)
.setParameter("maxId", 50L)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();

_posts = entityManager.createQuery("""
    select distinct p
    from Post p
    left join fetch p.tags t
    where p in :posts
    """, Post.class)
.setParameter("posts", _posts)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();

此策略允许您通过获取 M x (N + P) 条记录来避免 M x N x P 结果集。

从子端获取到父端

如果在获取子集合时必须使用 INNER JOIN,那么您可以简单地[从最内层的子集合获取到根][3],然后重新组装结构。这样效率更高,因为查询是这样的:

select t 
from Topic t
join t.subject s
join s.branch b

我想我遇到了类似的问题。我所有的 Device 实体都有一个 Transaction 对象,在我的例子中,该对象存储 datetime 以及该特定对象的用户和计算机信息。还有一个 DeviceType 实体,它有事务对象并且与我的 Device 实体有多对一关系。所以实际上我与这个 Transaction Entity 有 1 级和 2 级的嵌套关系。因此,我得到了一些嵌套的异常。使用 Subgraph 为我解决了这个问题。这是我的 NamedEntityGraphs 定义代码。希望对您有所帮助:

@NamedEntityGraphs(value = {
    @NamedEntityGraph(name = "Device.AllRelations",
            attributeNodes = {
                    @NamedAttributeNode("transaction"),
                    @NamedAttributeNode(value = "deviceType", subgraph = "DeviceType.Transaction")
            },
            subgraphs = {
                    @NamedSubgraph(name = "DeviceType.Transaction", attributeNodes = {
                            @NamedAttributeNode("transaction")
                    })
            }
    ),

    @NamedEntityGraph(name = "Device.TransactionOnly", attributeNodes = {
            @NamedAttributeNode("transaction")
    }),
})