【问题标题】:Fetch multiple onetoMany relationships Hibernate JPA获取多个单对多关系 Hibernate JPA
【发布时间】:2011-03-15 08:44:47
【问题描述】:

我正在使用 Hibernate JPA 1.0。

我有以下类型的模型,我认为 manyToOne 和 oneToOne 关系“急切地”获取,而 oneToMany 关系“懒惰地”获取。

我想获取实体 A 及其所有关联,其中 a.id=?

  • 一对多 B
    • B oneToOne C
      • C oneToMany D
    • B oneToOne E
      • E oneToMany D
    • B oneToOne F
      • F oneToMany D

是否可以在单个查询中加载此实体? 或者在考虑“n+1 选择问题”的查询子集中!

到目前为止,我加载所有 A 关联的解决方案是执行以下操作:

"Select DISTINCT a from A a JOIN FETCH a.bs WHERE a.id=:aID"

然后使用代码进行迭代以获取所有其他关联。

集合 B bs = A.getBs();

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

显然必须有更好的方法来做到这一点。

【问题讨论】:

  • 致 Dimitri,我也有同样的问题。您尚未接受任何答复。那么哪种解决方案适合您?
  • 我用batch fetching解决了大多数类似的用例。

标签: java hibernate orm jpa


【解决方案1】:

使用 FETCH JOIN。来自 JPA 1.0 规范:

4.4.5.3 获取连接

FETCH JOIN 可以获取关联 作为执行的副作用 询问。一个 FETCH JOIN 被指定在 一个实体及其相关实体。

获取连接的语法是

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

引用的关联 FETCH JOIN 子句的右侧 必须是属于的关联 作为结果返回的实体 的查询。不允许 指定一个标识变量 权利引用的实体 FETCH JOIN 子句的一侧,以及 因此隐含地引用 提取的实体不能出现 查询中的其他位置。

以下查询返回一组 部门。作为一个副作用, 相关员工 部门也被检索,甚至 虽然它们不是 显式查询结果。执着的 员工的领域或属性 急切地取来的完全 初始化。的初始化 的关系属性 检索到的员工是 由元数据决定 员工实体类。

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

获取连接具有相同的连接 语义作为相应的内部 或外连接,除了相关的 右侧指定的对象 加入操作的一边不是 在查询结果中返回或 否则在查询中引用。 因此,例如,如果部门 1 有五名员工,以上查询 返回五个对 部门 1 实体。

当然要明智地使用它,不要加入太多的桌子,否则会扼杀表演。

【讨论】:

  • 是的,但也需要 SELECT d FROM Department d LEFT JOIN FETCH d.employees es LEFT JOIN FETCH es.employee.addresses WHERE d.deptno = 1 并加载每个员工拥有的所有地址。
  • @Dimitri:FETCH JOIN 是急切地获取关联的方式。我不明白你的意思(顺便说一下,我不确定你是否想要一个 LEFT JOIN)。
【解决方案2】:

您可能有兴趣阅读this 文章。我都测试过

@Fetch(FetchMode.SUBSELECT)

和 也使用 Set 而不是 List,与 fetch = FetchType.EAGER 结合使用。

【讨论】:

  • 在关系上使用FetchType.EAGER 通常是一种反模式,因为并非所有关系都需要加载,在不使用它们的情况下加载它们是依赖 JPA 的应用程序中性能问题的一个很好的例子.通常要做的是加载数据,然后通过 JPQL 查询或通过 initialize 函数独立初始化您实际需要的数据。
【解决方案3】:

从可靠和/或官方来源寻找答案。

JBoss ORM 文档怎么样?

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

定义抓取的范围有很多:

static

抓取策略的静态定义是在 映射。静态定义的获取策略用于 没有任何动态定义的策略

SELECT 执行单独的 SQL 选择以加载数据。这个可以 要么是 EAGER(立即发出第二个选择)要么是 LAZY( 第二次选择被延迟,直到需要数据)。这是 通常称为 N+1 的策略。

JOIN 本质上是一种 EAGER 的获取方式。要获取的数据是 通过使用 SQL 外连接获得。

BATCH 执行单独的 SQL 选择以加载许多相关数据 项目使用 IN 限制作为 SQL WHERE 子句的一部分,基于 批量大小。同样,这可以是 EAGER(第二个选择是 立即发出)或 LAZY(第二次选择延迟到 需要数据)。

SUBSELECT 执行单独的 SQL 选择以加载基于关联的数据 关于用于加载所有者的 SQL 限制。同样,这可以 是 EAGER(立即发出第二个选择)或 LAZY(第二个 select 会延迟到需要数据为止)。


动态(有时称为运行时)

动态定义确实以用例为中心。有多种定义动态的方法 获取:

获取配置文件在映射中定义,但可以在 会话。

HQL/JPQL 和 Hibernate 和 JPA Criteria 查询都有能力 指定获取,特定于所述查询。

实体图 从 Hibernate 4.2 (JPA 2.1) 开始,这也是一个 选项。

为了证明上面的答案,这里有一个例子:

FetchMode.SUBSELECT 为了演示 FetchMode.SUBSELECT 的工作原理,我们 将修改 FetchMode.SELECT 映射示例以使用 FetchMode.SUBSELECT:

示例 17. FetchMode.SUBSELECT 映射示例:

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

现在,我们将获取与给定匹配的所有部门实体
过滤条件,然后浏览他们的员工集合。

Hibernate 将通过生成单个查询来避免 N+1 查询问题 初始化所有员工集合的 SQL 语句 之前获取的部门实体。而不是使用 传递所有实体标识符,Hibernate 只是重新运行前一个 获取部门实体的查询。

示例 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());
}

-- 获取 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%'
    )

【讨论】:

    猜你喜欢
    • 2014-12-09
    • 1970-01-01
    • 1970-01-01
    • 2017-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-07
    • 2019-02-11
    相关资源
    最近更新 更多