休眠 HQL 联接提取不递归提取

2022-09-03 17:35:51

我有以下查询和方法

private static final String FIND = "SELECT DISTINCT domain FROM Domain domain LEFT OUTER JOIN FETCH domain.operators LEFT OUTER JOIN FETCH domain.networkCodes WHERE domain.domainId = :domainId";

@Override
public Domain find(Long domainId) {
    Query query = getCurrentSession().createQuery(FIND);
    query.setLong("domainId", domainId);
    return (Domain) query.uniqueResult();
}

Domain

@Entity
@Table
public class Domain {
    @Id
    @GenericGenerator(name = "generator", strategy = "increment")
    @GeneratedValue(generator = "generator")
    @Column(name = "domain_id")
    private Long domainId;

    @Column(nullable = false, unique = true)
    @NotNull
    private String name;

    @Column(nullable = false)
    @NotNull
    @Enumerated(EnumType.STRING)
    private DomainType type;

    @OneToMany(cascade = {
            CascadeType.PERSIST,
            CascadeType.MERGE
    }, fetch = FetchType.EAGER)
    @JoinTable(joinColumns = {
            @JoinColumn(name = "domain_id")
    }, inverseJoinColumns = {
            @JoinColumn(name = "code")
    })
    @NotEmpty
    @Valid // needed to recur because we specify network codes when creating the domain
    private Set<NetworkCode> networkCodes = new HashSet<>();

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(joinColumns = {
            @JoinColumn(name = "parent", referencedColumnName = "domain_id")
    }, inverseJoinColumns = {
            @JoinColumn(name = "child", referencedColumnName = "domain_id")
    })
    private Set<Domain> operators = new HashSet<>();
    // more
}

我希望这个单个查询能够获取和>关系,但事实并非如此。假设 I 查询有两个运算符,Hibernate 将执行 1 + 2 * 2 = 5 个查询Set<NetworkCode>Set<DomainDomain

Hibernate: select distinct domain0_.domain_id as domain1_1_0_, domain2_.domain_id as domain1_1_1_, networkcod4_.code as code2_2_, domain0_.name as name1_0_, domain0_.type as type1_0_, domain2_.name as name1_1_, domain2_.type as type1_1_, operators1_.parent as parent1_0__, operators1_.child as child4_0__, networkcod3_.domain_id as domain1_1_1__, networkcod3_.code as code5_1__ from domain domain0_ left outer join domain_operators operators1_ on domain0_.domain_id=operators1_.parent left outer join domain domain2_ on operators1_.child=domain2_.domain_id inner join domain_network_codes networkcod3_ on domain0_.domain_id=networkcod3_.domain_id inner join network_code networkcod4_ on networkcod3_.code=networkcod4_.code where domain0_.domain_id=?
Hibernate: select operators0_.parent as parent1_1_, operators0_.child as child4_1_, domain1_.domain_id as domain1_1_0_, domain1_.name as name1_0_, domain1_.type as type1_0_ from domain_operators operators0_ inner join domain domain1_ on operators0_.child=domain1_.domain_id where operators0_.parent=?
Hibernate: select networkcod0_.domain_id as domain1_1_1_, networkcod0_.code as code5_1_, networkcod1_.code as code2_0_ from domain_network_codes networkcod0_ inner join network_code networkcod1_ on networkcod0_.code=networkcod1_.code where networkcod0_.domain_id=?
Hibernate: select operators0_.parent as parent1_1_, operators0_.child as child4_1_, domain1_.domain_id as domain1_1_0_, domain1_.name as name1_0_, domain1_.type as type1_0_ from domain_operators operators0_ inner join domain domain1_ on operators0_.child=domain1_.domain_id where operators0_.parent=?
Hibernate: select networkcod0_.domain_id as domain1_1_1_, networkcod0_.code as code5_1_, networkcod1_.code as code2_0_ from domain_network_codes networkcod0_ inner join network_code networkcod1_ on networkcod0_.code=networkcod1_.code where networkcod0_.domain_id=?

我猜这是因为我正在加入运算符元素,但他们必须自己加入。Domain

是否有我可以执行的 HQL 查询可以同时执行这两项操作?


答案 1

如果你知道你的树中只有两个层次,你有没有想过加入更深的一个层次。像下面这样?

SELECT DISTINCT domain FROM Domain domain 
  LEFT OUTER JOIN FETCH domain.operators operators1 
  LEFT OUTER JOIN FETCH domain.networkCodes 
  LEFT OUTER JOIN FETCH operators1.operators operators2 
  LEFT OUTER JOIN FETCH operators1.networkCodes
WHERE domain.domainId = :domainId

答案 2

冬眠关系适用于不同的获取策略..!!

Hibernate 提供了 4 种检索数据的策略:

选择

@OneToMany(mappedBy="tableName", cascade=CascadeType.ALL)
@Column(name="id") 
@Fetch(FetchMode.SELECT)

在此方法中,会触发多个 SQL。第一个用于检索父表中的所有记录而被触发。将针对检索每个父记录的记录而触发其余项。这基本上是N+1问题。第一个查询从数据库中检索 N 条记录,在本例中为 N 条父记录。对于每个父项,一个新查询将检索子项。因此,对于 N 个父级,N 个查询从子表中检索信息。

加入

@OneToMany(mappedBy="tableName", cascade=CascadeType.ALL)
@Column(name="id")
@Fetch(FetchMode.JOIN) 

这与 SELECT 提取策略类似,只是所有数据库检索都在 JOIN 提取中预先进行,这与 SELECT 中不同,在 SELECT 中,它是根据需要进行的。这可以成为一个重要的性能考虑因素。

部分选择

 @OneToMany(mappedBy="tableName", cascade=CascadeType.ALL)
 @Column(name="id")
 @Fetch(FetchMode.SUBSELECT)

将触发两个 SQL。一个用于检索所有父项,另一个使用 WHERE 子句中的 SUBSELECT 查询来检索具有匹配父 ID 的所有子项。

@OneToMany(mappedBy="tableName", cascade=CascadeType.ALL)
@Column(name="id")
@@BatchSize(size=2)

批大小映射到检索其子项的父项的数目。因此,我们可以指定一次要获取的记录数。但是将执行多个查询。

一对多和多对多允许 - 加入,选择和子选择

多对一和一对一允许 - 加入并选择


休眠还区分(何时获取关联)

1.即时获取 -

加载父级时,将立即获取关联、集合或属性。(lazy=“false”)

2.惰性集合获取 -

当应用程序对某个集合调用操作时,将提取该集合。(这是集合的默认值。(懒惰=“真”)

3.“超懒”集合获取 -

可根据需要从数据库访问集合的各个元素。休眠尝试不将整个集合提取到内存中,除非绝对需要(适用于非常大的集合)(lazy=“extra”)

4.代理获取 -

当对关联的对象调用标识符 getter 以外的方法时,将获取单值关联。(lazy=“proxy”)

5.“无代理”获取 -

访问实例变量时,将提取单值关联。与代理提取相比,此方法的惰性较小。(lazy=“no-proxy”)

6.惰性属性获取 -

访问实例变量时,将提取属性或单值关联。(懒惰=“真”)

一对多和多对多允许即时,Layzy,Extra Lazy

多对一和一对一允许即时代理,无代理


推荐