QueryDSL @OneToOne Join-FetchMode с Hibernate - PullRequest
0 голосов
/ 11 января 2019

Предположим, у нас есть простая сущность "Клиент", которая имеет отношение 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, ...
}

Если вы сейчас загрузите все объекты Customer с помощью 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

Если вы загрузите объект Customer с помощью QueryDSL, он выполнит один запрос подсчета (что ожидается и все в порядке), один запрос на выборку для объекта Customer и один запрос для каждого клиента в наборе результатов. Это означает, что если я захочу загрузить 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=?

Вопрос:
Можно ли установить что-то вроде глобального FetchMode для запросов QueryDSL. В Hibernate вы можете указать это с помощью @Fetch (FetchMode.JOIN), но, к сожалению, это игнорируется QueryDSL.

Таким образом, моя цель - загрузить 1000 клиентов с помощью QueryDSL и выполнить только 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 голосов
/ 14 января 2019

Однажды у меня возникла такая же проблема. Но я работаю с Hibernate JPA критериями Query. Если вы хотите получить свой результат в одном запросе, то одним из способов является использование

@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);

для справки см. Мой Вопрос

...