JPA (Hibernate) / QueryDSL левое соединение с условием не работает - PullRequest
0 голосов
/ 25 июня 2018

У меня есть две таблицы - user и booking.У каждого пользователя может быть много заказов (отношение один ко многим).

user:                          booking:

    id | name    |               id | country    | user_id | price   |
    -------------|               ------------------------------------|
     1 | Alice   |                1 | Italy      | 1       | 2000    |
     2 | Bob     |                2 | France     | 1       | 2500    |
                                  3 | Spain      | 1       | 3000    |

Я хочу выбрать всех пользователей и все заказы, цена которых превышает 2000, используя Query DSL.Если у пользователя нет заказов или заказы не соответствуют условию, я все еще хочу выбрать этого пользователя.

Во-первых, давайте посмотрим, как это будет выглядеть при использовании простого SQL-запроса на левое соединение:

SELECT u.*, b.* FROM user u LEFT JOIN booking b ON u.id = b.user_id AND b.price > 2000

Приведенный выше запрос должен дать следующий результат:

    id | name    | id    | country    | user_id | price   |
    -------------|----------------------------------------|
     1 | Alice   | 2     | France     | 1       | 2500    |
     1 | Alice   | 3     | Spain      | 1       | 3000    |
     2 | Bob     | null  | null       | null    | null    |

Теперь я хочу сделать это, используя JPA с Query DSL

Связанные с JPA вещи:

@Entity
public class User {

    @Id
    private Long id;
    private String name;

    @OneToMany(cascade = ALL, fetch = EAGER, orphanRemoval = true, mappedBy = "user")
    private List<Booking> bookings;

    // getters and setters
}

@Entity
public class Booking {

    @Id
    private Long id;
    private String name;
    private Integer price;

    @ManyToOne(fetch = LAZY)
    @JoinColumn(name = "user_id")
    private User user;

    // getters and setters
}

Запрос DSL:

public List<User> getUsersAndBookings() {
    QUser user = QUser.user;
    QBooking booking = QBooking.booking;

    JPAQuery<User> jpaQuery = new JPAQuery(entityManager);

    List<User> result = jpaQuery.from(user).leftJoin(user.bookings, booking).on(booking.price.gt(2000)).fetchJoin().fetch();

    return result;
}

На самом деле этот код не работает, и я получаю следующее исключение:

org.hibernate.hql.internal.ast.QuerySyntaxException: with-clause not allowed on fetched associations; use filters [select user from com.example.demo.entity.User user left join fetch user.bookings as booking with booking.price > ?1]

Проблема в том, что условие условие указано в on метод -on(booking.price.gt(2000)).

После некоторых исследований я обнаружил, что это условие должно быть указано в методе where и должно выглядеть так:

List<User> result = jpaQuery.from(user).leftJoin(user.bookings, booking).where(booking.price.gt(2000)).fetchJoin().fetch();

Это работает, но не так, как я ожидал бы, что оно будет работать,поскольку он не возвращает ВСЕХ пользователей, он возвращает только одного пользователя (Алису), у которого есть несколько заказов, соответствующих условию условия.По сути, он просто фильтрует объединенную таблицу (таблицу результатов после операции левого соединения), и это не то, что я ищу.

Я хочу получить всех пользователей, и если нет никаких бронирований для конкретного пользователя, тогда просто укажите null вместо списка бронирования для этого пользователя.

Пожалуйста, помогите, боролись в течение нескольких часов без какого-либо успеха.

Используемые версии:

  • Spring Boot 2.0.2
  • Spring Data JPA 2.0.7
  • Hibernate 5.2.16.Final
  • QueryDSL 4.1.4

1 Ответ

0 голосов
/ 26 июня 2018

Вы можете использовать выражение isNull в выражении where, чтобы получить строки с нулевыми значениями.

Ваш запрос должен выглядеть следующим образом:

jpaQuery.from(user)
    .leftJoin(user.bookings, booking)
    .fetchJoin()
    .where(booking.price.gt(2000).or(booking.id.isNull())).fetch();

Произведенный в Hibernate запрос:

select
user0_.id as id1_1_0_,
bookings1_.id as id1_0_1_,
user0_.name as name2_1_0_,
bookings1_.country as country2_0_1_,
bookings1_.price as price3_0_1_,
bookings1_.user_id as user_id4_0_1_,
bookings1_.user_id as user_id4_0_0__,
bookings1_.id as id1_0_0__
from
user user0_
left outer join
booking bookings1_
on user0_.id=bookings1_.user_id
where
bookings1_.id is null
or bookings1_.price>?
...