Skip to content

HHH-19574 fix metamodel population for nested entity classes #10410

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down Expand Up @@ -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
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,6 @@ public Map<String, IdentifiableDomainType<?>> getIdentifiableTypesByName() {
Property property,
IdentifiableDomainType<X> entityType,
BiFunction<IdentifiableDomainType<X>, Property, PersistentAttribute<X, ?>> factoryFunction) {
final PersistentAttribute<X, ?> 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
Expand All @@ -273,22 +272,22 @@ public Map<String, IdentifiableDomainType<?>> getIdentifiableTypesByName() {
final Property genericProperty = property.copy();
genericProperty.setValue( genericComponent );
genericProperty.setGeneric( true );
attribute = factoryFunction.apply( entityType, genericProperty );
final PersistentAttribute<X, ?> attribute = factoryFunction.apply( entityType, genericProperty );
if ( !property.isGeneric() ) {
final PersistentAttribute<X, ?> concreteAttribute = factoryFunction.apply( entityType, property );
if ( concreteAttribute != null ) {
@SuppressWarnings("unchecked") final AttributeContainer<X> attributeContainer = (AttributeContainer<X>) entityType;
@SuppressWarnings("unchecked")
final AttributeContainer<X> attributeContainer = (AttributeContainer<X>) 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..." );
Expand All @@ -306,8 +305,7 @@ public void wrapUp() {
log.trace( "Starting entity [" + safeMapping.getEntityName() + ']' );
}
try {
final EntityDomainType<Object> jpaMapping = (EntityDomainType<Object>)
entityTypesByPersistentClass.get( safeMapping );
final EntityDomainType<?> jpaMapping = entityTypesByPersistentClass.get( safeMapping );

applyIdMetadata( safeMapping, jpaMapping );
applyVersionAttribute( safeMapping, jpaMapping );
Expand Down Expand Up @@ -345,8 +343,7 @@ else if ( MappedSuperclass.class.isAssignableFrom( mapping.getClass() ) ) {
log.trace( "Starting mapped superclass [" + safeMapping.getMappedClass().getName() + ']' );
}
try {
final MappedSuperclassDomainType<Object> jpaType = (MappedSuperclassDomainType<Object>)
mappedSuperclassByMappedSuperclassMapping.get( safeMapping );
final var jpaType = mappedSuperclassByMappedSuperclassMapping.get( safeMapping );

applyIdMetadata( safeMapping, jpaType );
applyVersionAttribute( safeMapping, jpaType );
Expand Down Expand Up @@ -446,7 +443,7 @@ private <T> void buildAttribute(Property property, IdentifiableDomainType<T> jpa
final PersistentAttribute<T, ?> attribute =
buildAttribute( property, jpaType, attributeFactory::buildAttribute );
if ( attribute != null ) {
addAttribute(jpaType, attribute );
addAttribute( jpaType, attribute );
if ( property.isNaturalIdentifier() ) {
@SuppressWarnings("unchecked")
final AttributeContainer<T> attributeContainer = (AttributeContainer<T>) jpaType;
Expand Down Expand Up @@ -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() );
}

Expand All @@ -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 <X> Set<SingularPersistentAttribute<? super X, ?>> buildIdClassAttributes(
Expand All @@ -721,7 +718,7 @@ private Property getMappedSuperclassProperty(String propertyName, MappedSupercla
if ( log.isTraceEnabled() ) {
log.trace( "Building old-school composite identifier [" + ownerType.getJavaType().getName() + ']' );
}
Set<SingularPersistentAttribute<? super X, ?>> attributes = new HashSet<>();
final Set<SingularPersistentAttribute<? super X, ?>> attributes = new HashSet<>();
for ( Property property : properties ) {
attributes.add( attributeFactory.buildIdAttribute( ownerType, property ) );
}
Expand Down Expand Up @@ -761,7 +758,13 @@ private static <X> void injectManagedType(ManagedDomainType<X> 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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}