2016-05-26 11 views
1

У меня есть три объекта в моем приложении, Customer, PriceLevel и TemporalCustomerPriceLevel. Каждый клиент имеет поле, называемое defaultPriceLevel, которое напрямую ссылается на PriceLevel. Временно клиенты могут быть переключены на альтернативный PriceLevel. С этой целью добавляется запись в TemporalCustomerPriceLevel, которая ссылается как на PriceLevel, так и на Customer.antlr.NoViableAltException для подзапроса в then-clause с Querydsl/JPQL/Hibernate

Классы сущностей являются:

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

Теперь я хочу, чтобы запросить активный уровень цен, я. е. defaultPriceLevel, если нет (активный, опущен для простоты) TemporalCustomerPriceLevel существует, а PriceLevel ссылается на TemporalCustomerPriceLevel в противном случае.

Я использую 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 фиксированными объектами PriceLevel, запрос выполняется так, как ожидалось.

Итак, главный вопрос: есть ли какое-либо ограничение на использование подзапросов в then -раздел блока case? Что-то не так с моим Querydsl? Любая помощь очень ценится.

ответ

0

Я разработал, что следующий альтернативный запрос решает мою проблему. Тем не менее, если кто-нибудь знает о подзапросах 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() 
     ) 
    ) 
    );