Skip to content

Fix HHH-19076 #10281

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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,6 +9,7 @@
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.Set;

import org.hibernate.AssertionFailure;
import org.hibernate.PropertyNotFoundException;
Expand Down Expand Up @@ -395,20 +396,29 @@ private static JavaType<?> determineRelationalJavaType(
private static EntityPersister getDeclaringEntity(
AbstractIdentifiableType<?> ownerType,
MetadataContext metadataContext) {
final java.util.List<EntityPersister> resultList = getDeclarerEntityPersister( ownerType, metadataContext );
return resultList.isEmpty() ? null : resultList.getFirst();
}

private static java.util.List<EntityPersister> getAllDeclaringEntities(AbstractIdentifiableType<?> ownerType,
MetadataContext metadataContext) {
return getDeclarerEntityPersister( ownerType, metadataContext );
}

private static EntityPersister getDeclarerEntityPersister(
private static java.util.List<EntityPersister> getDeclarerEntityPersister(
AbstractIdentifiableType<?> ownerType,
MetadataContext metadataContext) {
final Type.PersistenceType persistenceType = ownerType.getPersistenceType();
if ( persistenceType == Type.PersistenceType.ENTITY ) {
return metadataContext.getMetamodel().getEntityDescriptor( ownerType.getTypeName() );
return java.util.List.of(metadataContext.getMetamodel().getEntityDescriptor( ownerType.getTypeName() ));
}
else if ( persistenceType == Type.PersistenceType.MAPPED_SUPERCLASS ) {
final PersistentClass persistentClass =
final Set<PersistentClass> persistentClassSet =
metadataContext.getPersistentClassHostingProperties( (MappedSuperclassTypeImpl<?>) ownerType );
return persistentClass != null ? metadataContext.getMetamodel().findEntityDescriptor( persistentClass.getClassName() ) : null;
if (persistentClassSet == null) {
return java.util.List.of();
}
return persistentClassSet.stream().map( persistentClass -> metadataContext.getMetamodel().findEntityDescriptor( persistentClass.getClassName() ) ).toList();
}
else {
throw new AssertionFailure( "Cannot get the metamodel for PersistenceType: " + persistenceType );
Expand Down Expand Up @@ -680,18 +690,27 @@ private static EmbeddableRepresentationStrategy ownerRepresentationStrategy(

private static final MemberResolver virtualIdentifierMemberResolver = (attributeContext, metadataContext) -> {
final AbstractIdentifiableType<?> identifiableType = (AbstractIdentifiableType<?>) attributeContext.getOwnerType();
final EntityPersister declaringEntity = getDeclaringEntity( identifiableType, metadataContext );
return resolveVirtualIdentifierMember( attributeContext.getPropertyMapping(), declaringEntity );
final java.util.List<EntityPersister> declaringEntities = getAllDeclaringEntities( identifiableType, metadataContext );
return resolveVirtualIdentifierMember( attributeContext.getPropertyMapping(), declaringEntities );
};

private static Member resolveVirtualIdentifierMember( Property property, EntityPersister entityPersister) {
final EntityIdentifierMapping identifierMapping = entityPersister.getIdentifierMapping();
private static Member resolveVirtualIdentifierMember( Property property, java.util.List<EntityPersister> entityPersisters) {
CompositeIdentifierMapping cid = null;

// HHH-19076: Find the first EntityPersister for the property with a VIRTUAL identifierMapping
for (EntityPersister entityPersister : entityPersisters) {
EntityIdentifierMapping identifierMapping = entityPersister.getIdentifierMapping();

if ( identifierMapping.getNature() == EntityIdentifierMapping.Nature.VIRTUAL ) {
cid = (CompositeIdentifierMapping) identifierMapping;
break;
}
}

if ( identifierMapping.getNature() != EntityIdentifierMapping.Nature.VIRTUAL ) {
if ( cid == null ) {
throw new IllegalArgumentException( "expecting IdClass mapping" );
}

final CompositeIdentifierMapping cid = (CompositeIdentifierMapping) identifierMapping;
final EmbeddableMappingType embeddable = cid.getPartMappingType();
final String attributeName = property.getName();
final AttributeMapping attributeMapping = embeddable.findAttributeMapping( attributeName );
Expand All @@ -718,7 +737,7 @@ private static Member resolveVirtualIdentifierMember( Property property, EntityP
return switch ( persistenceType ) {
case ENTITY ->
resolveEntityMember( property,
getDeclaringEntity( (AbstractIdentifiableType<?>) ownerType, metadataContext ) );
getAllDeclaringEntities( (AbstractIdentifiableType<?>) ownerType, metadataContext ) );
case MAPPED_SUPERCLASS ->
resolveMappedSuperclassMember( property, (MappedSuperclassDomainType<?>) ownerType, metadataContext );
case EMBEDDABLE ->
Expand All @@ -727,31 +746,32 @@ private static Member resolveVirtualIdentifierMember( Property property, EntityP
};
};

private static Member resolveEntityMember(Property property, EntityPersister declaringEntity) {
private static Member resolveEntityMember(Property property, java.util.List<EntityPersister> declaringEntities) {
final String propertyName = property.getName();
final AttributeMapping attributeMapping = declaringEntity.findAttributeMapping( propertyName );
final EntityPersister firstDeclaringEntity = declaringEntities.getFirst();
final AttributeMapping attributeMapping = firstDeclaringEntity.findAttributeMapping( propertyName );
return attributeMapping == null
// just like in #determineIdentifierJavaMember , this *should* indicate we have an IdClass mapping
? resolveVirtualIdentifierMember( property, declaringEntity )
: getter( declaringEntity, property, propertyName, property.getType().getReturnedClass() );
? resolveVirtualIdentifierMember( property, declaringEntities )
: getter( firstDeclaringEntity, property, propertyName, property.getType().getReturnedClass() );
}

private static Member resolveMappedSuperclassMember(
Property property,
MappedSuperclassDomainType<?> ownerType,
MetadataContext context) {
final EntityPersister declaringEntity =
getDeclaringEntity( (AbstractIdentifiableType<?>) ownerType, context );
if ( declaringEntity != null ) {
return resolveEntityMember( property, declaringEntity );
final java.util.List<EntityPersister> declaringEntities =
getAllDeclaringEntities( (AbstractIdentifiableType<?>) ownerType, context );
if ( !declaringEntities.isEmpty() ) {
return resolveEntityMember( property, declaringEntities );
}
else {
final ManagedDomainType<?> subType = ownerType.getSubTypes().iterator().next();
final Type.PersistenceType persistenceType = subType.getPersistenceType();
return switch ( persistenceType ) {
case ENTITY ->
resolveEntityMember( property,
getDeclaringEntity( (AbstractIdentifiableType<?>) subType, context ) );
getAllDeclaringEntities( (AbstractIdentifiableType<?>) subType, context ) );
case MAPPED_SUPERCLASS ->
resolveMappedSuperclassMember( property, (MappedSuperclassDomainType<?>) subType, context );
case EMBEDDABLE ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public class MetadataContext {
private final Map<EmbeddableDomainType<?>, Component> componentByEmbeddable = new HashMap<>();

private final Map<MappedSuperclass, MappedSuperclassDomainType<?>> mappedSuperclassByMappedSuperclassMapping = new HashMap<>();
private final Map<MappedSuperclassDomainType<?>, PersistentClass> mappedSuperClassTypeToPersistentClass = new HashMap<>();
private final Map<MappedSuperclassDomainType<?>, Set<PersistentClass>> mappedSuperClassTypeToPersistentClass = new HashMap<>();

//this list contains MappedSuperclass and EntityTypes ordered by superclass first
private final List<Object> orderedMappings = new ArrayList<>();
Expand Down Expand Up @@ -211,13 +211,19 @@ public void registerMappedSuperclassType(
identifiableTypesByName.put( mappedSuperclassType.getTypeName(), mappedSuperclassType );
mappedSuperclassByMappedSuperclassMapping.put( mappedSuperclass, mappedSuperclassType );
orderedMappings.add( mappedSuperclass );
if ( !stackOfPersistentClassesBeingProcessed.isEmpty() ) {
mappedSuperClassTypeToPersistentClass.put( mappedSuperclassType, getEntityWorkedOn() );
}

knownMappedSuperclasses.remove( mappedSuperclass );
}

public void registerMappedSuperclassForPersistenceClass(MappedSuperclassDomainType<?> mappedSuperclassType) {
if ( stackOfPersistentClassesBeingProcessed.isEmpty() ) {
return;
}

final Set<PersistentClass> persistentClassSet = mappedSuperClassTypeToPersistentClass.computeIfAbsent( mappedSuperclassType, x -> new HashSet<>() );
persistentClassSet.add( getEntityWorkedOn() );
}

/**
* Given a Hibernate {@link PersistentClass}, locate the corresponding JPA {@link org.hibernate.type.EntityType}
* implementation. May return null if the given {@link PersistentClass} has not yet been processed.
Expand Down Expand Up @@ -855,7 +861,7 @@ private PersistentClass getEntityWorkedOn() {
);
}

public PersistentClass getPersistentClassHostingProperties(MappedSuperclassTypeImpl<?> mappedSuperclassType) {
public Set<PersistentClass> getPersistentClassHostingProperties(MappedSuperclassTypeImpl<?> mappedSuperclassType) {
return mappedSuperClassTypeToPersistentClass.get( mappedSuperclassType );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -780,11 +780,14 @@ private <T> MappedSuperclassDomainType<T> locateOrBuildMappedSuperclassType(
MetadataContext context,
TypeConfiguration typeConfiguration) {
@SuppressWarnings("unchecked")
final MappedSuperclassDomainType<T> mappedSuperclassType =
MappedSuperclassDomainType<T> mappedSuperclassType =
(MappedSuperclassDomainType<T>) context.locateMappedSuperclassType( mappedSuperclass );
return mappedSuperclassType == null
? buildMappedSuperclassType( mappedSuperclass, context, typeConfiguration )
: mappedSuperclassType;
if (mappedSuperclassType == null) {
mappedSuperclassType = buildMappedSuperclassType( mappedSuperclass, context, typeConfiguration );
}
// HHH-19076: Ensure that each mapped superclass knows ALL its implementations
context.registerMappedSuperclassForPersistenceClass( mappedSuperclassType );
return mappedSuperclassType;
}

private <T> MappedSuperclassTypeImpl<T> buildMappedSuperclassType(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.mapping.identifier.composite;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.IdClass;
import jakarta.persistence.MappedSuperclass;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.Jira;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertNotNull;

/**
* This test Fails
*/
@DomainModel(
annotatedClasses = {
CompositeInheritanceFailTest.TupAbstractEntity.class,
CompositeInheritanceFailTest.DummyEntity.class,
CompositeInheritanceFailTest.TestEntity.class, // Here the class is called TestEntity
}
)
@ServiceRegistry(
settings = {
// For your own convenience to see generated queries:
@Setting(name = AvailableSettings.SHOW_SQL, value = "true"),
@Setting(name = AvailableSettings.FORMAT_SQL, value = "true"),
// @Setting( name = AvailableSettings.GENERATE_STATISTICS, value = "true" ),
}
)
@SessionFactory
@Jira("HHH-19076")
public class CompositeInheritanceFailTest {

@Test
void hhh19076FailingTest(SessionFactoryScope scope) {
scope.inTransaction( em -> {
TestEntity e1 = new TestEntity("foo", "bar");
em.persist(e1);

CompositeIdClass key = e1.getCompositeId();
TestEntity e2 = em.find(TestEntity.class, key);
assertNotNull(e2);
} );
}

@MappedSuperclass
public static abstract class TupAbstractEntity {
@Id
private String oid = null;


@SuppressWarnings("this-escape")
protected TupAbstractEntity() {
}

protected TupAbstractEntity(String oid) {
this.oid = oid;
}

public String getOid() {
return oid;
}

}

@Entity
public static class DummyEntity extends TupAbstractEntity {
}

@Entity
@IdClass(CompositeIdClass.class)
public static class TestEntity extends TupAbstractEntity {

@Id
private String myId;

protected TestEntity() {
// for JPA
}

public TestEntity(String oid, String myId) {
super(oid);
this.myId = myId;
}

public String myId() {
return myId;
}

public CompositeIdClass getCompositeId() {
return new CompositeIdClass(getOid(), myId);
}

}

public static class CompositeIdClass {

private String oid;
private String myId;

public CompositeIdClass(String oid, String myId) {
this.oid = oid;
this.myId = myId;
}

public CompositeIdClass() {
}

public String oid() {
return oid;
}

public String myId() {
return myId;
}

}

}
Loading