Exception when calling treat() twice

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.

Looks like a bug, yes. Please try to create a reproducer with our test case template and if you are able to reproduce the issue, create a bug ticket in our issue tracker and attach that reproducer.

1 Like

I couldn’t find a workaround to make this work. Would it be possible that you consider raising the priority of the issue? My gut feeling is that it should not be too complex to fix it. This bug currently prevents us from upgrading from Hibernate 6 to 7.

I looked into it now since it really seemed easy to fix, yet something people might easily stumble into without a workaround. Anyway, please note that this is an open source project, which means that it lives off of contributions. Either code contributions by users like you or monetary contributions by customers to drive our teams priorities.
Next time you have a blocking issue that hasn’t been worked on by the community yet consider contributing yourself or become a Red Hat or IBM customer and open a support ticket.

I understand this but please consider that Hibernate is a very complex project and it can be extremely difficult to figure out what is causing the problem or even fixing the problem if you are not well aware of the architecture of Hibernate.

Before I opened this ticket (and others) I tried to find a solution or workaround myself. Then I spent time finding a minimal reproducer. All this takes many hours and should already be respected as a contribution to the open source community.

In my opinion it makes more sense to make you aware of simple fixes that potentially affect many more users over time when they are migration to Hibernate 7.

Anyway, thank you for looking into it.

I’m grateful for the details, minimal reproducer etc. that you provided and understand that this takes time on your end as well. It just seems that I have to remind people from time to time that open source is not a free lunch and that you can’t demand or pressure people to get a fix. There are defined ways how you can get fixes into open source, and if you want the projects you use to stay sustainable, you have to play by their rules.