5

Does anyone know why Hibernate throws this error?

org.hibernate.query.SemanticException: Could not interpret path expression 'com.example.entity.security.AccountStatus.Description.ACTIVE'
at org.hibernate.query.hql.internal.BasicDotIdentifierConsumer$BaseLocalSequencePart.resolvePathPart(BasicDotIdentifierConsumer.java:256) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final]
at org.hibernate.query.hql.internal.BasicDotIdentifierConsumer.consumeIdentifier(BasicDotIdentifierConsumer.java:91) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final]
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitSimplePath(SemanticQueryBuilder.java:4808) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final]
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitIndexedPathAccessFragment(SemanticQueryBuilder.java:4755) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final]
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitGeneralPathFragment(SemanticQueryBuilder.java:4724) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final]
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitGeneralPathExpression(SemanticQueryBuilder.java:1423) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final]
at org.hibernate.grammars.hql.HqlParser$GeneralPathExpressionContext.accept(HqlParser.java:6963) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final]

The repository with the reportedly incorrect query looks like this:

@Repository
public interface UserAccountRepository extends JpaRepository<UserAccount, Long> {

    @EntityGraph(type = EntityGraph.EntityGraphType.FETCH, attributePaths = {
            "accountStatus"
    })
    @Query("""
            SELECT user
            FROM UserAccount user
            WHERE user.userId = ?1
            AND user.accountStatus.description = com.example.entity.security.AccountStatus.Description.ACTIVE
            """)
    Optional<UserAccount> findActiveByUserId(Long userId);
}

And the enum Description, which is inside the entity AccountStatus, looks like that:

@Entity
public class AccountStatus {

    //...

    @JsonIgnoreType
    public enum Description {
        ACTIVE, INACTIVE, DISABLED, BANNED;

        private static Map<String, Description> descriptionByName;

        public static Description getByName(String name) {
            if (descriptionByName == null) {
                descriptionByName = Arrays.stream(values())
                        .collect(Collectors.toMap(Description::name, d -> d));
            }
            return descriptionByName.get(name);
        }
    }
}

Strangely enough, a direct comparison against a string works. So this query doesn't cause any problems:

@Query("""
        SELECT user
        FROM UserAccount user
        WHERE user.userId = ?1
        AND user.accountStatus.description = 'ACTIVE'
        """)

Only the comparison against the corresponding enum value throws the error. Does anyone know what this could be?

Many thanks for any help

Slevin
  • 617
  • 1
  • 7
  • 17
  • You should use a second argument `?2` for the description and modify your method something like `findActiveByUserIdAndDescription( Long userId, Description description)`. – dariosicily Mar 05 '23 at 18:29
  • So for a beginner to understand: it is not possible to match against an enum directly in the HQL query? Is it at least possible to get the 2nd parameter into the query somehow easier than using a method created solely for this purpose? – Slevin Mar 05 '23 at 18:40
  • 1
    You are right because I was not accurate before: you can use a more general method like `findByUserIdAndDescription( Long userId, Description description)` with two arguments `?1` and `?2` in the query or as you have made one method for every status and then one argument. – dariosicily Mar 05 '23 at 18:52
  • 1
    No that's fine, I understood you correctly^^. I was just not aware that there is no way to make a direct comparison within the query itself. Passing explicit parameters is of course a solution that works, thanks for that. I just thought I could avoid that. – Slevin Mar 05 '23 at 18:58
  • Glad to have helped, [here](https://stackoverflow.com/questions/17242408/spring-query-annotation-with-enum-parameter) and probably [here](https://stackoverflow.com/questions/17242408/spring-query-annotation-with-enum-parameter) an example pertinent to your question. – dariosicily Mar 05 '23 at 19:41
  • Very nice solution proposed below. – dariosicily Mar 06 '23 at 21:13

1 Answers1

5

An inner class in Java is separated by $, not a ., which is why you'd have to use com.example.entity.security.AccountStatus$Description.ACTIVE if you want to use the fully qualified name.

I would suggest you to simply use the simple name (ACTIVE) though, or the simple qualified name (Description.ACTIVE) if possible.

Christian Beikov
  • 15,141
  • 2
  • 32
  • 58
  • Although I will stick with the solution suggested by dariosicily (which is kind of what you'd suggest anyway), this is the answer I was looking for. Your code works as initially hoped, thanks for the clarification. – Slevin Mar 06 '23 at 19:44
  • Btw, could you give an example how to use the simple qualified name within an JPA/HQL query? As far as I can tell only fully qualified names do work (with love and greets from carinthia^^). – Slevin Mar 06 '23 at 19:58
  • 1
    In Hibernate 6 we introduced support for the short hand syntax. For Hibernate 5 I believe that you have to use the FQN in HQL, but as you figured, it's also possible to compare against the JDBC representation i.e. the String enum name or ordinal. – Christian Beikov Mar 07 '23 at 08:19