Validation failed for query, cannot compare left expression of type with right expression of type
Asked Answered
E

3

6

Just upgraded from Spring Boot 3.1.5 to 3.2.0 and started having Cannot compare left expression of type 'com.acme.domain.AbstractEntity' with right expression of type 'com.acme.domain.User' in several of our repositories.

The definitions are:

public class Auditing<T extends AbstractEntity & Identifiable, E extends Enum<?>>
...
public class User extends AbstractEntity implements Identifiable<Long>
...
public class AuditEventUser extends Auditing<User, UserEventType>
...

Where Identifiable<T> is an interface just with a T getId() method.

And the failing query:

@Query("""
    SELECT MAX(h.eventInstant)
      FROM User u
      JOIN AuditEventUser a ON a.entity = u
      JOIN a.eventHistory h
     WHERE u.username = :username
       AND a.event = :event
    """)
  Instant getUserEventInstant(@Param("username") String username, @Param("event") UserEventType event);

I have thoroughly checked other similar questions and they mostly suggest upgrading to Hibernate 6.4, but it seems an unrelated problem. In our case it seems that Hibernate is flat out ignoring the Identifiable bound and fails to find an id on AbstractEntity. What do we have to do to get our queries working again?

ALso found this Validation failed for query for method upgrading from Spring Boot 3.1.5 to 3.2.0 but the accepted answer is just a workaround that we cannot afford.

Eloign answered 12/12, 2023 at 18:27 Comment(2)
This sounds like a bug in Hibernate 6.2.x. I had a similar problem with Spring Boot 3.1.x, which is tied to Hibernate 6.1.x. Upgrading Hibernate to its next minor version (6.2) didn't work, since there were incompatible interface changes. So I doubt upgrading from Hibernate 6.2 to 6.4 would work here. Fortunately, the problem was solved for Hibernate 6.0 soon and I could continue my work.Gailey
Thanks for your comment @Karsten. It seems to me that it's not that the problem is fixed on Hibernate 6.0, but instead that it simply doesn't actually check for the types no? I am still trying to figure out what the next steps are since we use this construct in multiple places and refactoring everything to please Hibernate would be very expensive.Eloign
M
3

Overcame the similar issue when I tried to upgrade my spring boot version from 2.7 to 3.2.1.

Initial query:

@Query("SELECT * FROM User u INNER JOIN UserRole ur ON ur.user = u.id WHERE u.id = '1'")

This worked for me:

@Query("SELECT * FROM User u INNER JOIN UserRole ur ON ur.user.id = u.id WHERE u.id = '1'")

where UserRole Entity class will have the relation/mapping to User class.

public class UserRole {
 @ManyToOne
 @JoinColumn(name = "user_Id", referencedColumnName = "id")
 private User user;
}

In the latest version of hibernate(8.x), hibernate query validations are done in a way that is expects the same data type on join statements. Initially ur.user = u.id worked as it implicitly fetched the id field from user (ur.user.id) but now it expects us to provide the exact field to evaluate. So it throwed semantic exception saying string is assigned to User type. Changing it to ur.user.id = u.id fixed the issue for me

Motto answered 11/1 at 8:31 Comment(2)
I think this fails to address the main point of the original question regarding generics, for which this answer is not applicable. Refactoring your example to match the problem: UserRole should be UserRole extends Role<User> where Role is the actual entity, defined as public class Role<U extends AbstractEntity & Identifiable>. In any case, I appreciate your input, thank you.Eloign
I tried this suggestion but I get same error !Wroughtup
E
0

In case this helps someone, for that specific situation, we were able to work around the issue by refactoring the queries a bit and using TREAT, like:

@Query("""
SELECT MAX(h.eventInstant)
      FROM AuditEventUser a
      JOIN TREAT(a.entity as User) u
      JOIN a.eventHistory h
     WHERE u.username = :username
       AND a.event = :event
""")

Unfortunately, while this change is easy to apply on these static queries, it is not so obvious how to apply to the places using dynamic queries by simply using CriteriaBuilder#treat without a lot of refactoring effort.

One might say it's not a workaround, but actually the way it should be to begin with. I will leave that to the reader as a thought exercise.

Eloign answered 14/12, 2023 at 8:26 Comment(0)
A
0

Still having the same issue even with SpringBoot version 3.2.4 which uses Hibernate version 6.4.4 Final. I'm using @Repository inside @Service to retrieve @Entity and the only workaround that I found was using another @Repository to retrieve the joined entity and then using this entity to search maching entity.

As an example, in my AServiceImpl

public List<ADto> getAByB(String idB) {

    Optional<B> b = bRepository.findById(idB);
    if(b.isPresent()) {
        List<A> list = aRepository.findByB(b.get());
        return aConverter.createFromEntities(list);
    } else
        return Collections.emptyList();
}
Alum answered 9/4 at 13:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.