带有Hibernate的QueryDSL @OneToOne Join-FetchMode

时间:2019-01-11 19:55:00

标签: java hibernate querydsl

假设我们有一个简单的实体“客户”,它与实体“地址”具有一个OneToOne关系。外键在地址侧。

@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加载所有Customer实体并将结果查询打印到控制台,则可以看到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加载Customer实体,它将运行一个计数查询(预期和可以),对Customer实体进行一次select查询,对结果集中的每个客户进行一次查询。这意味着,如果要加载1000个客户,它将运行1002个SQL查询。这会占用大量网络流量,并降低应用程序速度。

new HibernateQuery<Customer>(session).from(QCustomer.customer).fetchResults();

使用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个查询(count + select)。

我已经知道有一种方法可以指定这样的内容:

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 :(得分:0)

一旦我遇到同样的问题。但是我正在使用Hibernate JPA条件查询。 如果要在一个查询中获得结果,则一种方法是使用

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

或者我有使用条件查询的解决方案。也许可以帮助您将其转换为DSL。

创建Customer类的根。

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

供参考,请参阅我的Question