From fb99451a6264caf52bdf9eba1639f61d76aa6e3b Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Sun, 26 Nov 2023 09:13:28 +0100 Subject: [PATCH] #25 - Create AnnotationUsage for named-entity-graph in XML --- .../xml/internal/ManagedTypeProcessor.java | 7 + .../xml/internal/XmlAnnotationHelper.java | 146 +++++++++++++++++- .../orm/xml/dynamic/NamedEntityGraphTest.java | 138 +++++++++++++++++ .../dynamic/dynamic-named-entity-graph.xml | 35 +++++ 4 files changed, 325 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/hibernate/models/orm/xml/dynamic/NamedEntityGraphTest.java create mode 100644 src/test/resources/mappings/dynamic/dynamic-named-entity-graph.xml diff --git a/src/main/java/org/hibernate/boot/models/categorize/xml/internal/ManagedTypeProcessor.java b/src/main/java/org/hibernate/boot/models/categorize/xml/internal/ManagedTypeProcessor.java index c838aa0..60f21ba 100644 --- a/src/main/java/org/hibernate/boot/models/categorize/xml/internal/ManagedTypeProcessor.java +++ b/src/main/java/org/hibernate/boot/models/categorize/xml/internal/ManagedTypeProcessor.java @@ -32,6 +32,7 @@ import org.hibernate.boot.jaxb.mapping.spi.JaxbIdImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbManagedType; import org.hibernate.boot.jaxb.mapping.spi.JaxbMappedSuperclassImpl; +import org.hibernate.boot.jaxb.mapping.spi.JaxbNamedEntityGraphImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbOneToOneImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbPersistentAttribute; import org.hibernate.boot.jaxb.mapping.spi.JaxbPluralAttribute; @@ -59,6 +60,7 @@ import jakarta.persistence.AccessType; import jakarta.persistence.Embeddable; import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.NamedEntityGraph; import static org.hibernate.internal.util.NullnessHelper.coalesce; import static org.hibernate.internal.util.NullnessHelper.nullif; @@ -475,6 +477,11 @@ private static void processEntityMetadata( applyTenantId( classDetails, jaxbEntity, classAccessType, xmlDocumentContext ); + final List namedEntityGraphs = jaxbEntity.getNamedEntityGraphs(); + for ( JaxbNamedEntityGraphImpl namedEntityGraph : namedEntityGraphs ) { + XmlAnnotationHelper.applyNamedEntityGraph( namedEntityGraph, classDetails, xmlDocumentContext ); + } + // todo : secondary-tables } diff --git a/src/main/java/org/hibernate/boot/models/categorize/xml/internal/XmlAnnotationHelper.java b/src/main/java/org/hibernate/boot/models/categorize/xml/internal/XmlAnnotationHelper.java index a300c27..78a8471 100644 --- a/src/main/java/org/hibernate/boot/models/categorize/xml/internal/XmlAnnotationHelper.java +++ b/src/main/java/org/hibernate/boot/models/categorize/xml/internal/XmlAnnotationHelper.java @@ -59,6 +59,9 @@ import org.hibernate.boot.jaxb.mapping.spi.JaxbLifecycleCallback; import org.hibernate.boot.jaxb.mapping.spi.JaxbLifecycleCallbackContainer; import org.hibernate.boot.jaxb.mapping.spi.JaxbLobImpl; +import org.hibernate.boot.jaxb.mapping.spi.JaxbNamedAttributeNodeImpl; +import org.hibernate.boot.jaxb.mapping.spi.JaxbNamedEntityGraphImpl; +import org.hibernate.boot.jaxb.mapping.spi.JaxbNamedSubgraphImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbNationalizedImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbNaturalId; import org.hibernate.boot.jaxb.mapping.spi.JaxbSequenceGeneratorImpl; @@ -107,6 +110,9 @@ import jakarta.persistence.Inheritance; import jakarta.persistence.JoinColumn; import jakarta.persistence.Lob; +import jakarta.persistence.NamedAttributeNode; +import jakarta.persistence.NamedEntityGraph; +import jakarta.persistence.NamedSubgraph; import jakarta.persistence.PostLoad; import jakarta.persistence.PostPersist; import jakarta.persistence.PostRemove; @@ -978,7 +984,7 @@ static void applyEntityListener( entityListeners.setAttributeValue( "value", new ArrayList<>( List.of( entityListenerClass ) ) ); } } - + static void applyLifecycleCallbacks( JaxbLifecycleCallbackContainer lifecycleCallbackContainer, JpaEventListenerStyle callbackType, @@ -1037,4 +1043,142 @@ static void applyRowId( applyAttributeIfSpecified( rowIdAnn, "value", rowId ); } } + + static void applyNamedEntityGraph( + JaxbNamedEntityGraphImpl namedEntityGraph, + MutableClassDetails target, + XmlDocumentContext xmlDocumentContext) { + if ( namedEntityGraph != null ) { + final MutableAnnotationUsage namedEntityGraphAnn = XmlProcessingHelper.getOrMakeAnnotation( + NamedEntityGraph.class, + target, + xmlDocumentContext + ); + + final AnnotationDescriptor namedEntityGraphAnnotationDescriptor = namedEntityGraphAnn.getAnnotationDescriptor(); + applyOr( + namedEntityGraph, + JaxbNamedEntityGraphImpl::getName, + "name", + namedEntityGraphAnn, + namedEntityGraphAnnotationDescriptor + ); + + applyOr( + namedEntityGraph, + JaxbNamedEntityGraphImpl::isIncludeAllAttributes, + "includeAllAttributes", + namedEntityGraphAnn, + namedEntityGraphAnnotationDescriptor + ); + + namedEntityGraphAnn.setAttributeValue( + "attributeNodes", + makeNamedAttributeNodes( namedEntityGraph.getNamedAttributeNode(), target, xmlDocumentContext ) + ); + + namedEntityGraphAnn.setAttributeValue( + "subgraphs", + makeNamedSubgraphs( + target, + xmlDocumentContext, + namedEntityGraph.getSubgraph() + ) + ); + + namedEntityGraphAnn.setAttributeValue( + "subclassSubgraphs", + makeNamedSubgraphs( + target, + xmlDocumentContext, + namedEntityGraph.getSubclassSubgraph() + ) + ); + + } + + } + + private static List> makeNamedSubgraphs( + MutableClassDetails target, + XmlDocumentContext xmlDocumentContext, + List subclassSubgraphNodes) { + final List> subgraphAnnotations = + new ArrayList<>( subclassSubgraphNodes.size() ); + for ( JaxbNamedSubgraphImpl subclassSubgraphNode : subclassSubgraphNodes ) { + final String subGraphsNodeName = subclassSubgraphNode.getName(); + final MutableAnnotationUsage namedSubgraphNodeAnn = XmlProcessingHelper.getOrMakeNamedAnnotation( + NamedSubgraph.class, + subGraphsNodeName, + target, + xmlDocumentContext + ); + applyAttributeIfSpecified( namedSubgraphNodeAnn, "name", subGraphsNodeName ); + + final String clazz = subclassSubgraphNode.getClazz(); + if ( clazz == null ) { + namedSubgraphNodeAnn.setAttributeValue( + "type", + resolveJavaType( + namedSubgraphNodeAnn.getAnnotationDescriptor() + .getAttribute( "type" ) + .getAttributeMethod() + .getDefaultValue().toString(), + xmlDocumentContext + ) + ); + } + else { + namedSubgraphNodeAnn.setAttributeValue( + "type", + resolveJavaType( subclassSubgraphNode.getClazz(), xmlDocumentContext ) + + ); + } + namedSubgraphNodeAnn.setAttributeValue( + "attributeNodes", + makeNamedAttributeNodes( subclassSubgraphNode.getNamedAttributeNode(), target, xmlDocumentContext ) + ); + + subgraphAnnotations.add( namedSubgraphNodeAnn ); + } + return subgraphAnnotations; + } + + private static List> makeNamedAttributeNodes( + List namedAttributeNodes, + MutableClassDetails target, + XmlDocumentContext xmlDocumentContext) { + final List> namedAttributeNodeAnnotations = + new ArrayList<>( namedAttributeNodes.size() ); + for ( JaxbNamedAttributeNodeImpl namedAttributeNode : namedAttributeNodes ) { + final MutableAnnotationUsage namedAttributeNodeAnn = XmlProcessingHelper.makeNestedAnnotation( + NamedAttributeNode.class, + target, + xmlDocumentContext + ); + applyAttributeIfSpecified( namedAttributeNodeAnn, "value", namedAttributeNode.getName() ); + final AnnotationDescriptor namedAttributeNodeDescriptor = xmlDocumentContext + .getModelBuildingContext() + .getAnnotationDescriptorRegistry() + .getDescriptor( NamedAttributeNode.class ); + applyOr( + namedAttributeNode, + JaxbNamedAttributeNodeImpl::getSubgraph, + "subgraph", + namedAttributeNodeAnn, + namedAttributeNodeDescriptor + ); + applyOr( + namedAttributeNode, + JaxbNamedAttributeNodeImpl::getKeySubgraph, + "keySubgraph", + namedAttributeNodeAnn, + namedAttributeNodeDescriptor + ); + namedAttributeNodeAnnotations.add( namedAttributeNodeAnn ); + + } + return namedAttributeNodeAnnotations; + } } diff --git a/src/test/java/org/hibernate/models/orm/xml/dynamic/NamedEntityGraphTest.java b/src/test/java/org/hibernate/models/orm/xml/dynamic/NamedEntityGraphTest.java new file mode 100644 index 0000000..f4ed303 --- /dev/null +++ b/src/test/java/org/hibernate/models/orm/xml/dynamic/NamedEntityGraphTest.java @@ -0,0 +1,138 @@ +package org.hibernate.models.orm.xml.dynamic; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.hibernate.boot.internal.BootstrapContextImpl; +import org.hibernate.boot.internal.MetadataBuilderImpl; +import org.hibernate.boot.model.process.spi.ManagedResources; +import org.hibernate.boot.models.categorize.spi.CategorizedDomainModel; +import org.hibernate.boot.models.categorize.spi.EntityHierarchy; +import org.hibernate.boot.models.categorize.spi.EntityTypeMetadata; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.models.orm.process.ManagedResourcesImpl; +import org.hibernate.models.spi.AnnotationUsage; +import org.hibernate.models.spi.ClassDetails; + +import org.junit.jupiter.api.Test; + +import jakarta.persistence.NamedAttributeNode; +import jakarta.persistence.NamedEntityGraph; +import jakarta.persistence.NamedSubgraph; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.hibernate.boot.models.categorize.spi.ManagedResourcesProcessor.processManagedResources; + +public class NamedEntityGraphTest { + @Test + void testNamedEntityGraph() { + final ManagedResources managedResources = new ManagedResourcesImpl.Builder() + .addXmlMappings( "mappings/dynamic/dynamic-named-entity-graph.xml" ) + .build(); + try (StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().build()) { + final BootstrapContextImpl bootstrapContext = new BootstrapContextImpl( + serviceRegistry, + new MetadataBuilderImpl.MetadataBuildingOptionsImpl( serviceRegistry ) + ); + final CategorizedDomainModel categorizedDomainModel = processManagedResources( + managedResources, + bootstrapContext + ); + + final Set entityHierarchies = categorizedDomainModel.getEntityHierarchies(); + assertThat( entityHierarchies ).hasSize( 1 ); + + entityHierarchies.forEach( + entityHierarchy -> { + final EntityTypeMetadata root = entityHierarchy.getRoot(); + final String entityName = root.getEntityName(); + + final AnnotationUsage namedEntityGraphAnnotationUsage = root.getClassDetails() + .getAnnotationUsage( NamedEntityGraph.class ); + + if ( entityName.equals( "Address" ) ) { + assertThat( namedEntityGraphAnnotationUsage ).isNull(); + } + else { + assertThat( namedEntityGraphAnnotationUsage ).isNotNull(); + + final String graphName = namedEntityGraphAnnotationUsage.getAttributeValue( "name" ); + assertThat( graphName ).isEqualTo( "employee" ); + + final boolean includeAllAttributes = namedEntityGraphAnnotationUsage.getAttributeValue( + "includeAllAttributes" ); + assertThat( includeAllAttributes ).isTrue(); + + List> namedAttributeNodeUsage = namedEntityGraphAnnotationUsage + .getAttributeValue( "attributeNodes" ); + assertThat( namedAttributeNodeUsage ).size().isEqualTo( 2 ); + + final Set namedAttributeNodeValues = new HashSet<>(); + + for ( AnnotationUsage attributeNode : namedAttributeNodeUsage ) { + final String attributeNodeValue = attributeNode.getString( "value" ); + final String subgraph = attributeNode.getString( "subgraph" ); + final String keySubgraph = attributeNode.getString( "keySubgraph" ); + namedAttributeNodeValues.add( attributeNodeValue ); + if ( attributeNodeValue.equals( "name" ) ) { + assertThat( subgraph ).isEqualTo( "" ); + assertThat( keySubgraph ).isEqualTo( "" ); + } + else if ( attributeNodeValue.equals( "address" ) ) { + assertThat( subgraph ).isEqualTo( "employee.address" ); + assertThat( keySubgraph ).isEqualTo( "" ); + } + else { + fail( "NamedEntityGraph, wrong NamedAttributeNode value : " + attributeNodeValue ); + } + } + assertThat( namedAttributeNodeValues ).size().isEqualTo( 2 ); + assertThat( namedAttributeNodeValues ).contains( "name" ); + assertThat( namedAttributeNodeValues ).contains( "address" ); + + final List> subgraphUsages = namedEntityGraphAnnotationUsage + .getAttributeValue( "subgraphs" ); + assertThat( subgraphUsages ).size().isEqualTo( 2 ); + + AnnotationUsage subgraphUsage = subgraphUsages.get( 0 ); + assertThat( subgraphUsage.getString( "name" )).isEqualTo( "employee.address.city" ); + assertThat( subgraphUsage.getAttributeValue( "type" ).getName() ).isEqualTo( void.class.getName() ); + + namedAttributeNodeUsage = subgraphUsage.getAttributeValue( "attributeNodes" ); + assertThat( namedAttributeNodeUsage ).size().isEqualTo( 1 ); + + AnnotationUsage attributeNode = namedAttributeNodeUsage.get( 0 ); + assertThat( attributeNode.getString( "value" ) ).isEqualTo( "city" ); + assertThat( attributeNode.getString( "subgraph" ) ).isEqualTo( "" ); + assertThat( attributeNode.getString( "keySubgraph" ) ).isEqualTo( "" ); + + subgraphUsage = subgraphUsages.get( 1 ); + assertThat( subgraphUsage.getString( "name" )).isEqualTo( "employee.address.cityAndName" ); + assertThat( subgraphUsage.getAttributeValue( "type" ).getName() ).isEqualTo( String.class.getName() ); + + namedAttributeNodeUsage = subgraphUsage.getAttributeValue( "attributeNodes" ); + assertThat( namedAttributeNodeUsage ).size().isEqualTo( 2 ); + + attributeNode = namedAttributeNodeUsage.get( 0 ); + assertThat( attributeNode.getString( "value" ) ).isEqualTo( "city" ); + assertThat( attributeNode.getString( "subgraph" ) ).isEqualTo( "sub1" ); + assertThat( attributeNode.getString( "keySubgraph" ) ).isEqualTo( "" ); + + attributeNode = namedAttributeNodeUsage.get( 1 ); + assertThat( attributeNode.getString( "value" ) ).isEqualTo( "name" ); + assertThat( attributeNode.getString( "subgraph" ) ).isEqualTo( "sub" ); + assertThat( attributeNode.getString( "keySubgraph" ) ).isEqualTo( "" ); + + final List> subClassSubgraphUsages = namedEntityGraphAnnotationUsage + .getAttributeValue( "subclassSubgraphs" ); + assertThat( subClassSubgraphUsages ).size().isEqualTo( 0 ); + + } + } + ); + } + } +} diff --git a/src/test/resources/mappings/dynamic/dynamic-named-entity-graph.xml b/src/test/resources/mappings/dynamic/dynamic-named-entity-graph.xml new file mode 100644 index 0000000..e835d23 --- /dev/null +++ b/src/test/resources/mappings/dynamic/dynamic-named-entity-graph.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file