I tried to migrate my application from Hibernate 5.4.30.Final
to 6.1.6.Final
, database H2 2.1.214. I observed a different behaviour regarding generics when using a CriteriaQuery
. I have stripped it down to a testcase (which does not make any sense but shows the problem). In Hibernate 5 the following query to a generic field name
runs fine whereas Hibernate 6 throws an Exception.
CriteriaBuilder cb = eMgr.getCriteriaBuilder();
CriteriaQuery<String> cr = cb.createQuery(String.class);
Root<Person> person = cr.from(Person.class);
cr.select(person.<String> get("name"));
TypedQuery<String> query = eMgr.createQuery(cr);
Exception:
Converting `org.hibernate.query.QueryTypeMismatchException` to JPA `PersistenceException` : Specified result type [java.lang.String] did not match Query selection type [java.lang.Object] - multiple selections: use Tuple or array
Here are my sample class definitions:
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
@Entity
public class GenericPerson<T>
{
@Id
@GeneratedValue(generator = "increment")
private long id;
private T name;
public GenericPerson() { }
public GenericPerson(T name) { this.name = name;}
public T getName() { return this.name; }
public void setName(T name) { this.name = name; }
public long getId() { return this.id;}
public void setId(long id) { this.id = id; }
}
@Entity
public class Person extends GenericPerson<String>
{
public Person() { }
public Person(String name) { super(name); }
}
Hibernate 5 seems to handle generics differently to Hibernate 6 but I could not find any hint in the migration document. Why fails the test case with Hibernate 6?
CodePudding user response:
In Hibernate 5, when using a generic field in a CriteriaQuery
, the type parameter of the generic field is inferred at runtime. However, in Hibernate 6, the type parameter of the generic field needs to be explicitly specified at compile-time.
One way to fix this issue is to explicitly specify the type of the "name" field in the CriteriaQuery
, by using the "as" method on the Root object:
cr.select(person.as(String.class).get("name"));
This tells Hibernate 6 that the "name" field should be considered a String, and it should work as expected.
Alternatively, if you want to keep the generic structure you can use the "get" method with a Class parameter like this:
cr.select(person.<String>get("name", String.class));
This tells Hibernate 6 that you are expecting a String value from the field "name" of the class Person.