Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/354.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 返回多个部分重复的联接的高效Hibernate条件_Java_Hibernate_Optimization_Criteria_Hibernate Criteria - Fatal编程技术网

Java 返回多个部分重复的联接的高效Hibernate条件

Java 返回多个部分重复的联接的高效Hibernate条件,java,hibernate,optimization,criteria,hibernate-criteria,Java,Hibernate,Optimization,Criteria,Hibernate Criteria,我正在获取一个长长的实体列表,这些实体引用了其他实体,这些实体引用了。。。最后,他们通常将一个用户称为他们的所有者。这并不奇怪,因为查询的是属于单个用户的实体。多行复制的零件较多;实际上,只有一小部分是唯一的数据。由于查询速度似乎很慢,我想通过使用 criteria.setFetchMode(path, FetchMode.SELECT); 在我上面的例子中,这是可行的,但是当查询多个用户(作为管理员)时,情况会变得很糟糕,因为hibernate会为每个用户发出一个单独的查询,而不是像这样的查

我正在获取一个长长的实体列表,这些实体引用了其他实体,这些实体引用了。。。最后,他们通常将一个
用户
称为他们的
所有者
。这并不奇怪,因为查询的是属于单个
用户的实体。多行复制的零件较多;实际上,只有一小部分是唯一的数据。由于查询速度似乎很慢,我想通过使用

criteria.setFetchMode(path, FetchMode.SELECT);
在我上面的例子中,这是可行的,但是当查询多个用户(作为管理员)时,情况会变得很糟糕,因为hibernate会为每个
用户发出一个单独的查询,而不是像这样的查询

SELECT * FROM User WHERE id IN (?, ?, ..., ?)
或者根本就不获取它们(每个实体只能得到一个查询)。我想知道我错过了什么

因此,我没有获取大量冗余数据,而是遇到了1+N问题,显然1+1查询就可以了

  • 是否有方法指示Hibernate使用正确的查询
  • 是否有一种方法可以通过在条件本身中指定来防止Hibernate获取所有者(而不是将
    fetch=FetchType.LAZY
    放在字段上;惰性应该是特定于查询的)

我认为这不重要,但我的课

class Child {
    @ManyToOne Father father;
    @ManyToOne Mother mother;
    ...
}
class Father {
    @ManyToOne User owner;
    ...
}
class Mother {
    @ManyToOne User owner;
    ...
}
查询如下

createCriteria(Child.class)
.add(Restrictions.in("id", idList))
.add(Restrictions.eq("isDeleted", false))

.createAlias("Father", "f")
.add(Restrictions.eq("f.isDeleted", false))
.setFetchMode("f.owner", FetchMode.SELECT)

.createAlias("Mother", "m")
.add(Restrictions.eq("m.isDeleted", false))
.setFetchMode("m.owner", FetchMode.SELECT)

.list();
重要的一点是,
owner
不会被使用,可以被代理。
FetchMode的javadoc。选择

使用一个单独的选择按钮,急切地获取


因此,它基本上承诺了我想要的1+1查询,而不是“对每个实体使用单独的select”。

我写了一个小项目来演示这种行为。根据您的条件生成的SQL如下所示:

select
    this_.id as id1_0_4_,
    this_.father_id as father_i3_0_4_,
    this_.isDeleted as isDelete2_0_4_,
    this_.mother_id as mother_i4_0_4_,
    f1_.id as id1_1_0_,
    f1_.isDeleted as isDelete2_1_0_,
    f1_.owner_id as owner_id3_1_0_,
    user5_.id as id1_3_1_,
    user5_.isDeleted as isDelete2_3_1_,
    m2_.id as id1_2_2_,
    m2_.isDeleted as isDelete2_2_2_,
    m2_.owner_id as owner_id3_2_2_,
    user7_.id as id1_3_3_,
    user7_.isDeleted as isDelete2_3_3_ 
from
    Child this_ 
inner join
    Father f1_ 
        on this_.father_id=f1_.id 
left outer join
    User user5_ 
        on f1_.owner_id=user5_.id 
inner join
    Mother m2_ 
        on this_.mother_id=m2_.id 
left outer join
    User user7_ 
        on m2_.owner_id=user7_.id 
where
    this_.id in (
        ?, ?
    ) 
    and this_.isDeleted=? 
    and f1_.isDeleted=? 
    and m2_.isDeleted=?
  • 更改criteria API中的FetchMode不会影响查询。所有者数据仍在查询中
  • Id位于“in”子句中,Hibernate没有对每个Id发出单独的查询
  • 如其他答案中所述,如果实体关系设置为
    EAGER
    ,即JPA默认值,则无法更改Criteria API中的获取模式。提取模式需要更改为
    惰性

    您可以看到,

    旨在帮助您实现您想要的目标,但目前非常有限,您只能使用联接样式的获取配置文件覆盖默认的获取计划/策略(您可以使惰性关联变得急切,但反之亦然)。但是,您可以使用它来反转该行为:默认情况下使关联变为惰性,并默认情况下为所有会话/事务启用概要文件。然后在事务中禁用要延迟加载的配置文件


    上面的解决方案看起来太麻烦了,我在大多数用例中使用的避免加载冗余数据和N+1选择问题的方法是使关联变懒并定义

    总结一下我的挫败感。。。Hibernate在这方面充满了惊喜(bug?)

    • 除非使用
      @ManyToOne(fetch=FetchType.LAZY)
      声明属性,否则无法更改任何内容
    • 默认值是
      FetchType.EAGER
      ,这很愚蠢,因为它不能被重写
    • 使用
      标准。setFetchMode(path,FetchMode.SELECT)
      是毫无意义的,因为它总是一个no-op(要么由于属性的不可重写的渴望而被忽略,要么属性已经懒惰)
    • 默认情况下,延迟抓取会导致1+N问题
    • 它可以通过类级别的
      @BatchSize
      注释进行控制
    • 在标量字段上放置
      @BatchSize
      注释会被静默忽略
    为了得到我想要的(两个SQL查询),我只需要两件事:

    • 使用
      @ManyToOne(fetch=FetchType.LAZY)
    • @BatchSize(size=aLot)
      放在属性的类上
    这很简单,但有点难找到(因为上面所有被忽略的事情)。我还没有查看获取配置文件

    除非该财产是以
    @ManyToOne(fetch=FetchType.LAZY)
    ,您不能更改任何内容

    至少目前是这样,直到fetch配置文件功能被扩展以提供将急切加载更改为延迟加载的能力

    默认值为
    FetchType.EAGER
    ,这是愚蠢的,因为它不可能 凌驾

    是的,我同意这是不好的,但在Hibernate原生API中,默认情况下一切都是懒惰的;除非另有明确规定,否则JPA要求一个协会表现出渴望

    使用criteria.setFetchMode(路径,FetchMode.SELECT)与 它总是一个无操作(或者因为 属性的不可重写的渴望或属性是懒惰的 已经)!

    使用它,您应该能够覆盖其他延迟获取模式。请参阅Hibernate主要贡献者之一关于javadoc混淆的介绍

    默认情况下,延迟抓取会导致1+N问题

    具体来说,它与延迟加载无关,如果不在同一查询中获取急切加载的关联,它也是急切加载的默认设置

    它可以通过类级别的
    @BatchSize
    注释进行控制


    您必须将其放在类级别,才能使其在与该实体的一个关联上生效;这很有帮助。对于集合关联(与其他实体中定义的实体的多个关联),您可以灵活地为每个关联分别定义它。

    N+1问题?你试过FetchMode.JOIN或FetchMode.SUBSELECT吗?@dimitrisli当然,如果是2,则改为N+1
    FetchMode.JOIN
    是我想要避免的,因为它读取大量重复数据
    SUBSELECT
    存在于
    org.hibernate.annotations.FetchMode
    中,但不存在于我正在使用的
    org.hibernate.FetchMode
    中(I