From 25dce1e8451ea97c788dd1bf2db357e9319e1e0c Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Thu, 19 Oct 2023 17:48:34 -0500 Subject: [PATCH] Continue support for XML complete mappings - inheritance --- .../orm/xml/internal/XmlAnnotationHelper.java | 43 +++++++++++ .../xml/internal/XmlManagedTypeHelper.java | 42 ++++++++-- .../complete/CompleteXmlInheritanceTests.java | 76 +++++++++++++++++++ .../CompleteXmlWithEmbeddableTests.java | 6 ++ .../models/orm/xml/complete/Root.java | 36 +++++++++ .../xml/complete/SimpleCompleteXmlTests.java | 5 ++ .../models/orm/xml/complete/Sub.java | 31 ++++++++ .../mappings/complete/simple-inherited.xml | 28 +++++++ todos.adoc | 3 +- 9 files changed, 263 insertions(+), 7 deletions(-) create mode 100644 hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/CompleteXmlInheritanceTests.java create mode 100644 hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/Root.java create mode 100644 hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/Sub.java create mode 100644 hibernate-models-orm/src/test/resources/mappings/complete/simple-inherited.xml diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/XmlAnnotationHelper.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/XmlAnnotationHelper.java index cd86303..a3b88e0 100644 --- a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/XmlAnnotationHelper.java +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/XmlAnnotationHelper.java @@ -27,7 +27,9 @@ import org.hibernate.boot.jaxb.mapping.JaxbColumnType; import org.hibernate.boot.jaxb.mapping.JaxbConfigurationParameter; import org.hibernate.boot.jaxb.mapping.JaxbConvert; +import org.hibernate.boot.jaxb.mapping.JaxbEmbeddedId; import org.hibernate.boot.jaxb.mapping.JaxbGeneratedValue; +import org.hibernate.boot.jaxb.mapping.JaxbId; import org.hibernate.boot.jaxb.mapping.JaxbLob; import org.hibernate.boot.jaxb.mapping.JaxbNationalized; import org.hibernate.boot.jaxb.mapping.JaxbNaturalId; @@ -55,9 +57,11 @@ import jakarta.persistence.Basic; import jakarta.persistence.Column; import jakarta.persistence.Convert; +import jakarta.persistence.EmbeddedId; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; import jakarta.persistence.Lob; import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; @@ -65,6 +69,7 @@ import jakarta.persistence.Temporal; import jakarta.persistence.TemporalType; +import static jakarta.persistence.FetchType.EAGER; import static java.util.Collections.emptyList; import static org.hibernate.internal.util.NullnessHelper.coalesce; import static org.hibernate.models.internal.StringHelper.nullIfEmpty; @@ -86,6 +91,16 @@ public static void applyBasic( annotationUsage.setAttributeValue( "optional", jaxbBasic.isOptional() ); } + public static void applyBasic( + JaxbId jaxbId, + MutableMemberDetails memberDetails, + SourceModelBuildingContext sourceModelBuildingContext) { + final DynamicAnnotationUsage annotationUsage = new DynamicAnnotationUsage<>( Basic.class, memberDetails ); + memberDetails.addAnnotationUsage( annotationUsage ); + annotationUsage.setAttributeValue( "fetch", EAGER ); + annotationUsage.setAttributeValue( "optional", false ); + } + public static void applyAccess( AccessType accessType, MutableMemberDetails memberDetails, @@ -496,4 +511,32 @@ public static void applyNaturalIdCache( final JaxbCaching jaxbCaching = jaxbNaturalId.getCache(); annotationUsage.setAttributeValue( "region", jaxbCaching.getRegion() ); } + + public static void applyId( + JaxbId jaxbId, + MutableMemberDetails memberDetails, + SourceModelBuildingContext sourceModelBuildingContext) { + if ( jaxbId == null ) { + return; + } + final DynamicAnnotationUsage annotationUsage = new DynamicAnnotationUsage<>( + Id.class, + memberDetails + ); + memberDetails.addAnnotationUsage( annotationUsage ); + } + + public static void applyEmbeddedId( + JaxbEmbeddedId jaxbEmbeddedId, + MutableMemberDetails memberDetails, + SourceModelBuildingContext sourceModelBuildingContext) { + if ( jaxbEmbeddedId == null ) { + return; + } + final DynamicAnnotationUsage annotationUsage = new DynamicAnnotationUsage<>( + EmbeddedId.class, + memberDetails + ); + memberDetails.addAnnotationUsage( annotationUsage ); + } } diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/XmlManagedTypeHelper.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/XmlManagedTypeHelper.java index 2c63eb8..a05415c 100644 --- a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/XmlManagedTypeHelper.java +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/XmlManagedTypeHelper.java @@ -23,6 +23,7 @@ import org.hibernate.models.orm.xml.spi.PersistenceUnitMetadata; import org.hibernate.models.source.internal.MutableClassDetails; import org.hibernate.models.source.internal.MutableMemberDetails; +import org.hibernate.models.source.internal.SourceModelLogging; import org.hibernate.models.source.internal.dynamic.DynamicAnnotationUsage; import org.hibernate.models.source.internal.dynamic.DynamicClassDetails; import org.hibernate.models.source.spi.ClassDetails; @@ -31,6 +32,7 @@ import jakarta.persistence.AccessType; import jakarta.persistence.Embeddable; import jakarta.persistence.Entity; +import jakarta.persistence.Inheritance; import static org.hibernate.internal.util.NullnessHelper.coalesce; @@ -109,6 +111,8 @@ public static void makeCompleteEntityMapping( entityAnn.setAttributeValue( "name", jaxbEntity.getName() ); classDetails.addAnnotationUsage( entityAnn ); + applyInheritance( jaxbEntity, classDetails, sourceModelBuildingContext ); + if ( jaxbEntity.getTable() != null ) { XmlAnnotationHelper.applyTable( jaxbEntity.getTable(), classDetails, persistenceUnitMetadata ); } @@ -132,14 +136,33 @@ public static void makeCompleteEntityMapping( // todo : secondary-tables } + private static void applyInheritance( + JaxbEntity jaxbEntity, + MutableClassDetails classDetails, + SourceModelBuildingContext sourceModelBuildingContext) { + if ( jaxbEntity.getInheritance() == null ) { + return; + } + + final DynamicAnnotationUsage annotationUsage = new DynamicAnnotationUsage<>( + Inheritance.class, + classDetails + ); + classDetails.addAnnotationUsage( annotationUsage ); + annotationUsage.setAttributeValue( "strategy", jaxbEntity.getInheritance().getStrategy() ); + } + private static void handleIdMappings( JaxbAttributes attributes, AccessType classAccessType, MutableClassDetails classDetails, SourceModelBuildingContext sourceModelBuildingContext) { - if ( CollectionHelper.isNotEmpty( attributes.getId() ) ) { - for ( int i = 0; i < attributes.getId().size(); i++ ) { - final JaxbId jaxbId = attributes.getId().get( i ); + final List jaxbIds = attributes.getId(); + final JaxbEmbeddedId jaxbEmbeddedId = attributes.getEmbeddedId(); + + if ( CollectionHelper.isNotEmpty( jaxbIds ) ) { + for ( int i = 0; i < jaxbIds.size(); i++ ) { + final JaxbId jaxbId = jaxbIds.get( i ); final AccessType accessType = coalesce( jaxbId.getAccess(), classAccessType ); final MutableMemberDetails memberDetails = XmlAttributeHelper.findAttributeMember( jaxbId.getName(), @@ -148,6 +171,8 @@ private static void handleIdMappings( sourceModelBuildingContext ); + XmlAnnotationHelper.applyId( jaxbId, memberDetails, sourceModelBuildingContext ); + XmlAnnotationHelper.applyBasic( jaxbId, memberDetails, sourceModelBuildingContext ); XmlAttributeHelper.applyCommonAttributeAnnotations( jaxbId, memberDetails, @@ -201,9 +226,7 @@ private static void handleIdMappings( // todo : unsaved-value? } } - else { - final JaxbEmbeddedId jaxbEmbeddedId = attributes.getEmbeddedId(); - assert jaxbEmbeddedId != null; + else if ( jaxbEmbeddedId != null ) { final AccessType accessType = coalesce( jaxbEmbeddedId.getAccess(), classAccessType ); final MutableMemberDetails memberDetails = XmlAttributeHelper.findAttributeMember( jaxbEmbeddedId.getName(), @@ -212,6 +235,7 @@ private static void handleIdMappings( sourceModelBuildingContext ); + XmlAnnotationHelper.applyEmbeddedId( jaxbEmbeddedId, memberDetails, sourceModelBuildingContext ); XmlAttributeHelper.applyCommonAttributeAnnotations( jaxbEmbeddedId, memberDetails, @@ -225,6 +249,12 @@ private static void handleIdMappings( sourceModelBuildingContext ); } + else { + SourceModelLogging.SOURCE_MODEL_LOGGER.debugf( + "Identifiable type [%s] contained no nor ", + classDetails.getName() + ); + } } public static void makeCompleteEmbeddableMapping( diff --git a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/CompleteXmlInheritanceTests.java b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/CompleteXmlInheritanceTests.java new file mode 100644 index 0000000..6e8197c --- /dev/null +++ b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/CompleteXmlInheritanceTests.java @@ -0,0 +1,76 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.xml.complete; + +import org.hibernate.models.orm.internal.ManagedResourcesImpl; +import org.hibernate.models.orm.spi.AttributeMetadata; +import org.hibernate.models.orm.spi.EntityHierarchy; +import org.hibernate.models.orm.spi.EntityTypeMetadata; +import org.hibernate.models.orm.spi.ManagedResources; +import org.hibernate.models.orm.spi.ProcessResult; +import org.hibernate.models.orm.spi.Processor; +import org.hibernate.models.source.SourceModelTestHelper; +import org.hibernate.models.source.internal.SourceModelBuildingContextImpl; + +import org.junit.jupiter.api.Test; + +import org.jboss.jandex.Index; + +import jakarta.persistence.Id; + +import static jakarta.persistence.InheritanceType.JOINED; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.models.internal.SimpleClassLoading.SIMPLE_CLASS_LOADING; + +/** + * @author Steve Ebersole + */ +public class CompleteXmlInheritanceTests { + @Test + void testIt() { + + final ManagedResourcesImpl.Builder managedResourcesBuilder = new ManagedResourcesImpl.Builder(); + managedResourcesBuilder.addXmlMappings( "mappings/complete/simple-inherited.xml" ); + final ManagedResources managedResources = managedResourcesBuilder.build(); + + final Index jandexIndex = SourceModelTestHelper.buildJandexIndex( + SIMPLE_CLASS_LOADING, + Root.class, + Sub.class + ); + final SourceModelBuildingContextImpl buildingContext = SourceModelTestHelper.createBuildingContext( + jandexIndex, + SIMPLE_CLASS_LOADING + ); + + final ProcessResult processResult = Processor.process( + managedResources, + null, + new Processor.Options() { + @Override + public boolean areGeneratorsGlobal() { + return false; + } + + @Override + public boolean shouldIgnoreUnlistedClasses() { + return false; + } + }, + buildingContext + ); + + assertThat( processResult.getEntityHierarchies() ).hasSize( 1 ); + final EntityHierarchy hierarchy = processResult.getEntityHierarchies().iterator().next(); + assertThat( hierarchy.getInheritanceType() ).isEqualTo( JOINED ); + + final EntityTypeMetadata rootMetadata = hierarchy.getRoot(); + assertThat( rootMetadata.getClassDetails().getClassName() ).isEqualTo( Root.class.getName() ); + final AttributeMetadata idAttr = rootMetadata.findAttribute( "id" ); + assertThat( idAttr.getMember().getAnnotationUsage( Id.class ) ).isNotNull(); + } +} diff --git a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/CompleteXmlWithEmbeddableTests.java b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/CompleteXmlWithEmbeddableTests.java index e170fd4..9ca6150 100644 --- a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/CompleteXmlWithEmbeddableTests.java +++ b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/CompleteXmlWithEmbeddableTests.java @@ -22,6 +22,9 @@ import org.jboss.jandex.Index; import jakarta.persistence.AccessType; +import jakarta.persistence.Basic; +import jakarta.persistence.Embedded; +import jakarta.persistence.Id; import static org.assertj.core.api.Assertions.assertThat; import static org.hibernate.models.internal.SimpleClassLoading.SIMPLE_CLASS_LOADING; @@ -75,8 +78,11 @@ public boolean shouldIgnoreUnlistedClasses() { final AttributeMetadata idAttribute = personMetadata.findAttribute( "id" ); assertThat( idAttribute.getNature() ).isEqualTo( BASIC ); + assertThat( idAttribute.getMember().getAnnotationUsage( Basic.class ) ).isNotNull(); + assertThat( idAttribute.getMember().getAnnotationUsage( Id.class ) ).isNotNull(); final AttributeMetadata nameAttribute = personMetadata.findAttribute( "name" ); assertThat( nameAttribute.getNature() ).isEqualTo( EMBEDDED ); + assertThat( nameAttribute.getMember().getAnnotationUsage( Embedded.class ) ).isNotNull(); } } diff --git a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/Root.java b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/Root.java new file mode 100644 index 0000000..dc3dfde --- /dev/null +++ b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/Root.java @@ -0,0 +1,36 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.xml.complete; + +/** + * @author Steve Ebersole + */ +public class Root { + private Integer id; + private String name; + + protected Root() { + // for Hibernate use + } + + public Root(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/SimpleCompleteXmlTests.java b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/SimpleCompleteXmlTests.java index 6dc59f9..81ba866 100644 --- a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/SimpleCompleteXmlTests.java +++ b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/SimpleCompleteXmlTests.java @@ -22,7 +22,9 @@ import org.jboss.jandex.Index; +import jakarta.persistence.Basic; import jakarta.persistence.Column; +import jakarta.persistence.Id; import static org.assertj.core.api.Assertions.assertThat; import static org.hibernate.models.internal.SimpleClassLoading.SIMPLE_CLASS_LOADING; @@ -73,12 +75,15 @@ public boolean shouldIgnoreUnlistedClasses() { final AttributeMetadata idAttribute = root.findAttribute( "id" ); assertThat( idAttribute.getNature() ).isEqualTo( AttributeMetadata.AttributeNature.BASIC ); + assertThat( idAttribute.getMember().getAnnotationUsage( Basic.class ) ).isNotNull(); + assertThat( idAttribute.getMember().getAnnotationUsage( Id.class ) ).isNotNull(); final AnnotationUsage idColumnAnn = idAttribute.getMember().getAnnotationUsage( Column.class ); assertThat( idColumnAnn ).isNotNull(); assertThat( idColumnAnn.getAttributeValue( "name" ) ).isEqualTo( "pk" ); final AttributeMetadata nameAttribute = root.findAttribute( "name" ); assertThat( nameAttribute.getNature() ).isEqualTo( AttributeMetadata.AttributeNature.BASIC ); + assertThat( nameAttribute.getMember().getAnnotationUsage( Basic.class ) ).isNotNull(); final AnnotationUsage nameColumnAnn = nameAttribute.getMember().getAnnotationUsage( Column.class ); assertThat( nameColumnAnn ).isNotNull(); assertThat( nameColumnAnn.getAttributeValue( "name" ) ).isEqualTo( "description" ); diff --git a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/Sub.java b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/Sub.java new file mode 100644 index 0000000..211a8ac --- /dev/null +++ b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/complete/Sub.java @@ -0,0 +1,31 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.xml.complete; + +/** + * @author Steve Ebersole + */ +public class Sub extends Root { + private String subName; + + protected Sub() { + // for Hibernate use + } + + public Sub(Integer id, String name, String subName) { + super( id, name ); + this.subName = subName; + } + + public String getSubName() { + return subName; + } + + public void setSubName(String subName) { + this.subName = subName; + } +} diff --git a/hibernate-models-orm/src/test/resources/mappings/complete/simple-inherited.xml b/hibernate-models-orm/src/test/resources/mappings/complete/simple-inherited.xml new file mode 100644 index 0000000..ffb25a3 --- /dev/null +++ b/hibernate-models-orm/src/test/resources/mappings/complete/simple-inherited.xml @@ -0,0 +1,28 @@ + + + + org.hibernate.models.orm.xml.complete + + + + + + + + + + + + Root + + + + + \ No newline at end of file diff --git a/todos.adoc b/todos.adoc index 55e4486..975a670 100644 --- a/todos.adoc +++ b/todos.adoc @@ -1,4 +1,5 @@ = Open Questions and To-do items * Hierarchical packages? E.g. should things defined on the `com.acme` package apply to things in the `com.acme.model` package? Should `PackageDetails` have reference to its "parent" `PackageDetails`? -* Model `AnnotationAttributeValue`? Dropped that here from earlier iterations - its only real benefit was to help model the idea of implicit (unspecified) values. But since the quorum is to simply follow JLS, "this is the way". It simplifies the code quite a bit and imo only makes sense if we want to be able to model that implicitness aspect \ No newline at end of file +* Model `AnnotationAttributeValue`? Dropped that here from earlier iterations - its only real benefit was to help model the idea of implicit (unspecified) values. But since the quorum is to simply follow JLS, "this is the way". It simplifies the code quite a bit and imo only makes sense if we want to be able to model that implicitness aspect +* Support ``? This comes from hbm.xml world, but not seeing its usefulness. \ No newline at end of file