使用Hibernate的QueryDSL @OneToOne Join-FetchMode查询

9
假设我们有一个简单的实体“客户”,它与一个实体“地址”具有一对一的关系。外键位于地址端。
@Entity
public class Customer extends EntityBase {
    @Column(name = "name", nullable = true)
    private String name;

    @OneToOne(mappedBy = "customer")
    private Address address;

    // getter, setter, ...
}

@Entity
public class Address extends EntityBase {
    @OneToOne(optional = false)
    private Customer customer;

    @Column(nullable = true)
    private String street;
    @Column(nullable = true)
    private String zip;
    @Column(nullable = true)
    private String city;

    // getter, setter, ...
}

如果您现在使用Hibernate加载所有客户实体并将生成的查询打印到控制台上,您会发现Hibernate内部仅触发了一个查询。
session.createCriteria(Customer.class).list();

Hibernate的作用:

select
    this_.id as id1_1_1_,
    this_.name as name2_1_1_,
    address2_.id as id1_0_0_,
    address2_.city as city2_0_0_,
    address2_.customer_id as customer5_0_0_,
    address2_.street as street3_0_0_,
    address2_.zip as zip4_0_0_ 
from
    Customer this_ 
left outer join
    Address address2_ 
        on this_.id=address2_.customer_id

如果使用QueryDSL加载客户实体,它将运行一个计数查询(这是预期的并且可以接受的),一个选择查询用于客户实体以及结果集中每个客户的一个查询。这意味着,如果我想加载1000个客户,它将运行1002个SQL查询。这会产生大量的网络流量,并减缓应用程序的速度。
new HibernateQuery<Customer>(session).from(QCustomer.customer).fetchResults();

Hibernate与QueryDSL的作用:

select
    count(customer0_.id) as col_0_0_
from
    Customer customer0_

select
    customer0_.id as id1_1_,
    customer0_.name as name2_1_
from
    Customer customer0_

select
    address0_.id as id1_0_1_,
    address0_.city as city2_0_1_,
    address0_.customer_id as customer5_0_1_,
    address0_.street as street3_0_1_,
    address0_.zip as zip4_0_1_,
    customer1_.id as id1_1_0_,
    customer1_.name as name2_1_0_
from
    Address address0_
inner join
    Customer customer1_
        on address0_.customer_id=customer1_.id
where
    address0_.customer_id=?
问题:
是否可以为QueryDSL查询设置类似全局的FetchMode。在Hibernate中,您可以使用@Fetch(FetchMode.JOIN)指定此内容,但不幸的是,QueryDSL会忽略它。
我的目标是使用QueryDSL加载1000个客户,并仅运行2个查询(计数+选择)。
我已经知道有一种方法可以指定这样的内容:
new HibernateQuery<Customer>(session)
    .from(QCustomer.customer)
    .leftJoin(QCustomer.customer.address).fetchJoin()
    .fetchResults();

但这种方式容易出错,因为您必须在每个查询中都指定它,我不想自己声明每个连接。当使用谓词时,QueryDSL已经自动完成了它:

new HibernateQuery<Customer>(session)
    .from(QCustomer.customer)
    .where(QCustomer.customer.address.street.in("Musterstraße 12"))
    .fetchResults();

我想使用上述表达式来加载我的客户,但我不想向我的数据库发送成千上万个请求,也不想自己声明每个联接。这可行吗?

我在这里推送了一个示例项目:https://github.com/MatWein/testproject

1个回答

0

我曾经也遇到过同样的问题。但是我使用了Hibernate JPA Criteria Query来解决它。 如果你想在一次查询中获得结果,那么一种方法是使用

@OneToOne(fetch=FetchType.EAGER)
@JoinColumn(name = "address_id", insertable = false, updatable = false, referencedColumnName = "id")
private Address address;

或者我有一个使用 Criteria Query 的解决方案。也许它可以帮助您将其转换为 DSL。

创建一个 Customer 类的根。

Root<Customer> root = . . .
Join<Customer, Address> join = (Join<Customer, Address>)root.fetch(Customer_.address);

请参考我的问题


这里的问题是在Customer的Address字段上将@OneToOne与mapped by组合使用。我们已经设置了“EAGER”值(它是注释的默认值)。当执行Hibernate标准或按ID加载时,左连接会自动添加(请参见上面的初始问题)。但是,当通过QueryDSL加载时,它会执行单个查询以加载Customer,然后执行N个查询以加载Addresses。目标是配置QueryDSL以自动提供此信息。 - Nathan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接