Hibernate 7 now requires “downcasting” via treat() when trying to access attributes of a sub type. When calling treat() a second time it should return the treat that has already been added. This doesn’t work and throws an exception
Here is a minimal reproducer of the problem:
@Entity
public class Foo {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "foo", cascade = CascadeType.ALL)
private List<BarParent> barList;
}
@Entity
@Inheritance
@DiscriminatorColumn
public abstract class BarParent {
@Id
@GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "fk_foo")
private Foo foo;
}
@Entity
@DiscriminatorValue("Bar")
public class Bar extends BarParent {
private String barString;
}
try (EntityManager entityManager = entityManagerFactory.createEntityManager()) {
entityManager.getTransaction().begin();
Foo foo = new Foo();
foo.setBarList(List.of(new Bar(), new Bar()));
entityManager.persist(foo);
entityManager.flush();
entityManager.clear();
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<String> query = cb.createQuery(String.class);
Root<Foo> fooRoot = query.from(Foo.class);
ListJoin<Foo, BarParent> barList = fooRoot.joinList("barList");
ListJoin<Foo, Bar> treat = cb.treat(barList, Bar.class);
cb.treat(barList, Bar.class); // <-- This call fails. Without it, there is no problem
Path<String> barString = treat.get("barString");
query.select(barString);
entityManager.createQuery(query).getResultList();
entityManager.getTransaction().commit();
}
This is the exception:
java.lang.ClassCastException: class org.hibernate.metamodel.model.domain.internal.EntityTypeImpl cannot be cast to class org.hibernate.query.sqm.tree.domain.SqmPluralPersistentAttribute (org.hibernate.metamodel.model.domain.internal.EntityTypeImpl and org.hibernate.query.sqm.tree.domain.SqmPluralPersistentAttribute are in unnamed module of loader 'app')
at org.hibernate.query.sqm.tree.domain.AbstractSqmPluralJoin.getModel(AbstractSqmPluralJoin.java:63)
at org.hibernate.query.sqm.tree.domain.SqmListJoin.getModel(SqmListJoin.java:81)
at org.hibernate.query.sqm.tree.domain.SqmListJoin.getModel(SqmListJoin.java:32)
at org.hibernate.query.sqm.tree.domain.AbstractSqmFrom.findTreat(AbstractSqmFrom.java:282)
at org.hibernate.query.sqm.tree.domain.SqmListJoin.treatAs(SqmListJoin.java:148)
at org.hibernate.query.sqm.tree.domain.SqmListJoin.treatAs(SqmListJoin.java:137)
at org.hibernate.query.sqm.tree.domain.SqmListJoin.treatAs(SqmListJoin.java:127)
at org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder.treat(SqmCriteriaNodeBuilder.java:822)
at org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder.treat(SqmCriteriaNodeBuilder.java:182)
at org.hibernate.bugs.JPAUnitTestCase.testNotNullableOrderColumn2(JPAUnitTestCase.java:77)
My understanding is when calling treat it calls org.hibernate.query.sqm.tree.domain.AbstractSqmFrom#findTreat under the hood to return a treat that might have already been added. However, this fails.
Is this a bug? Tested with 7.2.6.