2016-05-26 1 views
1

私のアプリケーションには、Customer、PriceLevel、およびTemporalCustomerPriceLevelという3つのエンティティがあります。各CustomerにはPriceLevelを直接参照するdefaultPriceLevelというフィールドがあります。一時的に、顧客は代替PriceLevelに切り替えることができます。この目的のために、PriceLevelとCustomerの両方を参照するTemporalCustomerPriceLevelへのエントリが追加されます。Querydsl/JPQL/Hibernateを使用したthen節のサブクエリに対するNolr.NoViableAltException

エンティティクラスは以下のとおりです。

@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(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 

これは理にかなっているようです。さらに、2つのJPAExpressions行を固定PriceLevelオブジェクトで置き換えると、クエリは期待どおりに実行されます。

したがって、主な質問は次のとおりです。caseブロックのthen - 節にサブクエリを使用する際に制約がありますか?私のQuerydslに何か間違っていますか?どんな助けもありがとうございます。

答えて

0

次の代替クエリが私の問題を解決することを確認しました。それでも、誰かが節節とJPQLサブクエリをthen節で知っていれば、私はまだ興味があります。

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() 
     ) 
    ) 
    ); 
関連する問題