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