diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractManagedType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractManagedType.java index 4a45210b738e..9d84851ea3d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractManagedType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractManagedType.java @@ -39,6 +39,7 @@ * Functionality common to all implementations of {@link ManagedType}. * * @author Steve Ebersole + * @author Yanming Zhou */ public abstract class AbstractManagedType extends AbstractDomainType @@ -152,7 +153,13 @@ public PersistentAttribute getAttribute(String name) { @Override public PersistentAttribute findAttribute(String name) { // first look at declared attributes - final PersistentAttribute attribute = findDeclaredAttribute( name ); + PersistentAttribute attribute = findDeclaredAttribute( name ); + if ( attribute != null ) { + return attribute; + } + + // second look at declared concrete generic attributes + attribute = findDeclaredConcreteGenericAttribute( name ); if ( attribute != null ) { return attribute; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/EmbeddedIdGenericsSuperclassTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/EmbeddedIdGenericsSuperclassTest.java index 77d29ac01919..22ac69224efb 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/EmbeddedIdGenericsSuperclassTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/EmbeddedIdGenericsSuperclassTest.java @@ -31,6 +31,7 @@ /** * @author Marco Belladelli + * @author Yanming Zhou */ @DomainModel(annotatedClasses = { EmbeddedIdGenericsSuperclassTest.Customer.class, @@ -81,7 +82,7 @@ public void testCustomerCriteria(SessionFactoryScope scope) { final CriteriaQuery query = cb.createQuery( Customer.class ); final Root root = query.from( Customer.class ); final Path id = root.get( "id" ); - assertThat( id.getJavaType() ).isEqualTo( DomainEntityId.class ); + assertThat( id.getJavaType() ).isEqualTo( CustomerId.class ); assertThat( id.getModel() ).isSameAs( root.getModel().getAttribute( "id" ) ); assertThat( ( (SqmPath) id ).getResolvedModel().getBindableJavaType() ).isEqualTo( CustomerId.class ); query.select( root ).where( cb.equal( id.get( "someDomainField" ), 1 ) ); @@ -111,7 +112,7 @@ public void testInvoiceCriteria(SessionFactoryScope scope) { final CriteriaQuery query = cb.createQuery( Invoice.class ); final Root root = query.from( Invoice.class ); final Path id = root.get( "id" ); - assertThat( id.getJavaType() ).isEqualTo( DomainEntityId.class ); + assertThat( id.getJavaType() ).isEqualTo( InvoiceId.class ); assertThat( id.getModel() ).isSameAs( root.getModel().getAttribute( "id" ) ); assertThat( ( (SqmPath) id ).getResolvedModel().getBindableJavaType() ).isEqualTo( InvoiceId.class ); query.select( root ).where( cb.equal( id.get( "someOtherDomainField" ), 1 ) ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericBasicValuedPathTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericBasicValuedPathTest.java index e7e8a5ed9320..a55509e393bd 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericBasicValuedPathTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericBasicValuedPathTest.java @@ -28,6 +28,7 @@ /** * @author Marco Belladelli + * @author Yanming Zhou */ @SessionFactory @DomainModel( annotatedClasses = { @@ -53,8 +54,7 @@ public void testId(SessionFactoryScope scope) { final CriteriaQuery query = cb.createQuery(); final Root root = query.from( MyEntity.class ); final Path idPath = root.get( "id" ); - // generic attributes are always reported as Object java type - assertThat( idPath.getJavaType() ).isEqualTo( Object.class ); + assertThat( idPath.getJavaType() ).isEqualTo( Integer.class ); assertThat( idPath.getModel() ).isSameAs( root.getModel().getAttribute( "id" ) ); assertThat( ( (SqmPath) idPath ).getResolvedModel().getBindableJavaType() ).isEqualTo( Integer.class ); final Object result = session.createQuery( query.select( idPath ) ).getSingleResult(); @@ -69,8 +69,7 @@ public void testProperty(SessionFactoryScope scope) { final CriteriaQuery query = cb.createQuery(); final Root root = query.from( MyEntity.class ); final Path dataPath = root.get( "data" ); - // generic attributes are always reported as Object java type - assertThat( dataPath.getJavaType() ).isEqualTo( Object.class ); + assertThat( dataPath.getJavaType() ).isEqualTo( String.class ); assertThat( dataPath.getModel() ).isSameAs( root.getModel().getAttribute( "data" ) ); assertThat( ( (SqmPath) dataPath ).getResolvedModel().getBindableJavaType() ).isEqualTo( String.class ); final Object result = session.createQuery( query.select( dataPath ) ).getSingleResult(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericMapAssociationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericMapAssociationTest.java index 01e807f67fdd..44a3f306c935 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericMapAssociationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericMapAssociationTest.java @@ -34,6 +34,7 @@ /** * @author Marco Belladelli + * @author Yanming Zhou */ @SessionFactory @DomainModel( annotatedClasses = { @@ -81,8 +82,7 @@ public void testChildCriteriaQuery(SessionFactoryScope scope) { final CriteriaQuery query = cb.createQuery( Long.class ); final Root root = query.from( MapContainerEntity.class ); final Join join = root.join( "map" ); - // generic attributes are always reported as Object java type - assertThat( join.getJavaType() ).isEqualTo( Object.class ); + assertThat( join.getJavaType() ).isEqualTo( MapValueEntity.class ); assertThat( join.getModel() ).isSameAs( root.getModel().getAttribute( "map" ) ); assertThat( ( (SqmPath) join ).getResolvedModel() .getBindableJavaType() ).isEqualTo( MapValueEntity.class ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericMappedSuperclassAssociationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericMappedSuperclassAssociationTest.java index 082ee7c9ecbb..b1c95501934a 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericMappedSuperclassAssociationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericMappedSuperclassAssociationTest.java @@ -34,6 +34,7 @@ /** * @author Marco Belladelli + * @author Yanming Zhou */ @DomainModel( annotatedClasses = { GenericMappedSuperclassAssociationTest.Parent.class, @@ -86,7 +87,7 @@ public void testCriteriaQuery(SessionFactoryScope scope) { final CriteriaQuery cq = cb.createQuery( ChildB.class ); final Root from = cq.from( ChildB.class ); final Path parent = from.get( "parent" ); - assertThat( parent.getModel().getBindableJavaType() ).isEqualTo( Parent.class ); + assertThat( parent.getModel().getBindableJavaType() ).isEqualTo( ParentB.class ); assertThat( ( (SqmPath) parent ).getResolvedModel().getBindableJavaType() ).isEqualTo( ParentB.class ); cq.select( from ).where( cb.equal( from.get( "parent" ).get( "id" ), 2L ) ); final ChildB result = session.createQuery( cq ).getSingleResult(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericToManyAssociationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericToManyAssociationTest.java index 23296f92bd67..7137a3bed622 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericToManyAssociationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericToManyAssociationTest.java @@ -34,6 +34,7 @@ /** * @author Yoann Rodière * @author Marco Belladelli + * @author Yanming Zhou */ @SessionFactory @DomainModel( annotatedClasses = { @@ -82,8 +83,7 @@ public void testParentCriteriaQuery(SessionFactoryScope scope) { final CriteriaQuery query = cb.createQuery( Long.class ); final Root root = query.from( Child.class ); final Path parent = root.get( "parent" ); - // generic attributes are always reported as Object java type - assertThat( parent.getJavaType() ).isEqualTo( Object.class ); + assertThat( parent.getJavaType() ).isEqualTo( Parent.class ); assertThat( parent.getModel() ).isSameAs( root.getModel().getAttribute( "parent" ) ); assertThat( ( (SqmPath) parent ).getResolvedModel().getBindableJavaType() ).isEqualTo( Parent.class ); final Long result = session.createQuery( query.select( parent.get( "id" ) ) ).getSingleResult(); @@ -106,8 +106,7 @@ public void testChildCriteriaQuery(SessionFactoryScope scope) { final CriteriaQuery query = cb.createQuery( Long.class ); final Root root = query.from( Parent.class ); final Join join = root.join( "children" ); - // generic attributes are always reported as Object java type - assertThat( join.getJavaType() ).isEqualTo( Object.class ); + assertThat( join.getJavaType() ).isEqualTo( Child.class ); assertThat( join.getModel() ).isSameAs( root.getModel().getAttribute( "children" ) ); assertThat( ( (SqmPath) join ).getResolvedModel().getBindableJavaType() ).isEqualTo( Child.class ); final Long result = session.createQuery( query.select( join.get( "id" ) ) ).getSingleResult(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericToOneAssociationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericToOneAssociationTest.java index 5fd25682f1a8..4b9338424922 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericToOneAssociationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/GenericToOneAssociationTest.java @@ -30,6 +30,7 @@ /** * @author Yoann Rodière * @author Marco Belladelli + * @author Yanming Zhou */ @SessionFactory @DomainModel( annotatedClasses = { @@ -78,8 +79,7 @@ public void testParentCriteriaQuery(SessionFactoryScope scope) { final CriteriaQuery query = cb.createQuery( Long.class ); final Root root = query.from( Child.class ); final Path parent = root.get( "parent" ); - // generic attributes are always reported as Object java type - assertThat( parent.getJavaType() ).isEqualTo( Object.class ); + assertThat( parent.getJavaType() ).isEqualTo( Parent.class ); assertThat( parent.getModel() ).isSameAs( root.getModel().getAttribute( "parent" ) ); assertThat( ( (SqmPath) parent ).getResolvedModel().getBindableJavaType() ).isEqualTo( Parent.class ); final Long result = session.createQuery( query.select( parent.get( "id" ) ) ).getSingleResult(); @@ -102,8 +102,7 @@ public void testChildCriteriaQuery(SessionFactoryScope scope) { final CriteriaQuery query = cb.createQuery( Long.class ); final Root root = query.from( Parent.class ); final Join join = root.join( "child" ); - // generic attributes are always reported as Object java type - assertThat( join.getJavaType() ).isEqualTo( Object.class ); + assertThat( join.getJavaType() ).isEqualTo( Child.class ); assertThat( join.getModel() ).isSameAs( root.getModel().getAttribute( "child" ) ); assertThat( ( (SqmPath) join ).getResolvedModel().getBindableJavaType() ).isEqualTo( Child.class ); final Long result = session.createQuery( query.select( join.get( "id" ) ) ).getSingleResult(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/MultipleEmbeddedGenericsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/MultipleEmbeddedGenericsTest.java index 6c0c958a2f51..78fcfac1e715 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/MultipleEmbeddedGenericsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/generics/MultipleEmbeddedGenericsTest.java @@ -30,6 +30,7 @@ /** * @author Marco Belladelli + * @author Yanming Zhou */ @DomainModel(annotatedClasses = { MultipleEmbeddedGenericsTest.Customer.class, @@ -74,12 +75,12 @@ public void testCustomerCriteria(SessionFactoryScope scope) { final CriteriaQuery query = cb.createQuery( Customer.class ); final Root root = query.from( Customer.class ); final Path firstEmbedded = root.get( "firstEmbedded" ); - assertThat( firstEmbedded.getJavaType() ).isEqualTo( GenericEmbeddableOne.class ); + assertThat( firstEmbedded.getJavaType() ).isEqualTo( CustomerEmbeddableOne.class ); assertThat( firstEmbedded.getModel() ).isSameAs( root.getModel().getAttribute( "firstEmbedded" ) ); assertThat( ( (SqmPath) firstEmbedded ).getResolvedModel().getBindableJavaType() ) .isEqualTo( CustomerEmbeddableOne.class ); final Path secondEmbedded = root.get( "secondEmbedded" ); - assertThat( secondEmbedded.getJavaType() ).isEqualTo( GenericEmbeddableTwo.class ); + assertThat( secondEmbedded.getJavaType() ).isEqualTo( CustomerEmbeddableTwo.class ); assertThat( secondEmbedded.getModel() ).isSameAs( root.getModel().getAttribute( "secondEmbedded" ) ); assertThat( ( (SqmPath) secondEmbedded ).getResolvedModel().getBindableJavaType() ) .isEqualTo( CustomerEmbeddableTwo.class ); @@ -110,12 +111,12 @@ public void testInvoiceCriteria(SessionFactoryScope scope) { final CriteriaQuery query = cb.createQuery( Invoice.class ); final Root root = query.from( Invoice.class ); final Path firstEmbedded = root.get( "firstEmbedded" ); - assertThat( firstEmbedded.getJavaType() ).isEqualTo( GenericEmbeddableOne.class ); + assertThat( firstEmbedded.getJavaType() ).isEqualTo( InvoiceEmbeddableOne.class ); assertThat( firstEmbedded.getModel() ).isSameAs( root.getModel().getAttribute( "firstEmbedded" ) ); assertThat( ( (SqmPath) firstEmbedded ).getResolvedModel().getBindableJavaType() ) .isEqualTo( InvoiceEmbeddableOne.class ); final Path secondEmbedded = root.get( "secondEmbedded" ); - assertThat( secondEmbedded.getJavaType() ).isEqualTo( GenericEmbeddableTwo.class ); + assertThat( secondEmbedded.getJavaType() ).isEqualTo( InvoiceEmbeddableTwo.class ); assertThat( secondEmbedded.getModel() ).isSameAs( root.getModel().getAttribute( "secondEmbedded" ) ); assertThat( ( (SqmPath) secondEmbedded ).getResolvedModel().getBindableJavaType() ) .isEqualTo( InvoiceEmbeddableTwo.class ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/genericmodel/GenericMappedSuperclassMetamodelTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/genericmodel/GenericMappedSuperclassMetamodelTest.java new file mode 100644 index 000000000000..297e3156f0bd --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/genericmodel/GenericMappedSuperclassMetamodelTest.java @@ -0,0 +1,59 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.orm.test.metamodel.genericmodel; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.metamodel.EntityType; +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author Yanming Zhou + */ +@Jpa( + annotatedClasses = { + GenericMappedSuperclassMetamodelTest.Book.class, + GenericMappedSuperclassMetamodelTest.Owner.class, + GenericMappedSuperclassMetamodelTest.OwnerContainer.class + } +) +public class GenericMappedSuperclassMetamodelTest { + + @Test + public void testGenericAttributeFromMappedSuperclassIsNotTypeErased(EntityManagerFactoryScope scope) { + scope.inTransaction( entityManager -> { + EntityType bookType = entityManager.getMetamodel().entity( Book.class ); + assertEquals( Owner.class, bookType.getAttribute("owner").getJavaType() ); + }); + } + + @Entity + public static class Book extends OwnerContainer { + + @Id + private Long id; + } + + @Entity + public static class Owner { + @Id + private Long id; + } + + @MappedSuperclass + public static class OwnerContainer { + + @ManyToOne + T owner; + } +}