代码之家  ›  专栏  ›  技术社区  ›  C Brand

安特尔。带有Querydsl/JPQL/Hibernate的then子句中的子查询出现NoViableAltException

  •  1
  • C Brand  · 技术社区  · 9 年前

    我的应用程序中有三个实体:客户、价格级别和临时客户价格级别。每个客户都有一个名为defaultPriceLevel的字段,它直接引用PriceLevel。暂时可以将客户切换到其他PriceLevel。为此,添加了一个引用PriceLevel和Customer的TemporalCustomerPriceLever条目。

    实体类包括:

    @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;
      ...
    }
    

    现在我想查询有效价格水平,即如果不存在(有效,为简单起见,省略了)TemporalCustomerPriceLevel,则查询defaultPriceLever,否则查询由Temporal自定义价格水平引用的价格水平。

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

    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);
    

    这将导致错误:

    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生成的查询如下:

    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
    

    这似乎有道理。此外,如果我替换这两个 JPAExpressions 行,则查询按预期运行。

    因此,主要问题是:在 then -a的条款 case 块我的Querydsl有什么问题吗?任何帮助都是非常感激的。

    1 回复  |  直到 9 年前
        1
  •  0
  •   C Brand    9 年前

    我发现下面的替代查询解决了我的问题。不过,如果有人知道then子句中的hibernate和JPQL子查询,我仍然会感兴趣。

    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()
              )
          )
        );