From ab9e67176177cdb457c4c06d0003d1d151dfe354 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Mon, 9 Sep 2024 11:01:14 +0200 Subject: [PATCH] HHH-18564 Handle attribute converters for query literals Allow both domain and relational forms when an attribute converter is present --- .../sqm/sql/BaseSqmToSqlAstConverter.java | 78 ++++++++++--------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index f310b8da85ae..15fe3b56cb79 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -5629,40 +5629,7 @@ else if ( inferableExpressible instanceof BasicValuedMapping ) { final BasicValuedMapping basicValuedMapping = (BasicValuedMapping) inferableExpressible; final BasicValueConverter valueConverter = basicValuedMapping.getJdbcMapping().getValueConverter(); if ( valueConverter != null ) { - final Object value = literal.getLiteralValue(); - final Object sqlLiteralValue; - // For converted query literals, we support both, the domain and relational java type - if ( value == null || valueConverter.getDomainJavaType().isInstance( value ) ) { - sqlLiteralValue = valueConverter.toRelationalValue( value ); - } - else if ( valueConverter.getRelationalJavaType().isInstance( value ) ) { - sqlLiteralValue = value; - } - else if ( Character.class.isAssignableFrom( valueConverter.getRelationalJavaType().getJavaTypeClass() ) - && value instanceof CharSequence && ( (CharSequence) value ).length() == 1 ) { - sqlLiteralValue = ( (CharSequence) value ).charAt( 0 ); - } - // In HQL, number literals might not match the relational java type exactly, - // so we allow coercion between the number types - else if ( Number.class.isAssignableFrom( valueConverter.getRelationalJavaType().getJavaTypeClass() ) - && value instanceof Number ) { - sqlLiteralValue = valueConverter.getRelationalJavaType().coerce( - value, - creationContext.getSessionFactory()::getTypeConfiguration - ); - } - else { - throw new SemanticException( - String.format( - Locale.ROOT, - "Literal type '%s' did not match domain type '%s' nor converted type '%s'", - value.getClass(), - valueConverter.getDomainJavaType().getJavaTypeClass().getName(), - valueConverter.getRelationalJavaType().getJavaTypeClass().getName() - ) - ); - } - return new QueryLiteral<>( sqlLiteralValue, basicValuedMapping ); + return new QueryLiteral<>( sqlLiteralValue( valueConverter, literal.getLiteralValue() ), basicValuedMapping ); } } @@ -5779,6 +5746,40 @@ else if ( expressible instanceof EntityValuedModelPart ) { } } + private Object sqlLiteralValue(BasicValueConverter valueConverter, D value) { + // For converted query literals, we support both, the domain and relational java type + if ( value == null || valueConverter.getDomainJavaType().isInstance( value ) ) { + return valueConverter.toRelationalValue( value ); + } + else if ( valueConverter.getRelationalJavaType().isInstance( value ) ) { + return value; + } + else if ( Character.class.isAssignableFrom( valueConverter.getRelationalJavaType().getJavaTypeClass() ) + && value instanceof CharSequence && ( (CharSequence) value ).length() == 1 ) { + return ( (CharSequence) value ).charAt( 0 ); + } + // In HQL, number literals might not match the relational java type exactly, + // so we allow coercion between the number types + else if ( Number.class.isAssignableFrom( valueConverter.getRelationalJavaType().getJavaTypeClass() ) + && value instanceof Number ) { + return valueConverter.getRelationalJavaType().coerce( + value, + creationContext.getSessionFactory()::getTypeConfiguration + ); + } + else { + throw new SemanticException( + String.format( + Locale.ROOT, + "Literal type '%s' did not match domain type '%s' nor converted type '%s'", + value.getClass(), + valueConverter.getDomainJavaType().getJavaTypeClass().getName(), + valueConverter.getRelationalJavaType().getJavaTypeClass().getName() + ) + ); + } + } + @Override public Expression visitHqlNumericLiteral(SqmHqlNumericLiteral numericLiteral) { final BasicValuedMapping inferredExpressible = (BasicValuedMapping) getInferredValueMapping(); @@ -7362,12 +7363,13 @@ private static > QueryLiteral queryLiteral( ); } + @SuppressWarnings({"unchecked", "rawtypes"}) @Override public Object visitFieldLiteral(SqmFieldLiteral sqmFieldLiteral) { - return new QueryLiteral<>( - sqmFieldLiteral.getValue(), - (BasicValuedMapping) determineValueMapping( sqmFieldLiteral ) - ); + final BasicValuedMapping valueMapping = (BasicValuedMapping) determineValueMapping( sqmFieldLiteral ); + final Object value = sqmFieldLiteral.getValue(); + final BasicValueConverter converter = valueMapping.getJdbcMapping().getValueConverter(); + return new QueryLiteral<>( converter != null ? sqlLiteralValue( converter, value ) : value, valueMapping ); } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~