antlr.NoViableAltException for子句中带有Querydsl / JPQL / Hibernate的子查询

antlr.NoViableAltException for subquery in then-clause with Querydsl / JPQL / Hibernate

我的应用程序中有三个实体,Customer,PriceLevel和TemporalCustomerPriceLevel。每个客户都有一个名为defaultPriceLevel的字段,该字段直接引用PriceLevel。可以暂时将客户切换到另一个PriceLevel。为此,向TemporalCustomerPriceLevel添加了一个条目,该条目同时引用PriceLevel和客户。

实体类为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Entity
public class Customer implements Serializable {
...
  @ManyToOne
  @JoinColumn(name ="default_price_level_id")
  private PriceLevel defaultPriceLevel;

  @OneToMany(mappedBy ="customer", cascade = CascadeType.ALL, orphanRemoval = true)
  private List<TemporalCustomerPriceLevel> temporalCustomerPriceLevels
      = new ArrayList<TemporalCustomerPriceLevel>();
...
}

@Entity
public class PriceLevel implements Serializable {
  ...
  @OneToMany(mappedBy ="priceLevel", cascade = CascadeType.ALL, orphanRemoval = true)
  private List<TemporalCustomerPriceLevel> temporalCustomerPriceLevels;

  @OneToMany(mappedBy ="defaultPriceLevel", cascade = CascadeType.PERSIST)
  private List<Customer> customers;
  ...
}

@Entity
public class TemporalCustomerPriceLevel implements Serializable {
  ...
  @ManyToOne
  @JoinColumn(name ="customer_id")
  private Customer customer;

  @ManyToOne(cascade = CascadeType.PERSIST)
  @JoinColumn(name ="price_level_id")
  private PriceLevel priceLevel;
  ...
}

我现在要查询有效价格水平,即。 e。如果没有,则为defaultPriceLevel(活动,为简单起见,省略)TemporalCustomerPriceLevel,否则存在TemporalCustomerPriceLevel引用的PriceLevel。

我使用Spring,JPA(休眠),MySql和Querydsl。在Querydsl中,我编写了以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
QCustomer customer = QCustomer.customer;
QTemporalCustomerPriceLevel qt = QTemporalCustomerPriceLevel.temporalCustomerPriceLevel;

SimpleExpression<PriceLevel> cases = new CaseBuilder()
    .when(JPAExpressions.select(qt.count()).from(qt).where(qt.customer.eq(customer)).eq(1L))
    .then(
        JPAExpressions.select(qt.priceLevel).from(qt).where(qt.customer.eq(customer))
        )
    .otherwise(
        JPAExpressions.select(customer.defaultPriceLevel).from(customer)
        ).as("activePriceLevel");

JPAQuery<Tuple> query = factory.select(customer, cases).from(customer);

这会导致错误:

1
2
3
antlr.NoViableAltException: unexpected AST node: query
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.expr(HqlSqlBaseWalker.java:1367) [hibernate-core-4.3.11.Final.jar:4.3.11.Final]
...

Querydsl生成的查询如下所示:

1
2
3
4
5
6
7
8
9
10
11
select customer,
    (case when ((select count(temporalCustomerPriceLevel)
        from TemporalCustomerPriceLevel temporalCustomerPriceLevel
        where temporalCustomerPriceLevel.customer = customer) = ?1)
    then (select temporalCustomerPriceLevel.priceLevel
        from TemporalCustomerPriceLevel temporalCustomerPriceLevel
        where temporalCustomerPriceLevel.customer = customer)
    else (select customer.defaultPriceLevel
        from Customer customer)
    end) as activePriceLevel
from Customer customer

这似乎是有道理的。此外,如果我用固定的PriceLevel对象替换了两个JPAExpressions行,查询将按预期运行。

因此,主要问题是:在case块的then子句中使用子查询是否有任何约束?我的Querydsl有什么问题吗?任何帮助都非常感谢。


我已经解决了以下替代查询可以解决我的问题。 不过,如果有人知道当时的休眠和JPQL子查询,我仍然会感兴趣。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
JPAQueryFactory factory = new JPAQueryFactory(em);
QCustomer customer = QCustomer.customer;
QPriceLevel priceLevel = QPriceLevel.priceLevel;
QTemporalCustomerPriceLevel tmp = new QTemporalCustomerPriceLevel("tmp_qtcpl");
Date now = new Date();

JPAQuery<Tuple> query = factory.select(customer, priceLevel).from(customer, priceLevel).where(
    JPAExpressions.select(tmp).from(tmp)
    .where(tmp.validAfter.loe(now)
        .and(tmp.validUntil.after(now)
            .and(tmp.priceLevel.eq(priceLevel)
                .and(tmp.customer.eq(customer))))).exists()
    .or(
      customer.defaultPriceLevel.eq(priceLevel)
      .and(
          JPAExpressions.select(tmp).from(tmp)
          .where(tmp.validAfter.loe(now)
              .and(tmp.validUntil.after(now)
                  .and(tmp.customer.eq(customer)))).notExists()
          )
      )
    );