diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/JpaMetamodel.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/JpaMetamodel.java index c4f526a29a5c..b26dad35594d 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/JpaMetamodel.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/JpaMetamodel.java @@ -78,16 +78,40 @@ public interface JpaMetamodel extends Metamodel { EntityDomainType resolveHqlEntityReference(String entityName); /** - * Same as {@link #managedType} except {@code null} is returned rather + * Same as {@link #managedType(Class)} except {@code null} is returned rather * than throwing an exception */ - ManagedDomainType findManagedType(Class cls); + @Nullable ManagedDomainType findManagedType(Class cls); /** - * Same as {@link #entity} except {@code null} is returned rather + * Same as {@link #entity(Class)} except {@code null} is returned rather * than throwing an exception */ - EntityDomainType findEntityType(Class cls); + @Nullable EntityDomainType findEntityType(Class cls); + + /** + * Same as {@link #embeddable(Class)} except {@code null} is returned rather + * than throwing an exception + */ + @Nullable EmbeddableDomainType findEmbeddableType(Class cls); + + /** + * Same as {@link #managedType(String)} except {@code null} is returned rather + * than throwing an exception + */ + @Nullable ManagedDomainType findManagedType(@Nullable String typeName); + + /** + * Same as {@link #entity(String)} except {@code null} is returned rather + * than throwing an exception + */ + @Nullable EntityDomainType findEntityType(@Nullable String entityName); + + /** + * Same as {@link #embeddable(String)} except {@code null} is returned rather + * than throwing an exception + */ + @Nullable EmbeddableDomainType findEmbeddableType(@Nullable String embeddableName); String qualifyImportableName(String queryName); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java index 289c11a3c920..12601134a37c 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java @@ -134,33 +134,61 @@ public JpaCompliance getJpaCompliance() { } @Override - public ManagedDomainType managedType(String typeName) { + public @Nullable ManagedDomainType findManagedType(@Nullable String typeName) { //noinspection unchecked return typeName == null ? null : (ManagedDomainType) managedTypeByName.get( typeName ); } @Override - public EntityDomainType entity(String entityName) { + public ManagedDomainType managedType(String typeName) { + final ManagedDomainType managedType = findManagedType( typeName ); + if ( managedType == null ) { + throw new IllegalArgumentException("Not a managed type: " + typeName); + } + return managedType; + } + + @Override + @Nullable public EntityDomainType findEntityType(@Nullable String entityName) { if ( entityName == null ) { return null; } final ManagedDomainType managedType = managedTypeByName.get( entityName ); - if ( !( managedType instanceof EntityDomainType ) ) { + if ( !( managedType instanceof EntityDomainType entityDomainType ) ) { return null; } - return (EntityDomainType) managedType; + return entityDomainType; } @Override - public EmbeddableDomainType embeddable(String embeddableName) { + public EntityDomainType entity(String entityName) { + final EntityDomainType entityType = findEntityType( entityName ); + if ( entityType == null ) { + // per JPA + throw new IllegalArgumentException("Not an entity: " + entityName); + } + return entityType; + } + + @Override + @Nullable public EmbeddableDomainType findEmbeddableType(@Nullable String embeddableName) { if ( embeddableName == null ) { return null; } final ManagedDomainType managedType = managedTypeByName.get( embeddableName ); - if ( !( managedType instanceof EmbeddableDomainType ) ) { + if ( !( managedType instanceof EmbeddableDomainType embeddableDomainType) ) { return null; } - return (EmbeddableDomainType) managedType; + return embeddableDomainType; + } + + @Override + public EmbeddableDomainType embeddable(String embeddableName) { + final EmbeddableDomainType embeddableType = findEmbeddableType( embeddableName ); + if ( embeddableType == null ) { + throw new IllegalArgumentException("Not an embeddable: " + embeddableName); + } + return embeddableType; } @Override @@ -172,7 +200,7 @@ public EntityDomainType getHqlEntityReference(String entityName) { entityName = importInfo.importedName; } - final EntityDomainType entityDescriptor = entity( entityName ); + final EntityDomainType entityDescriptor = findEntityType( entityName ); if ( entityDescriptor != null ) { //noinspection unchecked return (EntityDomainType) entityDescriptor; @@ -201,13 +229,23 @@ public EntityDomainType resolveHqlEntityReference(String entityName) { } @Override - public ManagedDomainType findManagedType(Class cls) { + @Nullable public ManagedDomainType findManagedType(Class cls) { //noinspection unchecked return (ManagedDomainType) managedTypeByClass.get( cls ); } @Override - public EntityDomainType findEntityType(Class cls) { + public ManagedDomainType managedType(Class cls) { + final ManagedDomainType type = findManagedType( cls ); + if ( type == null ) { + // per JPA + throw new IllegalArgumentException( "Not a managed type: " + cls ); + } + return type; + } + + @Override + @Nullable public EntityDomainType findEntityType(Class cls) { final ManagedType type = managedTypeByClass.get( cls ); if ( !( type instanceof EntityDomainType ) ) { return null; @@ -217,35 +255,31 @@ public EntityDomainType findEntityType(Class cls) { } @Override - public ManagedDomainType managedType(Class cls) { - final ManagedType type = managedTypeByClass.get( cls ); - if ( type == null ) { - // per JPA - throw new IllegalArgumentException( "Not a managed type: " + cls ); + public EntityDomainType entity(Class cls) { + final EntityDomainType entityType = findEntityType( cls ); + if ( entityType == null ) { + throw new IllegalArgumentException( "Not an entity: " + cls.getName() ); } - - //noinspection unchecked - return (ManagedDomainType) type; + return entityType; } @Override - public EntityDomainType entity(Class cls) { + public @Nullable EmbeddableDomainType findEmbeddableType(Class cls) { final ManagedType type = managedTypeByClass.get( cls ); - if ( !( type instanceof EntityDomainType ) ) { - throw new IllegalArgumentException( "Not an entity: " + cls.getName() ); + if ( !( type instanceof EmbeddableDomainType ) ) { + return null; } //noinspection unchecked - return (EntityDomainType) type; + return (EmbeddableDomainType) type; } @Override public EmbeddableDomainType embeddable(Class cls) { - final ManagedType type = managedTypeByClass.get( cls ); - if ( !( type instanceof EmbeddableDomainType ) ) { + final EmbeddableDomainType embeddableType = findEmbeddableType( cls ); + if ( embeddableType == null ) { throw new IllegalArgumentException( "Not an embeddable: " + cls.getName() ); } - //noinspection unchecked - return (EmbeddableDomainType) type; + return embeddableType; } private Collection> getAllManagedTypes() { @@ -441,7 +475,7 @@ private void applyNamedEntityGraphs(Collection named definition.getEntityName(), definition.getJpaEntityName() ); - final EntityDomainType entityType = entity( definition.getEntityName() ); + final EntityDomainType entityType = findEntityType( definition.getEntityName() ); if ( entityType == null ) { throw new IllegalArgumentException( "Attempted to register named entity graph [" + definition.getRegisteredName() diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java index afe60f76f86f..dc4d46b6553d 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java @@ -17,6 +17,7 @@ import java.util.function.Function; import java.util.stream.Stream; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.EntityNameResolver; import org.hibernate.HibernateException; import org.hibernate.MappingException; @@ -459,16 +460,31 @@ public Set> getEmbeddables() { return jpaMetamodel.getEmbeddables(); } + @Override + public @Nullable ManagedDomainType findManagedType(@Nullable String typeName) { + return jpaMetamodel.findManagedType( typeName ); + } + @Override public ManagedDomainType managedType(String typeName) { return jpaMetamodel.managedType( typeName ); } + @Override + public @Nullable EntityDomainType findEntityType(@Nullable String entityName) { + return jpaMetamodel.findEntityType( entityName ); + } + @Override public EntityDomainType entity(String entityName) { return jpaMetamodel.entity( entityName ); } + @Override + public @Nullable EmbeddableDomainType findEmbeddableType(@Nullable String embeddableName) { + return jpaMetamodel.findEmbeddableType( embeddableName ); + } + @Override public EmbeddableDomainType embeddable(String embeddableName) { return jpaMetamodel.embeddable( embeddableName ); @@ -494,6 +510,11 @@ public EntityDomainType findEntityType(Class cls) { return jpaMetamodel.findEntityType( cls ); } + @Override + public @Nullable EmbeddableDomainType findEmbeddableType(Class cls) { + return jpaMetamodel.findEmbeddableType( cls ); + } + @Override public String qualifyImportableName(String queryName) { return jpaMetamodel.qualifyImportableName( queryName ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/FullyQualifiedReflectivePathTerminal.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/FullyQualifiedReflectivePathTerminal.java index 3b3dea9c9eae..d2b673c7b488 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/FullyQualifiedReflectivePathTerminal.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/FullyQualifiedReflectivePathTerminal.java @@ -71,7 +71,7 @@ public FullyQualifiedReflectivePathTerminal copy(SqmCopyContext context) { // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // See if it is an entity-type literal - final EntityDomainType entityDescriptor = creationContext.getJpaMetamodel().entity( fullPath ); + final EntityDomainType entityDescriptor = creationContext.getJpaMetamodel().findEntityType( fullPath ); if ( entityDescriptor != null ) { return new SqmLiteralEntityType<>( entityDescriptor, creationContext.getNodeBuilder() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java index 67057794c64a..7233dd41e4c2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java @@ -1241,11 +1241,12 @@ private SqmFromClause buildInferredFromClause(HqlParser.SelectClauseContext sele private EntityDomainType getResultEntity() { final JpaMetamodel jpaMetamodel = creationContext.getJpaMetamodel(); if ( expectedResultEntity != null ) { - final EntityDomainType entityDescriptor = jpaMetamodel.entity( expectedResultEntity ); + final EntityDomainType entityDescriptor = jpaMetamodel.findEntityType( expectedResultEntity ); if ( entityDescriptor == null ) { throw new SemanticException( "Query has no 'from' clause, and the result type '" + expectedResultEntity + "' is not an entity type", query ); } + //noinspection unchecked return (EntityDomainType) entityDescriptor; } else if ( expectedResultType != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 1d7965ac2a11..6a9163094f5a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -3114,9 +3114,8 @@ private void registerEntityNameUsage( if ( tableGroup.getModelPart() instanceof EmbeddableValuedModelPart ) { persister = null; final EmbeddableDomainType embeddableDomainType = creationContext.getSessionFactory() - .getRuntimeMetamodels() .getJpaMetamodel() - .embeddable( treatTargetTypeName ); + .findEmbeddableType( treatTargetTypeName ); if ( embeddableDomainType == null || !embeddableDomainType.isPolymorphic() ) { return; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/GetJavaTypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/GetJavaTypeTest.java deleted file mode 100644 index 16d8e54b1f68..000000000000 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/GetJavaTypeTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * SPDX-License-Identifier: LGPL-2.1-or-later - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.test.jpa.compliance; - -import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; -import org.hibernate.testing.orm.junit.Jpa; -import org.junit.jupiter.api.Test; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Table; -import jakarta.persistence.metamodel.Attribute; -import jakarta.persistence.metamodel.ManagedType; -import jakarta.persistence.metamodel.Metamodel; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -@Jpa( - annotatedClasses = GetJavaTypeTest.Person.class -) -public class GetJavaTypeTest { - - @Test - public void getJavaType(EntityManagerFactoryScope scope) { - scope.inEntityManager( - entityManager -> { - Metamodel metaModel = entityManager.getMetamodel(); - if ( metaModel != null ) { - ManagedType mTypeOrder = metaModel.managedType( Person.class ); - assertNotNull( mTypeOrder ); - Attribute attrib = mTypeOrder.getDeclaredAttribute( "age" ); - assertNotNull( attrib ); - Class pAttribJavaType = attrib.getJavaType(); - assertEquals( "int", pAttribJavaType.getName() ); - } - } - ); - } - - @Entity(name = "Person") - @Table(name = "PERSON_TABLE") - public static class Person { - @Id - private int id; - - private String name; - - private int age; - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/MetamodelTypesTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/MetamodelTypesTest.java new file mode 100644 index 000000000000..232e30800b47 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/MetamodelTypesTest.java @@ -0,0 +1,172 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.jpa.compliance; + +import jakarta.persistence.Embeddable; +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.metamodel.Attribute; +import jakarta.persistence.metamodel.ManagedType; +import jakarta.persistence.metamodel.MappedSuperclassType; +import jakarta.persistence.metamodel.Metamodel; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.model.domain.EmbeddableDomainType; +import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.metamodel.model.domain.JpaMetamodel; +import org.hibernate.metamodel.model.domain.ManagedDomainType; +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; + +@Jpa(annotatedClasses = { + MetamodelTypesTest.BaseEntity.class, + MetamodelTypesTest.Person.class, + MetamodelTypesTest.Order.class, +}) +@Jira( "https://hibernate.atlassian.net/browse/HHH-18683" ) +public class MetamodelTypesTest { + @Test + public void getJavaType(EntityManagerFactoryScope scope) { + scope.inEntityManager( entityManager -> { + final Metamodel metaModel = entityManager.getMetamodel(); + final ManagedType mTypeOrder = metaModel.managedType( Person.class ); + assertNotNull( mTypeOrder ); + final Attribute attrib = mTypeOrder.getDeclaredAttribute( "age" ); + assertNotNull( attrib ); + final Class pAttribJavaType = attrib.getJavaType(); + assertEquals( "int", pAttribJavaType.getName() ); + } ); + } + + @Test + public void entityTypeTest(EntityManagerFactoryScope scope) { + scope.inEntityManager( entityManager -> { + final JpaMetamodel jpaMetamodel = entityManager.getEntityManagerFactory() + .unwrap( SessionFactoryImplementor.class ).getJpaMetamodel(); + + // Existing entity type + final EntityDomainType personType = jpaMetamodel.entity( Person.class ); + assertThat( personType ).isNotNull().isSameAs( jpaMetamodel.findEntityType( Person.class ) ) + .extracting( EntityDomainType::getName ).isEqualTo( "Person" ); + assertThat( jpaMetamodel.entity( Person.class.getName() ) ).isSameAs( + jpaMetamodel.findEntityType( Person.class.getName() ) ).isSameAs( personType ); + + // Nonexistent entity type + assertThat( jpaMetamodel.findEntityType( Order.class ) ).isNull(); + assertThat( jpaMetamodel.findEntityType( "AnotherEntity" ) ).isNull(); + try { + jpaMetamodel.entity( "AnotherEntity" ); + fail( "Expected IllegalArgumentException for nonexistent entity type" ); + } + catch (IllegalArgumentException e) { + assertThat( e ).hasMessageContaining( "Not an entity" ); + } + try { + jpaMetamodel.entity( Order.class.getName() ); + fail( "Expected IllegalArgumentException for embeddable type requested as entity" ); + } + catch (IllegalArgumentException e) { + assertThat( e ).hasMessageContaining( "Not an entity" ); + } + } ); + } + + @Test + public void mappedSuperclassTypeTest(EntityManagerFactoryScope scope) { + scope.inEntityManager( entityManager -> { + final JpaMetamodel jpaMetamodel = entityManager.getEntityManagerFactory() + .unwrap( SessionFactoryImplementor.class ).getJpaMetamodel(); + + // Existing mapped superclass type + final ManagedDomainType baseEntityType = jpaMetamodel.managedType( BaseEntity.class ); + assertThat( baseEntityType ).isInstanceOf( MappedSuperclassType.class ) + .isSameAs( jpaMetamodel.findManagedType( BaseEntity.class ) ) + .extracting( ManagedDomainType::getTypeName ).isEqualTo( BaseEntity.class.getName() ); + assertThat( jpaMetamodel.managedType( BaseEntity.class.getName() ) ).isSameAs( + jpaMetamodel.findManagedType( BaseEntity.class.getName() ) ).isSameAs( baseEntityType ); + + // Nonexistent mapped superclass type + assertThat( jpaMetamodel.findManagedType( MetamodelTypesTest.class ) ).isNull(); + assertThat( jpaMetamodel.findManagedType( "AnotherEntity" ) ).isNull(); + try { + jpaMetamodel.managedType( "AnotherEntity" ); + fail( "Expected IllegalArgumentException for nonexistent entity type" ); + } + catch (IllegalArgumentException e) { + assertThat( e ).hasMessageContaining( "Not a managed type" ); + } + try { + jpaMetamodel.managedType( MetamodelTypesTest.class ); + fail( "Expected IllegalArgumentException for embeddable type requested as entity" ); + } + catch (IllegalArgumentException e) { + assertThat( e ).hasMessageContaining( "Not a managed type" ); + } + } ); + } + + @Test + public void embeddableTypeTest(EntityManagerFactoryScope scope) { + scope.inEntityManager( entityManager -> { + final JpaMetamodel jpaMetamodel = entityManager.getEntityManagerFactory() + .unwrap( SessionFactoryImplementor.class ).getJpaMetamodel(); + + // Existing mapped superclass type + final EmbeddableDomainType embeddableType = jpaMetamodel.embeddable( Order.class ); + assertThat( embeddableType ).isNotNull().isSameAs( jpaMetamodel.findEmbeddableType( Order.class ) ) + .extracting( ManagedDomainType::getTypeName ).isEqualTo( Order.class.getName() ); + assertThat( jpaMetamodel.embeddable( Order.class.getName() ) ).isSameAs( + jpaMetamodel.findEmbeddableType( Order.class.getName() ) ).isSameAs( embeddableType ); + + // Nonexistent mapped superclass type + assertThat( jpaMetamodel.findEmbeddableType( BaseEntity.class ) ).isNull(); + assertThat( jpaMetamodel.findEmbeddableType( "AnotherEmbeddable" ) ).isNull(); + try { + jpaMetamodel.embeddable( "AnotherEmbeddable" ); + fail( "Expected IllegalArgumentException for nonexistent entity type" ); + } + catch (IllegalArgumentException e) { + assertThat( e ).hasMessageContaining( "Not an embeddable" ); + } + try { + jpaMetamodel.embeddable( Person.class ); + fail( "Expected IllegalArgumentException for embeddable type requested as entity" ); + } + catch (IllegalArgumentException e) { + assertThat( e ).hasMessageContaining( "Not an embeddable" ); + } + } ); + } + + @MappedSuperclass + public static abstract class BaseEntity { + @Id + private int id; + } + + @Entity(name = "Person") + public static class Person extends BaseEntity { + private String name; + + private int age; + + @Embedded + private Order order; + } + + @Embeddable + public static class Order { + private Integer number; + private String item; + } +} diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/MockSessionFactory.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/MockSessionFactory.java index f6405224e7da..5202f1a67722 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/MockSessionFactory.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/MockSessionFactory.java @@ -784,6 +784,15 @@ public MockJpaMetamodelImpl() { @Override public EntityDomainType entity(String entityName) { + final EntityDomainType entityType = findEntityType( entityName ); + if ( entityType == null ) { + throw new IllegalArgumentException("Not an entity: " + entityName); + } + return entityType; + } + + @Override + public @Nullable EntityDomainType findEntityType(@Nullable String entityName) { if ( isEntityDefined(entityName) ) { return new MockEntityDomainType<>(entityName); } @@ -805,9 +814,18 @@ else if (isEntityDefined(queryName)) { @Override public ManagedDomainType managedType(String typeName) { + final ManagedDomainType managedType = findManagedType( typeName ); + if ( managedType == null ) { + throw new IllegalArgumentException("Not a managed type: " + typeName); + } + return managedType; + } + + @Override + public @Nullable ManagedDomainType findManagedType(@Nullable String typeName) { final String entityName = qualifyName( typeName ); //noinspection unchecked - return entityName == null ? null : (ManagedDomainType) entity( entityName ); + return entityName == null ? null : (ManagedDomainType) findEntityType( entityName ); } @Override