I am trying to write a query to back a search API. Specifically for a flag searchDrafts – if the flag is true I have to get rows with status DRAFT and else I have to get all rows that have any status other than DRAFT.
In regular SQL, the below query works fine:
SELECT id, status FROM records where ((status = 'DRAFT') = :searchDrafts);
However, a similar thing in JPQL doesn’t work:
SELECT r FROM Records r WHERE ((r.status = 'DRAFT') = :searchDrafts);
It gives the error:
unexpected AST node: = near line 1, column nn
Is there any way in JPQL to use the value of a boolean result in another expression?
The alternative would be doing it the longer way, but its a bit verbose. This works fine in JPQL:
SELECT id, status FROM records where (:searchDrafts=true AND (status = 'DRAFT')) or (:searchDrafts=false AND (status != 'DRAFT'));
Advertisement
Answer
JPQL grammar from Jakarta Persistence 3.0 specification doesn’t allow that. (I had 3.0 by hand, but I don’t expect that earlier ones differ.) Boolean comparison corresponds to this production rule:
comparison_expression ::=
...
boolean_expression {= | <>} {boolean_expression | all_or_any_expression} |
...
Then comparison_expression is only ever encountered as a part of conditional_expression production rules:
conditional_expression ::= conditional_term | conditional_expression OR conditional_term
conditional_term ::= conditional_factor | conditional_term AND conditional_factor
conditional_factor ::= [NOT] conditional_primary
conditional_primary ::= simple_cond_expression | (conditional_expression)
simple_cond_expression ::=
comparison_expression |
...
And conditional_expression is what goes directly in WHEN, WHERE, HAVING and ON clauses. This means that comparison_expression cannot be a boolean_expression.
Apart from your solution with AND, this one seems to work too:
SELECT r FROM Records r WHERE CASE WHEN r.status = 'DRAFT' THEN TRUE ELSE FALSE END = :searchDrafts;
It’s quite ugly, but it does convert a conditional_expression into a boolean_expression.
In practice you’d better have two different queries: one for “=” and the other for “<>”.
Overall, it feels like an omission in the JPA standard.