繁体   English   中英

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

[英]Hibernate custom query on Maps generates unwanted subqueries

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

@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')条件。 我发现其他人也遇到了同样的问题,但找不到任何解决方案。

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

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'已经是(key,value)对的值!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM