From ed799d873ee3ba66fda2c08a6e50ab1773338ef3 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 25 Jun 2025 15:58:28 +0200 Subject: [PATCH] HHH-19574 fix metamodel population for nested entity classes --- .../metamodel/internal/InjectionHelper.java | 10 ++-- .../metamodel/internal/MetadataContext.java | 49 ++++++++++--------- .../inner/InnerEntityMetamodelTest.java | 48 ++++++++++++++++++ 3 files changed, 81 insertions(+), 26 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/inner/InnerEntityMetamodelTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/InjectionHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/InjectionHelper.java index 04dc1749920d..01dcfb91c5b0 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/InjectionHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/InjectionHelper.java @@ -9,12 +9,14 @@ import org.hibernate.boot.query.NamedQueryDefinition; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.internal.util.ReflectHelper; import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor; import java.lang.reflect.Field; import static java.lang.Character.charCount; import static java.lang.Character.isJavaIdentifierPart; +import static java.lang.reflect.Modifier.isPublic; public class InjectionHelper { private static final CoreMessageLogger log = CoreLogging.messageLogger( MetadataContext.class ); @@ -69,14 +71,16 @@ public static void injectField( ? metamodelClass.getField( name ) : metamodelClass.getDeclaredField( name ); try { - // should be public anyway, but to be sure... -// ReflectHelper.ensureAccessibility( field ); + if ( !isPublic( metamodelClass.getModifiers() ) ) { + ReflectHelper.ensureAccessibility( field ); + } field.set( null, model); } catch (IllegalAccessException e) { // todo : exception type? throw new AssertionFailure( - "Unable to inject static metamodel attribute : " + metamodelClass.getName() + '#' + name, + "Unable to inject attribute '" + name + + "' of static metamodel class '" + metamodelClass.getName() + "'", e ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataContext.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataContext.java index 1a46f17858aa..09cd1b77e153 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataContext.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataContext.java @@ -263,7 +263,6 @@ public Map> getIdentifiableTypesByName() { Property property, IdentifiableDomainType entityType, BiFunction, Property, PersistentAttribute> factoryFunction) { - final PersistentAttribute attribute; final Component component = property.getValue() instanceof Component comp ? comp : null; if ( component != null && component.isGeneric() ) { // This is an embeddable property that uses generics, we have to retrieve the generic @@ -273,22 +272,22 @@ public Map> getIdentifiableTypesByName() { final Property genericProperty = property.copy(); genericProperty.setValue( genericComponent ); genericProperty.setGeneric( true ); - attribute = factoryFunction.apply( entityType, genericProperty ); + final PersistentAttribute attribute = factoryFunction.apply( entityType, genericProperty ); if ( !property.isGeneric() ) { final PersistentAttribute concreteAttribute = factoryFunction.apply( entityType, property ); if ( concreteAttribute != null ) { - @SuppressWarnings("unchecked") final AttributeContainer attributeContainer = (AttributeContainer) entityType; + @SuppressWarnings("unchecked") + final AttributeContainer attributeContainer = (AttributeContainer) entityType; attributeContainer.getInFlightAccess().addConcreteGenericAttribute( concreteAttribute ); } } + return attribute; } else { - attribute = factoryFunction.apply( entityType, property ); + return factoryFunction.apply( entityType, property ); } - return attribute; } - @SuppressWarnings("unchecked") public void wrapUp() { if ( log.isTraceEnabled() ) { log.trace( "Wrapping up metadata context..." ); @@ -306,8 +305,7 @@ public void wrapUp() { log.trace( "Starting entity [" + safeMapping.getEntityName() + ']' ); } try { - final EntityDomainType jpaMapping = (EntityDomainType) - entityTypesByPersistentClass.get( safeMapping ); + final EntityDomainType jpaMapping = entityTypesByPersistentClass.get( safeMapping ); applyIdMetadata( safeMapping, jpaMapping ); applyVersionAttribute( safeMapping, jpaMapping ); @@ -345,8 +343,7 @@ else if ( MappedSuperclass.class.isAssignableFrom( mapping.getClass() ) ) { log.trace( "Starting mapped superclass [" + safeMapping.getMappedClass().getName() + ']' ); } try { - final MappedSuperclassDomainType jpaType = (MappedSuperclassDomainType) - mappedSuperclassByMappedSuperclassMapping.get( safeMapping ); + final var jpaType = mappedSuperclassByMappedSuperclassMapping.get( safeMapping ); applyIdMetadata( safeMapping, jpaType ); applyVersionAttribute( safeMapping, jpaType ); @@ -446,7 +443,7 @@ private void buildAttribute(Property property, IdentifiableDomainType jpa final PersistentAttribute attribute = buildAttribute( property, jpaType, attributeFactory::buildAttribute ); if ( attribute != null ) { - addAttribute(jpaType, attribute ); + addAttribute( jpaType, attribute ); if ( property.isNaturalIdentifier() ) { @SuppressWarnings("unchecked") final AttributeContainer attributeContainer = (AttributeContainer) jpaType; @@ -684,8 +681,9 @@ private MappedSuperclass getMappedSuperclass(PersistentClass persistentClass) { } private MappedSuperclass getMappedSuperclass(MappedSuperclass mappedSuperclass) { - return mappedSuperclass.getSuperMappedSuperclass() != null - ? mappedSuperclass.getSuperMappedSuperclass() + final MappedSuperclass superMappedSuperclass = mappedSuperclass.getSuperMappedSuperclass(); + return superMappedSuperclass != null + ? superMappedSuperclass : getMappedSuperclass( mappedSuperclass.getSuperPersistentClass() ); } @@ -700,19 +698,18 @@ private Property getMappedSuperclassProperty(String propertyName, MappedSupercla } } - final Property property = getMappedSuperclassProperty( - propertyName, - mappedSuperclass.getSuperMappedSuperclass() - ); + final Property property = + getMappedSuperclassProperty( propertyName, + mappedSuperclass.getSuperMappedSuperclass() ); if ( property != null ) { return property; } - - if ( mappedSuperclass.getSuperPersistentClass() != null ) { + else if ( mappedSuperclass.getSuperPersistentClass() != null ) { return mappedSuperclass.getSuperPersistentClass().getProperty( propertyName ); } - - return null; + else { + return null; + } } private Set> buildIdClassAttributes( @@ -721,7 +718,7 @@ private Property getMappedSuperclassProperty(String propertyName, MappedSupercla if ( log.isTraceEnabled() ) { log.trace( "Building old-school composite identifier [" + ownerType.getJavaType().getName() + ']' ); } - Set> attributes = new HashSet<>(); + final Set> attributes = new HashSet<>(); for ( Property property : properties ) { attributes.add( attributeFactory.buildIdAttribute( ownerType, property ) ); } @@ -761,7 +758,13 @@ private static void injectManagedType(ManagedDomainType managedType, Clas } private static String metamodelClassName(ManagedDomainType managedTypeClass) { - return managedTypeClass.getJavaType().getName() + '_'; + return metamodelClassName( managedTypeClass.getJavaType() ); + } + + private static String metamodelClassName(Class javaType) { + return javaType.isMemberClass() + ? metamodelClassName( javaType.getEnclosingClass() ) + "$" + javaType.getSimpleName() + "_" + : javaType.getName() + '_'; } public Class metamodelClass(ManagedDomainType managedDomainType) { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/inner/InnerEntityMetamodelTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/inner/InnerEntityMetamodelTest.java new file mode 100644 index 000000000000..2559669a2a19 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/inner/InnerEntityMetamodelTest.java @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.metamodel.inner; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Jpa(annotatedClasses = InnerEntityMetamodelTest.Inner.class) +class InnerEntityMetamodelTest { + @Test void test(EntityManagerFactoryScope scope) { + scope.getEntityManagerFactory(); + var innerName = InnerEntityMetamodelTest_.Inner_.name; + assertNotNull(innerName); + assertEquals("name", innerName.getName()); + assertEquals(String.class, innerName.getType().getJavaType()); + assertTrue(innerName.isOptional()); + assertFalse(innerName.isId()); + assertFalse(innerName.isVersion()); + assertFalse(innerName.isAssociation()); + assertFalse(innerName.isCollection()); + var innerId = InnerEntityMetamodelTest_.Inner_.id; + assertNotNull(innerId); + assertEquals("id", innerId.getName()); + assertEquals(long.class, innerId.getType().getJavaType()); + assertTrue(innerId.isId()); + assertFalse(innerId.isOptional()); + var metatype = InnerEntityMetamodelTest_.Inner_.class_; + assertNotNull(metatype); + assertEquals("InnerEntity", metatype.getName()); + assertEquals( 2, metatype.getAttributes().size() ); + assertEquals( Inner.class, metatype.getJavaType() ); + } + @Entity(name="InnerEntity") + static class Inner { + @Id long id; + String name; + } +}