Пользовательский запрос Hibernate на Картах генерирует нежелательные подзапросы - PullRequest
0 голосов
/ 09 марта 2019

У меня есть класс Покупка с картой внутри.

@Entity
public class Purchase {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Integer purchaseId;

    @ManyToOne
    @JoinColumn(name = "customer_id")
    private User customer;

    @ManyToOne
    @JoinColumn(name = "item_id")
    private Item item;

    private String customization;

    @ElementCollection
    @MapKeyEnumerated(value = EnumType.STRING)
    @CollectionTable(name = "purchase_status")
    @MapKeyColumn(name = "status")
    @Column(name = "date")
    private Map<PurchaseStatus, Date> statusTransitions = new HashMap<>();

    private Date expectedDeliveryDate;

    @ManyToOne
    @JoinColumn(name = "purchase_cart_id")
    @JsonIgnore
    private PurchaseCart purchaseCart;

    @OneToOne
    @JoinColumn(name = "destination_address_id")
    private DestinationAddress destinationAddress;

    @Transient
    @JsonIgnore
    private Date purchaseDate;

Это представляет покупку.PurchaseStatus является перечислителем.statusTransitions представляет весь статус, полученный этой покупкой.В частности, это могут быть 'READY_TO_BE_PAID' и 'PAID'.Статус имеет относительную дату, которая является ЗНАЧЕНИЕМ карты, а сам статус является КЛЮЧОМ карты только потому, что покупка может быть в точном состоянии только один раз.Я хотел бы получить из базы данных все покупки с определенным пользователем (имя пользователя, которое является атрибутом пользователя) и с определенной парой (KEY, VALUE) внутри HashMap statusTransitions.В частности, я хотел бы создать запрос, который возвращает все покупки, где Date (VALUE) находится между двумя датами (fromDate и toDate), а PurchaseStatus (KEY) равен значению enum 'READY_TO_BE_PAID'.

Я создал этот пользовательский запрос:

@Query("select p from Purchase p JOIN p.customer c JOIN p.statusTransitions s WHERE c.username = :username and " +
            "(KEY(s) = 'READY_TO_BE_PAID' and " +
            "VALUE(s) >= :fromDate and " +
            "VALUE(s) <= :toDate)")
List<Purchase> findByUsernameAndByDate(@Param("fromDate") Date fromDate, @Param("toDate") Date toDate, @Param("username") String username);

Единственная проблема заключается в том, что генерируемый SQL-запрос выглядит следующим образом:

select 
*
from 
  purchase purchase0_ 
    inner join user user1_ on purchase0_.customer_id=user1_.user_id 
    inner join purchase_status statustran2_ on purchase0_.purchase_id=statustran2_.purchase_purchase_id 
  where 
    statustran2_.status='READY_TO_BE_PAID' and 
    user1_.username=? and 
      (select 
        statustran2_.date 
      from 
        purchase_status statustran2_ 
      where 
        purchase0_.purchase_id=statustran2_.purchase_purchase_id
        )>=? and 
      (select 
        statustran2_.date 
      from 
        purchase_status statustran2_ 
      where 
        purchase0_.purchase_id=statustran2_.purchase_purchase_id
      )<=?

Это не то, что я хочу.Он генерирует два подзапроса и в результате выдает эту ошибку:

java.sql.SQLException: Subquery returns more than 1 row

Потому что, когда он выполняет подзапрос, он не фильтрует строки в PurchaseStatus, возвращая таким образом более одной строки.Дело в том, что я не знаю, как переписать запрос, чтобы избежать этих двух подзапросов или поместить в условие WHERE это условие (KEY (s) = 'READY_TO_BE_PAID').Я нашел других людей, у которых была такая же проблема, но я не мог найти никакого решения.

1 Ответ

0 голосов
/ 09 марта 2019

Я нашел решение!Кажется, что когда у вас есть этот запрос

SELECT p 
FROM Purchase p 
    JOIN p.customer c 
    JOIN p.statusTransitions s

, псевдоним 's карты statusTransactions уже является значением, а не парой (KEY, VALUE), в которую вы склонны верить.Это немного противоречит интуиции, потому что KEY возвращает ключ, но VALUE не возвращает значение, а вместо этого автоматически генерирует подзапрос, который неизбежно приводит к ошибкам.

java.sql.SQLException: Subquery returns more than 1 row

Так что правильный способ переписать это:

SELECT p 
FROM Purchase p 
    JOIN p.customer c 
    JOIN p.statusTransitions s 
WHERE 
    c.username = :username and
    KEY(s) = 'READY_TO_BE_PAID' and
    VALUE(s) >= :fromDate and
    VALUE(s) <= :toDate

- это:

SELECT p 
FROM Purchase p 
    JOIN p.customer c 
    JOIN p.statusTransitions s 
WHERE 
    c.username = :username and
    KEY(s) = 'READY_TO_BE_PAID' and
    s >= :fromDate and
    s <= :toDate

или, более компактно:

SELECT p 
FROM Purchase p 
    JOIN p.customer c 
    JOIN p.statusTransitions s 
WHERE 
    c.username = :username and
    KEY(s) = 'READY_TO_BE_PAID' and
    s BETWEEN :fromDate and :toDate

, где, как я уже говорил, псевдоним 's' уже является значением(ключ, значение) пара!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...