地图上的休眠自定义查询会生成不需要的子查询

时间:2019-03-09 09:55:40

标签: java spring hibernate jpa

我有一堂课,里面有地图。

@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”。状态具有它的相对日期,即地图的VALUE,状态本身就是地图的KEY,因为购买只能处于一次精确的状态。 我想从数据库中获取所有带有特定用户(用户名,这是用户的属性)的购买,以及statusTransitions HashMap中具有特定(键,值)对的所有购买。 特别是,我想创建一个查询,该查询返回日期(VALUE)在两个日期(fromDate和toDate)之间且PurchaseStatus(KEY)等于枚举值“ 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 :(得分:0)

我找到了解决方案!看来,当您有此查询

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

statusTransactions映射的别名“ s”已经是值,而不是使您相信的对(键,值)。这有点直观,因为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”已经是(键,值)对的值!