Skip to content

Commit ff99ddb

Browse files
quaffgavinking
authored andcommitted
HHH-18839 Validate property type of @IdClass at bind time
1 parent 82060f2 commit ff99ddb

File tree

2 files changed

+77
-1
lines changed

2 files changed

+77
-1
lines changed

hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -835,8 +835,9 @@ private static void processIdClassElements(
835835

836836
for ( int i = 0; i < classElements.size(); i++ ) {
837837
final PropertyData idClassPropertyData = classElements.get( i );
838+
final String propertyName = idClassPropertyData.getPropertyName();
838839
final PropertyData entityPropertyData =
839-
baseClassElementsByName.get( idClassPropertyData.getPropertyName() );
840+
baseClassElementsByName.get( propertyName );
840841
if ( propertyHolder.isInIdClass() ) {
841842
if ( entityPropertyData == null ) {
842843
throw new AnnotationException(
@@ -852,11 +853,38 @@ private static void processIdClassElements(
852853
//the annotation overriding will be dealt with by a mechanism similar to @MapsId
853854
continue;
854855
}
856+
if ( !hasCompatibleType( idClassPropertyData.getTypeName(), entityPropertyData.getTypeName() ) ) {
857+
throw new AnnotationException(
858+
"Property '" + propertyName + "' in @IdClass '" + idClassPropertyData.getDeclaringClass().getName()
859+
+ "' doesn't match type in entity class '" + baseInferredData.getPropertyType().getName()
860+
+ "' (expected '" + entityPropertyData.getTypeName() + "' but was '" + idClassPropertyData.getTypeName() + "')"
861+
);
862+
}
855863
}
856864
classElements.set( i, entityPropertyData ); //this works since they are in the same order
857865
}
858866
}
859867

868+
private static boolean hasCompatibleType(String typeNameInIdClass, String typeNameInEntityClass) {
869+
return typeNameInIdClass.equals( typeNameInEntityClass )
870+
|| canonicalize( typeNameInIdClass ).equals( typeNameInEntityClass )
871+
|| typeNameInIdClass.equals( canonicalize( typeNameInEntityClass ) );
872+
}
873+
874+
private static String canonicalize(String typeName) {
875+
return switch (typeName) {
876+
case "boolean" -> Boolean.class.getName();
877+
case "char" -> Character.class.getName();
878+
case "int" -> Integer.class.getName();
879+
case "long" -> Long.class.getName();
880+
case "short" -> Short.class.getName();
881+
case "byte" -> Byte.class.getName();
882+
case "float" -> Float.class.getName();
883+
case "double" -> Double.class.getName();
884+
default -> typeName;
885+
};
886+
}
887+
860888
static Component createEmbeddable(
861889
PropertyHolder propertyHolder,
862890
PropertyData inferredData,
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.annotations.cid;
6+
7+
import jakarta.persistence.Entity;
8+
import jakarta.persistence.Id;
9+
import jakarta.persistence.IdClass;
10+
import org.hibernate.AnnotationException;
11+
import org.hibernate.boot.MetadataSources;
12+
import org.hibernate.boot.registry.StandardServiceRegistry;
13+
import org.hibernate.testing.util.ServiceRegistryUtil;
14+
import org.junit.jupiter.api.Test;
15+
16+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
17+
18+
19+
/**
20+
* @author Yanming Zhou
21+
*/
22+
public class CompositeIdTypeMismatchTest {
23+
24+
@Test
25+
public void test() {
26+
try ( StandardServiceRegistry ssr = ServiceRegistryUtil.serviceRegistryBuilder().build() ) {
27+
final MetadataSources metadataSources = new MetadataSources( ssr );
28+
metadataSources.addAnnotatedClass( TestEntity.class );
29+
assertThatExceptionOfType( AnnotationException.class).isThrownBy( metadataSources::buildMetadata ).withMessageContaining( "doesn't match type" );
30+
}
31+
}
32+
33+
@IdClass(TestEntity.ID.class)
34+
@Entity
35+
static class TestEntity {
36+
37+
@Id
38+
String code;
39+
40+
41+
@Id
42+
Integer type;
43+
44+
record ID(String code, String type) {
45+
46+
}
47+
}
48+
}

0 commit comments

Comments
 (0)