简体   繁体   English

获取多个onetoMany关系Hibernate JPA

[英]Fetch multiple onetoMany relationships Hibernate JPA

I am using Hibernate JPA 1.0. 我正在使用Hibernate JPA 1.0。

I have the following type of model and I consider manyToOne and oneToOne relationships "eagerly" fetched and oneToMany "lazily" fetched. 我有以下类型的模型,我认为很多ToOne和oneToOne关系“热切地”获取并且oneToMany“懒洋洋地”获取。

I want to fetch Entity A and all its associations where a.id=? 我想获取实体A及其所有关联,其中a.id =?

  • A oneToMany B OneToMany B
    • B oneToOne C B oneToOne C.
      • C oneToMany D C oneToMany D
    • B oneToOne E B oneToOne E.
      • E oneToMany D E oneToMany D
    • B oneToOne F B oneToOne F.
      • F oneToMany D F oneToMany D

Is it possible to load this entity in a single query? 是否可以在单个查询中加载此实体? Or in a subset of queries baring in mind the "n+1 selects problem"! 或者在一系列查询中记住“n + 1选择问题”!

So far my solution to loading all of A associations was to perform the following: 到目前为止,我加载所有A关联的解决方案是执行以下操作:

"Select DISTINCT a from A a JOIN FETCH a.bs WHERE a.id=:aID" “从A JOIN FETCH中选择DISTINCT a a.bs WHERE a.id =:aID”

And then iterate using code in order to fetch all other associations. 然后使用代码进行迭代以获取所有其他关联。

Collection B bs = A.getBs(); 集合B bs = A.getBs();

         for (final B b : bs) {
         b.getCs().getDs().size();
         b.getEs().getDs().size();
         b.getFs().getDs().size();
         }

Obviously there must be a better way of doing this. 显然必须有更好的方法来做到这一点。

You might be interested in reading this article. 你可能有兴趣在阅读文章。 I have tested both 我测试了两个

@Fetch(FetchMode.SUBSELECT)

and also using a Set instead of List, in combination with fetch = FetchType.EAGER it works. 并且还使用Set而不是List,结合fetch = FetchType.EAGER它可以工作。

Use a FETCH JOIN. 使用FETCH JOIN。 From the JPA 1.0 specification: 从JPA 1.0规范:

4.4.5.3 Fetch Joins 4.4.5.3获取联接

A FETCH JOIN enables the fetching of an association as a side effect of the execution of a query. FETCH JOIN启用提取关联作为执行查询的副作用。 A FETCH JOIN is specified over an entity and its related entities. FETCH JOIN是在实体及其相关实体上指定的。

The syntax for a fetch join is 获取连接的语法是

 fetch_join ::= [ LEFT [OUTER] | INNER ] JOIN FETCH join_association_path_expression 

The association referenced by the right side of the FETCH JOIN clause must be an association that belongs to an entity that is returned as a result of the query. FETCH JOIN子句右侧引用的关联必须是属于作为查询结果返回的实体的关联。 It is not permitted to specify an identification variable for the entities referenced by the right side of the FETCH JOIN clause, and hence references to the implicitly fetched entities cannot appear elsewhere in the query. 不允许为FETCH JOIN子句右侧引用的实体指定标识变量,因此对隐式获取的实体的引用不能出现在查询的其他位置。

The following query returns a set of departments. 以下查询返回一组部门。 As a side effect, the associated employees for those departments are also retrieved, even though they are not part of the explicit query result. 作为副作用,即使它们不是显式查询结果的一部分,也会检索这些部门的关联员工。 The persistent fields or properties of the employees that are eagerly fetched are fully initialized. 急切获取的员工的持久字段或属性已完全初始化。 The initialization of the relationship properties of the employees that are retrieved is determined by the metadata for the Employee entity class. 检索的员工的关系属性的初始化由Employee实体类的元数据确定。

 SELECT d FROM Department d LEFT JOIN FETCH d.employees WHERE d.deptno = 1 

A fetch join has the same join semantics as the corresponding inner or outer join, except that the related objects specified on the right-hand side of the join operation are not returned in the query result or otherwise referenced in the query. 提取连接具有与对应的内连接或外连接相同的连接语义,除了在连接操作的右侧指定的相关对象不在查询结果中返回或以其他方式在查询中引用。 Hence, for example, if department 1 has five employees, the above query returns five references to the department 1 entity. 因此,例如,如果部门1有五个雇员,则上述查询返回对部门1实体的五个引用。

Of course, use it wisely, don't join too many tables or you will kill performances. 当然,明智地使用它,不要加入太多桌子,否则你会杀死表演。

Looking for an answer drawing from credible and/or official sources. 寻找可信和/或官方来源的答案。

How about JBoss ORM documentation? JBoss ORM文档怎么样?

https://docs.jboss.org/hibernate/orm/current/userguide/html_single/chapters/fetching/Fetching.html https://docs.jboss.org/hibernate/orm/current/userguide/html_single/chapters/fetching/Fetching.html

There are a number of scopes for defining fetching: 定义提取有很多范围:

static 静态的

Static definition of fetching strategies is done in the mappings. 获取策略的静态定义在映射中完成。 The statically-defined fetch strategies is used in the absence of any dynamically defined strategies 在没有任何动态定义的策略的情况下使用静态定义的提取策略

SELECT Performs a separate SQL select to load the data. SELECT执行单独的SQL select以加载数据。 This can either be EAGER (the second select is issued immediately) or LAZY (the second select is delayed until the data is needed). 这可以是EAGER(立即发出第二个选择)或LAZY(第二个选择被延迟直到需要数据)。 This is the strategy generally termed N+1. 这是通常称为N + 1的策略。

JOIN Inherently an EAGER style of fetching. JOIN本质上是EAGER的抓取方式。 The data to be fetched is obtained through the use of an SQL outer join. 要获取的数据是通过使用SQL外连接获得的。

BATCH Performs a separate SQL select to load a number of related data items using an IN-restriction as part of the SQL WHERE-clause based on a batch size. BATCH根据批量大小,使用IN限制作为SQL WHERE子句的一部分,执行单独的SQL选择以加载大量相关数据项。 Again, this can either be EAGER (the second select is issued immediately) or LAZY (the second select is delayed until the data is needed). 同样,这可以是EAGER(立即发出第二个选择)或LAZY(第二个选择被延迟直到需要数据)。

SUBSELECT Performs a separate SQL select to load associated data based on the SQL restriction used to load the owner. SUBSELECT根据用于加载所有者的SQL限制,执行单独的SQL选择以加载关联数据。 Again, this can either be EAGER (the second select is issued immediately) or LAZY (the second select is delayed until the data is needed). 同样,这可以是EAGER(立即发出第二个选择)或LAZY(第二个选择被延迟直到需要数据)。


dynamic (sometimes referred to as runtime) 动态的(有时称为运行时)

Dynamic definition is really use-case centric. 动态定义实际上是以用例为中心的。 There are multiple ways to define dynamic fetching: 有多种方法可以定义动态提取:

Fetch profiles defined in mappings, but can be enabled/disabled on the Session. 获取映射中定义的配置文件 ,但可以在会话中启用/禁用。

HQL/JPQL and both Hibernate and JPA Criteria queries have the ability to specify fetching, specific to said query. HQL / JPQL以及Hibernate和JPA Criteria查询都能够指定特定于所述查询的提取。

Entity Graphs Starting in Hibernate 4.2 (JPA 2.1) this is also an option. 实体图从Hibernate 4.2(JPA 2.1)开始,这也是一个选项。

And to prove the answer above, here's an example: 为了证明上面的答案,这是一个例子:

FetchMode.SUBSELECT To demonstrate how FetchMode.SUBSELECT works, we are going to modify the FetchMode.SELECT mapping example to use FetchMode.SUBSELECT: FetchMode.SUBSELECT为了演示FetchMode.SUBSELECT的工作原理,我们将修改FetchMode.SELECT映射示例以使用FetchMode.SUBSELECT:

Example 17. FetchMode.SUBSELECT mapping example: 示例17. FetchMode.SUBSELECT映射示例:

@OneToMany(mappedBy = "department", fetch = FetchType.LAZY)
@Fetch(FetchMode.SUBSELECT)
private List<Employee> employees = new ArrayList<>();

Now, we are going to fetch all Department entities that match a given 现在,我们将获取与给定匹配的所有Department实体
filtering criteria and then navigate their employees collections. 过滤标准,然后导航其员工集合。

Hibernate is going to avoid the N+1 query issue by generating a single SQL statement to initialize all employees collections for all Department entities that were previously fetched. Hibernate将通过生成单个SQL语句来初始化先前获取的所有Department实体的所有员工集合来避免N + 1查询问题。 Instead of using passing all entity identifiers, Hibernate simply reruns the previous query that fetched the Department entities. Hibernate不是使用传递所有实体标识符,而是简单地重新运行先前获取Department实体的查询。

Example 18. FetchMode.SUBSELECT mapping example: 示例18. FetchMode.SUBSELECT映射示例:

List<Department> departments = entityManager.createQuery(
    "select d " +
    "from Department d " +
    "where d.name like :token", Department.class)
    .setParameter( "token", "Department%" )
    .getResultList();

log.infof( "Fetched %d Departments", departments.size());

for (Department department : departments ) {
    assertEquals(3, department.getEmployees().size());
}

-- Fetched 2 Departments - 获得2个部门

SELECT
    d.id as id1_0_
FROM
    Department d
where
    d.name like 'Department%'

-- Fetched 2 Departments

SELECT
    e.department_id as departme3_1_1_,
    e.id as id1_1_1_,
    e.id as id1_1_0_,
    e.department_id as departme3_1_0_,
    e.username as username2_1_0_
FROM
    Employee e
WHERE
    e.department_id in (
        SELECT
            fetchmodes0_.id
        FROM
            Department fetchmodes0_
        WHERE
            d.name like 'Department%'
    )

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM