Hi,
I’m trying to add PostgreSQL jsonb_exists_any
function support to hibernate 6.3.1.Final. However it seems to have issues understanding its return type is Boolean, with this error:
Cannot compare left expression of type ‘java.lang.Object’ with right expression of type ‘java.lang.Boolean’
I defined it as follows:
public class JsonbExistsAnyFunction extends NamedSqmFunctionDescriptor {
public static final JsonbExistsAnyFunction INSTANCE = new JsonbExistsAnyFunction();
public JsonbExistsAnyFunction() {
super("jsonb_exists_any", true,
StandardArgumentsValidators.min(2),
FunctionProviderUtils.basicTypeToFunctionTypeResolver(StandardBasicTypes.BOOLEAN));
}
@Override
public void render(SqlAppender sqlAppender, List<? extends SqlAstNode> sqlAstArguments, SqlAstTranslator<?> translator) {
sqlAppender.appendSql("jsonb_exists_any(");
translator.render(sqlAstArguments.get(0), SqlAstNodeRenderingMode.DEFAULT);
sqlAppender.appendSql(", ARRAY[");
var serviceIdArguments = sqlAstArguments.subList(1, sqlAstArguments.size());
for (int i = 0; i < serviceIdArguments.size(); i++) {
translator.render(serviceIdArguments.get(i), SqlAstNodeRenderingMode.DEFAULT);
if (i < serviceIdArguments.size() - 1) {
sqlAppender.appendSql(",");
}
}
sqlAppender.appendSql("]");
}
}
Defined this util to define the FuctionReturnType:
public class FunctionProviderUtils {
public static FunctionReturnTypeResolver basicTypeToFunctionTypeResolver(BasicTypeReference<?> type) {
return new FunctionReturnTypeResolverInternal(type);
}
public static class FunctionReturnTypeResolverInternal implements FunctionReturnTypeResolver {
private BasicTypeReference<?> basicTypeReference;
public FunctionReturnTypeResolverInternal(BasicTypeReference<?> type) {
basicTypeReference = type;
}
@Override
public String getReturnType() {
return basicTypeReference.getName();
}
@Override
public ReturnableType<?> resolveFunctionReturnType(ReturnableType<?> returnableType, List<? extends SqmTypedNode<?>> list, TypeConfiguration typeConfiguration) {
return basicTypeReference == null ? null : typeConfiguration.getBasicTypeRegistry().resolve(basicTypeReference);
}
@Override
public BasicValuedMapping resolveFunctionReturnType(Supplier<BasicValuedMapping> supplier, List<? extends SqlAstNode> list) {
return basicTypeReference != null && supplier != null ? (BasicValuedMapping)supplier.get() : null;
}
}
}
And added it to an ExtendedDialect:
public class ExtendedDialect extends PostgreSQLDialect {
public ExtendedDialect() {
super();
}
@Override
public void contributeFunctions(FunctionContributions functionContributions) {
super.contributeFunctions(functionContributions);
var registry = functionContributions.getFunctionRegistry();
registry.register("jsonb_exists_any", JsonbExistsAnyFunction.INSTANCE);
}
}
Which is then added to JPA properties like this:
properties.setProperty("hibernate.dialect", "com.example.ExtendedDialect");
And this is the repository method:
@Query("SELECT i.id FROM Indicator i WHERE jsonb_exists_any(i.serviceIds, :serviceIds) = true")
List<Long> findAllMatchingServiceId(@Param("serviceIds") List<String> serviceIds);
for this entity:
@Entity
@Table(name = "indicator")
public class Indicator {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
protected Long id;
@JdbcTypeCode(SqlTypes.JSON)
@Column(name = "service_ids", nullable = false, columnDefinition = "jsonb default '[]'::jsonb")
private List<String> serviceIds = List.of();
}
And I invoke it like this:
var matchedIds = indicatorRepository.findAllMatchingServiceId(List.of("a", "b"));
log.info("matchedIds --> {}", matchedIds);
But I get the following error upon starting the application:
Caused by: java.lang.IllegalArgumentException: Validation failed for query for method public abstract java.util.List com.example.repository.IndicatorRepository.findAllMatchingServiceId(java.util.List)
at org.springframework.data.jpa.repository.query.SimpleJpaQuery.validateQuery(SimpleJpaQuery.java:100) ~[spring-data-jpa-3.1.5.jar:3.1.5]
at org.springframework.data.jpa.repository.query.SimpleJpaQuery.<init>(SimpleJpaQuery.java:70) ~[spring-data-jpa-3.1.5.jar:3.1.5]
at org.springframework.data.jpa.repository.query.JpaQueryFactory.fromMethodWithQueryString(JpaQueryFactory.java:60) ~[spring-data-jpa-3.1.5.jar:3.1.5]
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$DeclaredQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:170) ~[spring-data-jpa-3.1.5.jar:3.1.5]
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:252) ~[spring-data-jpa-3.1.5.jar:3.1.5]
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:95) ~[spring-data-jpa-3.1.5.jar:3.1.5]
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.lookupQuery(QueryExecutorMethodInterceptor.java:111) ~[spring-data-commons-3.1.5.jar:3.1.5]
... 41 common frames omitted
Caused by: java.lang.IllegalArgumentException: org.hibernate.query.SemanticException: Cannot compare left expression of type 'java.lang.Object' with right expression of type 'java.lang.Boolean'
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:143) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:167) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:173) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:802) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:707) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:132) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:360) ~[spring-orm-6.0.11.jar:6.0.11]
at jdk.proxy2/jdk.proxy2.$Proxy90.createQuery(Unknown Source) ~[na:na]
at org.springframework.data.jpa.repository.query.SimpleJpaQuery.validateQuery(SimpleJpaQuery.java:94) ~[spring-data-jpa-3.1.5.jar:3.1.5]
... 47 common frames omitted
Caused by: org.hibernate.query.SemanticException: Cannot compare left expression of type 'java.lang.Object' with right expression of type 'java.lang.Boolean'
at org.hibernate.query.sqm.internal.TypecheckUtil.assertComparable(TypecheckUtil.java:338) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate.<init>(SqmComparisonPredicate.java:48) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate.<init>(SqmComparisonPredicate.java:34) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.query.hql.internal.SemanticQueryBuilder.createComparisonPredicate(SemanticQueryBuilder.java:2447) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitComparisonPredicate(SemanticQueryBuilder.java:2391) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitComparisonPredicate(SemanticQueryBuilder.java:268) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.grammars.hql.HqlParser$ComparisonPredicateContext.accept(HqlParser.java:6071) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitWhereClause(SemanticQueryBuilder.java:2243) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitWhereClause(SemanticQueryBuilder.java:268) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.grammars.hql.HqlParser$WhereClauseContext.accept(HqlParser.java:5822) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitQuery(SemanticQueryBuilder.java:1158) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitQuerySpecExpression(SemanticQueryBuilder.java:940) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitQuerySpecExpression(SemanticQueryBuilder.java:268) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.grammars.hql.HqlParser$QuerySpecExpressionContext.accept(HqlParser.java:1844) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitSimpleQueryGroup(SemanticQueryBuilder.java:925) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitSimpleQueryGroup(SemanticQueryBuilder.java:268) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.grammars.hql.HqlParser$SimpleQueryGroupContext.accept(HqlParser.java:1718) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitSelectStatement(SemanticQueryBuilder.java:442) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitStatement(SemanticQueryBuilder.java:401) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.query.hql.internal.SemanticQueryBuilder.buildSemanticModel(SemanticQueryBuilder.java:310) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.query.hql.internal.StandardHqlTranslator.translate(StandardHqlTranslator.java:71) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.query.internal.QueryInterpretationCacheStandardImpl.createHqlInterpretation(QueryInterpretationCacheStandardImpl.java:165) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.query.internal.QueryInterpretationCacheStandardImpl.resolveHqlInterpretation(QueryInterpretationCacheStandardImpl.java:147) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.internal.AbstractSharedSessionContract.interpretHql(AbstractSharedSessionContract.java:744) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:794) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
... 56 common frames omitted
I’m not confident if I defined it correctly, as a lot of solutions I found online are deprecated now.
Many thanks in advance.