Skip to content

Commit 6138c76

Browse files
committed
HHH-17693 Fix typecheck assertions for converted properties
Also introduce a custom `DurationJdbcType`, mainly for validation purposes.
1 parent 658e9bc commit 6138c76

File tree

10 files changed

+79
-26
lines changed

10 files changed

+79
-26
lines changed

hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ public void contributeType(CompositeUserType<?> type) {
686686
);
687687
}
688688
else {
689-
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INTERVAL_SECOND, SqlTypes.NUMERIC );
689+
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INTERVAL_SECOND, SqlTypes.DURATION );
690690
}
691691

692692
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INET, SqlTypes.VARBINARY );

hibernate-core/src/main/java/org/hibernate/dialect/AbstractPostgreSQLStructJdbcType.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,7 @@ private void serializeBasicTo(
776776
case SqlTypes.DOUBLE:
777777
case SqlTypes.DECIMAL:
778778
case SqlTypes.NUMERIC:
779+
case SqlTypes.DURATION:
779780
jdbcJavaType.appendEncodedString(
780781
appender,
781782
jdbcJavaType.unwrap(

hibernate-core/src/main/java/org/hibernate/dialect/JsonHelper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ else if ( mappedType instanceof BasicType<?> ) {
194194
break;
195195
case SqlTypes.DECIMAL:
196196
case SqlTypes.NUMERIC:
197+
case SqlTypes.DURATION:
197198
case SqlTypes.UUID:
198199
// These types need to be serialized as JSON string, but don't have a need for escaping
199200
appender.append( '"' );

hibernate-core/src/main/java/org/hibernate/dialect/XmlHelper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,7 @@ private static void serializeValueTo(XMLAppender appender, SelectableMapping sel
564564
case SqlTypes.DOUBLE:
565565
case SqlTypes.DECIMAL:
566566
case SqlTypes.NUMERIC:
567+
case SqlTypes.DURATION:
567568
jdbcJavaType.appendEncodedString(
568569
appender,
569570
jdbcJavaType.unwrap(

hibernate-core/src/main/java/org/hibernate/internal/util/config/ConfigurationHelper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ public static synchronized int getPreferredSqlTypeCodeForDuration(StandardServic
546546
return explicitSetting;
547547
}
548548

549-
return SqlTypes.NUMERIC;
549+
return SqlTypes.DURATION;
550550
}
551551

552552
@Incubating

hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -507,39 +507,24 @@ private static boolean isNumberArray(SqmExpressible<?> expressible) {
507507
public static void assertString(SqmExpression<?> expression) {
508508
final SqmExpressible<?> nodeType = expression.getNodeType();
509509
if ( nodeType != null ) {
510-
final Class<?> javaType = nodeType.getExpressibleJavaType().getJavaTypeClass();
511-
if ( javaType != String.class && javaType != char[].class ) {
510+
final DomainType<?> domainType = nodeType.getSqmType();
511+
if ( !( domainType instanceof JdbcMapping ) || !( (JdbcMapping) domainType ).getJdbcType().isStringLike() ) {
512512
throw new SemanticException(
513513
"Operand of 'like' is of type '" + nodeType.getTypeName() +
514-
"' which is not a string (it is not an instance of 'java.lang.String' or 'char[]')"
514+
"' which is not a string (its JDBC type code is not string-like)"
515515
);
516516
}
517517
}
518518
}
519519

520-
// public static void assertNumeric(SqmExpression<?> expression, BinaryArithmeticOperator op) {
521-
// final SqmExpressible<?> nodeType = expression.getNodeType();
522-
// if ( nodeType != null ) {
523-
// final Class<?> javaType = nodeType.getExpressibleJavaType().getJavaTypeClass();
524-
// if ( !Number.class.isAssignableFrom( javaType )
525-
// && !Temporal.class.isAssignableFrom( javaType )
526-
// && !TemporalAmount.class.isAssignableFrom( javaType )
527-
// && !java.util.Date.class.isAssignableFrom( javaType ) ) {
528-
// throw new SemanticException( "Operand of " + op.getOperatorSqlText()
529-
// + " is of type '" + nodeType.getTypeName() + "' which is not a numeric type"
530-
// + " (it is not an instance of 'java.lang.Number', 'java.time.Temporal', or 'java.time.TemporalAmount')" );
531-
// }
532-
// }
533-
// }
534-
535520
public static void assertDuration(SqmExpression<?> expression) {
536521
final SqmExpressible<?> nodeType = expression.getNodeType();
537522
if ( nodeType != null ) {
538-
final Class<?> javaType = nodeType.getExpressibleJavaType().getJavaTypeClass();
539-
if ( !TemporalAmount.class.isAssignableFrom( javaType ) ) {
523+
final DomainType<?> domainType = nodeType.getSqmType();
524+
if ( !( domainType instanceof JdbcMapping ) || !( (JdbcMapping) domainType ).getJdbcType().isDuration() ) {
540525
throw new SemanticException(
541526
"Operand of 'by' is of type '" + nodeType.getTypeName() +
542-
"' which is not a duration (it is not an instance of 'java.time.TemporalAmount')"
527+
"' which is not a duration (its JDBC type code is not duration-like)"
543528
);
544529
}
545530
}
@@ -548,11 +533,11 @@ public static void assertDuration(SqmExpression<?> expression) {
548533
public static void assertNumeric(SqmExpression<?> expression, UnaryArithmeticOperator op) {
549534
final SqmExpressible<?> nodeType = expression.getNodeType();
550535
if ( nodeType != null ) {
551-
final Class<?> javaType = nodeType.getExpressibleJavaType().getJavaTypeClass();
552-
if ( !Number.class.isAssignableFrom( javaType ) ) {
536+
final DomainType<?> domainType = nodeType.getSqmType();
537+
if ( !( domainType instanceof JdbcMapping ) || !( (JdbcMapping) domainType ).getJdbcType().isNumber() ) {
553538
throw new SemanticException(
554539
"Operand of " + op.getOperatorChar() + " is of type '" + nodeType.getTypeName() +
555-
"' which is not a numeric type (it is not an instance of 'java.lang.Number')"
540+
"' which is not a numeric type (its JDBC type code is not numeric)"
556541
);
557542
}
558543
}

hibernate-core/src/main/java/org/hibernate/type/SqlTypes.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,14 @@ public class SqlTypes {
498498
*/
499499
public static final int TIME_UTC = 3007;
500500

501+
/**
502+
* A type code representing a "virtual mapping" of {@linkplain java.time.Duration}.
503+
*
504+
* @see Types#NUMERIC
505+
* @see org.hibernate.type.descriptor.jdbc.DurationJdbcType
506+
*/
507+
public static final int DURATION = 3015;
508+
501509
// Interval types
502510

503511
/**
@@ -779,6 +787,13 @@ public static boolean isIntervalType(int typeCode) {
779787
return typeCode == INTERVAL_SECOND;
780788
}
781789

790+
/**
791+
* Does the given typecode represent a {@code duration} type?
792+
*/
793+
public static boolean isDurationType(int typeCode) {
794+
return typeCode == DURATION;
795+
}
796+
782797
/**
783798
* Does the given typecode represent a SQL date or timestamp type?
784799
* @param typeCode a JDBC type code from {@link Types}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
5+
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
6+
*/
7+
package org.hibernate.type.descriptor.jdbc;
8+
9+
import java.sql.Types;
10+
import java.time.Duration;
11+
12+
import org.hibernate.type.SqlTypes;
13+
import org.hibernate.type.descriptor.WrapperOptions;
14+
15+
/**
16+
* Descriptor for {@link java.time.Duration}.
17+
*
18+
* @author Marco Belladelli
19+
*/
20+
public class DurationJdbcType extends NumericJdbcType {
21+
public static final DurationJdbcType INSTANCE = new DurationJdbcType();
22+
23+
@Override
24+
public int getDdlTypeCode() {
25+
return Types.NUMERIC;
26+
}
27+
28+
@Override
29+
public int getDefaultSqlTypeCode() {
30+
return SqlTypes.DURATION;
31+
}
32+
33+
@Override
34+
public String getFriendlyName() {
35+
return "DURATION";
36+
}
37+
38+
@Override
39+
public String toString() {
40+
return "DurationJdbcType";
41+
}
42+
}

hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcType.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,12 @@ default boolean isInterval() {
287287
return isIntervalType( getDdlTypeCode() );
288288
}
289289

290+
default boolean isDuration() {
291+
final int ddlTypeCode = getDefaultSqlTypeCode();
292+
return isDurationType( ddlTypeCode )
293+
|| isIntervalType( ddlTypeCode );
294+
}
295+
290296
default CastType getCastType() {
291297
return getCastType( getDdlTypeCode() );
292298
}

hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcTypeBaseline.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.hibernate.type.descriptor.jdbc.DateJdbcType;
1919
import org.hibernate.type.descriptor.jdbc.DecimalJdbcType;
2020
import org.hibernate.type.descriptor.jdbc.DoubleJdbcType;
21+
import org.hibernate.type.descriptor.jdbc.DurationJdbcType;
2122
import org.hibernate.type.descriptor.jdbc.FloatJdbcType;
2223
import org.hibernate.type.descriptor.jdbc.IntegerJdbcType;
2324
import org.hibernate.type.descriptor.jdbc.JdbcType;
@@ -66,6 +67,7 @@ public static void prime(BaselineTarget target) {
6667
target.addDescriptor( TimestampWithTimeZoneJdbcType.INSTANCE );
6768
target.addDescriptor( TimeJdbcType.INSTANCE );
6869
target.addDescriptor( TimeWithTimeZoneJdbcType.INSTANCE );
70+
target.addDescriptor( DurationJdbcType.INSTANCE );
6971

7072
target.addDescriptor( BinaryJdbcType.INSTANCE );
7173
target.addDescriptor( VarbinaryJdbcType.INSTANCE );

0 commit comments

Comments
 (0)