diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java index d200fd5a45..c5dc04fa65 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java @@ -835,8 +835,9 @@ public class EmbeddableBinder { for ( int i = 0; i < classElements.size(); i++ ) { final PropertyData idClassPropertyData = classElements.get( i ); + final String propertyName = idClassPropertyData.getPropertyName(); final PropertyData entityPropertyData = - baseClassElementsByName.get( idClassPropertyData.getPropertyName() ); + baseClassElementsByName.get( propertyName ); if ( propertyHolder.isInIdClass() ) { if ( entityPropertyData == null ) { throw new AnnotationException( @@ -852,11 +853,38 @@ public class EmbeddableBinder { //the annotation overriding will be dealt with by a mechanism similar to @MapsId continue; } + if ( !hasCompatibleType( idClassPropertyData.getTypeName(), entityPropertyData.getTypeName() ) ) { + throw new AnnotationException( + "Property '" + propertyName + "' in @IdClass '" + idClassPropertyData.getDeclaringClass().getName() + + "' doesn't match type in entity class '" + baseInferredData.getPropertyType().getName() + + "' (expected '" + entityPropertyData.getTypeName() + "' but was '" + idClassPropertyData.getTypeName() + "')" + ); + } } classElements.set( i, entityPropertyData ); //this works since they are in the same order } } + private static boolean hasCompatibleType(String typeNameInIdClass, String typeNameInEntityClass) { + return typeNameInIdClass.equals( typeNameInEntityClass ) + || canonicalize( typeNameInIdClass ).equals( typeNameInEntityClass ) + || typeNameInIdClass.equals( canonicalize( typeNameInEntityClass ) ); + } + + private static String canonicalize(String typeName) { + return switch (typeName) { + case "boolean" -> Boolean.class.getName(); + case "char" -> Character.class.getName(); + case "int" -> Integer.class.getName(); + case "long" -> Long.class.getName(); + case "short" -> Short.class.getName(); + case "byte" -> Byte.class.getName(); + case "float" -> Float.class.getName(); + case "double" -> Double.class.getName(); + default -> typeName; + }; + } + static Component createEmbeddable( PropertyHolder propertyHolder, PropertyData inferredData, diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/cid/CompositeIdTypeMismatchTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/cid/CompositeIdTypeMismatchTest.java new file mode 100644 index 0000000000..b9718835f3 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/cid/CompositeIdTypeMismatchTest.java @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.annotations.cid; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import org.hibernate.AnnotationException; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.testing.util.ServiceRegistryUtil; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + + +/** + * @author Yanming Zhou + */ +public class CompositeIdTypeMismatchTest { + + @Test + public void test() { + try ( StandardServiceRegistry ssr = ServiceRegistryUtil.serviceRegistryBuilder().build() ) { + final MetadataSources metadataSources = new MetadataSources( ssr ); + metadataSources.addAnnotatedClass( TestEntity.class ); + assertThatExceptionOfType( AnnotationException.class).isThrownBy( metadataSources::buildMetadata ).withMessageContaining( "doesn't match type" ); + } + } + + @IdClass(TestEntity.ID.class) + @Entity + static class TestEntity { + + @Id + String code; + + + @Id + Integer type; + + record ID(String code, String type) { + + } + } +}