Hello,
if I have a One-To-Many relationship between 2 tables, let’s say person and car and I want to select every person without a car, my SQL would look something like this:
select * from person p where
(select count(*) from car c left join person per on c.person = per.id where per.id = p.id) = 0;
In Hibernate 5 we used a generalized specification for these cases. Something like this:
public class HasNoElementsSpecificationOneToMany<T, F> implements Specification<T>
{
private final String subPath;
private final Class<F> subClass;
public HasNoElementsSpecificationOneToMany(final String subPath, final Class<F> subClass)
{
this.subPath = subPath;
this.subClass = subClass;
}
@Override
public Predicate toPredicate(final Root<T> root, final CriteriaQuery<?> query, final CriteriaBuilder cb)
{
final Subquery<Long> sub = query.subquery(Long.class);
final Root<F> subFrom = sub.from(this.subClass);
final Path<T> processedPath = subFrom.get(this.subPath);
sub.select(cb.count(subFrom));
sub.where(cb.equal(processedPath, root));
return cb.equal(sub, Long.valueOf(0));
}
}
This worked as expected, however after migrating to Hibernate 6 we’re getting an exception with this root cause:
Caused by: org.hibernate.query.sqm.InterpretationException: Error interpreting query [SqmRoot not yet resolved to TableGroup]; this may indicate a semantic (user query) problem or a bug in the parser [SqmRoot not yet resolved to TableGroup]
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitRootPath(BaseSqmToSqlAstConverter.java:3413) ~[hibernate-core-6.1.2.Final.jar:6.1.2.Final]
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitRootPath(BaseSqmToSqlAstConverter.java:415) ~[hibernate-core-6.1.2.Final.jar:6.1.2.Final]
at org.hibernate.query.sqm.tree.from.SqmRoot.accept(SqmRoot.java:160) ~[hibernate-core-6.1.2.Final.jar:6.1.2.Final]
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitComparisonPredicate(BaseSqmToSqlAstConverter.java:6562) ~[hibernate-core-6.1.2.Final.jar:6.1.2.Final]
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitComparisonPredicate(BaseSqmToSqlAstConverter.java:415) ~[hibernate-core-6.1.2.Final.jar:6.1.2.Final]
at org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate.accept(SqmComparisonPredicate.java:104) ~[hibernate-core-6.1.2.Final.jar:6.1.2.Final]
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitWhereClause(BaseSqmToSqlAstConverter.java:2263) ~[hibernate-core-6.1.2.Final.jar:6.1.2.Final]
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQuerySpec(BaseSqmToSqlAstConverter.java:1834) ~[hibernate-core-6.1.2.Final.jar:6.1.2.Final]
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQuerySpec(BaseSqmToSqlAstConverter.java:415) ~[hibernate-core-6.1.2.Final.jar:6.1.2.Final]
at org.hibernate.query.sqm.tree.select.SqmQuerySpec.accept(SqmQuerySpec.java:122) ~[hibernate-core-6.1.2.Final.jar:6.1.2.Final]
at org.hibernate.query.sqm.spi.BaseSemanticQueryWalker.visitQueryPart(BaseSemanticQueryWalker.java:213) ~[hibernate-core-6.1.2.Final.jar:6.1.2.Final]
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQueryPart(BaseSqmToSqlAstConverter.java:1690) ~[hibernate-core-6.1.2.Final.jar:6.1.2.Final]
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSubQueryExpression(BaseSqmToSqlAstConverter.java:6049) ~[hibernate-core-6.1.2.Final.jar:6.1.2.Final]
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSubQueryExpression(BaseSqmToSqlAstConverter.java:415) ~[hibernate-core-6.1.2.Final.jar:6.1.2.Final]
at org.hibernate.query.sqm.tree.select.SqmSubQuery.accept(SqmSubQuery.java:641) ~[hibernate-core-6.1.2.Final.jar:6.1.2.Final]
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitComparisonPredicate(BaseSqmToSqlAstConverter.java:6552) ~[hibernate-core-6.1.2.Final.jar:6.1.2.Final]
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitComparisonPredicate(BaseSqmToSqlAstConverter.java:415) ~[hibernate-core-6.1.2.Final.jar:6.1.2.Final]
at org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate.accept(SqmComparisonPredicate.java:104) ~[hibernate-core-6.1.2.Final.jar:6.1.2.Final]
...
Is there a way I can fix the specification so it works with the current version? I’m currently unsure why the object mapper doesn’t seem to like this subselect anymore.
Any insights would be very much appreciated.