Skip to content

Commit

Permalink
sebersole#25 - Create AnnotationUsage for named-entity-graph in XML
Browse files Browse the repository at this point in the history
  • Loading branch information
dreab8 committed Nov 27, 2023
1 parent 1e187d8 commit fb99451
Show file tree
Hide file tree
Showing 4 changed files with 325 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -475,6 +477,11 @@ private static void processEntityMetadata(

applyTenantId( classDetails, jaxbEntity, classAccessType, xmlDocumentContext );

final List<JaxbNamedEntityGraphImpl> namedEntityGraphs = jaxbEntity.getNamedEntityGraphs();
for ( JaxbNamedEntityGraphImpl namedEntityGraph : namedEntityGraphs ) {
XmlAnnotationHelper.applyNamedEntityGraph( namedEntityGraph, classDetails, xmlDocumentContext );
}

// todo : secondary-tables
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -978,7 +984,7 @@ static void applyEntityListener(
entityListeners.setAttributeValue( "value", new ArrayList<>( List.of( entityListenerClass ) ) );
}
}

static void applyLifecycleCallbacks(
JaxbLifecycleCallbackContainer lifecycleCallbackContainer,
JpaEventListenerStyle callbackType,
Expand Down Expand Up @@ -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<NamedEntityGraph> namedEntityGraphAnn = XmlProcessingHelper.getOrMakeAnnotation(
NamedEntityGraph.class,
target,
xmlDocumentContext
);

final AnnotationDescriptor<NamedEntityGraph> 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<MutableAnnotationUsage<NamedSubgraph>> makeNamedSubgraphs(
MutableClassDetails target,
XmlDocumentContext xmlDocumentContext,
List<JaxbNamedSubgraphImpl> subclassSubgraphNodes) {
final List<MutableAnnotationUsage<NamedSubgraph>> subgraphAnnotations =
new ArrayList<>( subclassSubgraphNodes.size() );
for ( JaxbNamedSubgraphImpl subclassSubgraphNode : subclassSubgraphNodes ) {
final String subGraphsNodeName = subclassSubgraphNode.getName();
final MutableAnnotationUsage<NamedSubgraph> 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<MutableAnnotationUsage<NamedAttributeNode>> makeNamedAttributeNodes(
List<JaxbNamedAttributeNodeImpl> namedAttributeNodes,
MutableClassDetails target,
XmlDocumentContext xmlDocumentContext) {
final List<MutableAnnotationUsage<NamedAttributeNode>> namedAttributeNodeAnnotations =
new ArrayList<>( namedAttributeNodes.size() );
for ( JaxbNamedAttributeNodeImpl namedAttributeNode : namedAttributeNodes ) {
final MutableAnnotationUsage<NamedAttributeNode> namedAttributeNodeAnn = XmlProcessingHelper.makeNestedAnnotation(
NamedAttributeNode.class,
target,
xmlDocumentContext
);
applyAttributeIfSpecified( namedAttributeNodeAnn, "value", namedAttributeNode.getName() );
final AnnotationDescriptor<NamedAttributeNode> 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;
}
}
Original file line number Diff line number Diff line change
@@ -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<EntityHierarchy> entityHierarchies = categorizedDomainModel.getEntityHierarchies();
assertThat( entityHierarchies ).hasSize( 1 );

entityHierarchies.forEach(
entityHierarchy -> {
final EntityTypeMetadata root = entityHierarchy.getRoot();
final String entityName = root.getEntityName();

final AnnotationUsage<NamedEntityGraph> 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<AnnotationUsage<NamedAttributeNode>> namedAttributeNodeUsage = namedEntityGraphAnnotationUsage
.getAttributeValue( "attributeNodes" );
assertThat( namedAttributeNodeUsage ).size().isEqualTo( 2 );

final Set<String> namedAttributeNodeValues = new HashSet<>();

for ( AnnotationUsage<NamedAttributeNode> 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<AnnotationUsage<NamedSubgraph>> subgraphUsages = namedEntityGraphAnnotationUsage
.getAttributeValue( "subgraphs" );
assertThat( subgraphUsages ).size().isEqualTo( 2 );

AnnotationUsage<NamedSubgraph> subgraphUsage = subgraphUsages.get( 0 );
assertThat( subgraphUsage.getString( "name" )).isEqualTo( "employee.address.city" );
assertThat( subgraphUsage.<ClassDetails>getAttributeValue( "type" ).getName() ).isEqualTo( void.class.getName() );

namedAttributeNodeUsage = subgraphUsage.getAttributeValue( "attributeNodes" );
assertThat( namedAttributeNodeUsage ).size().isEqualTo( 1 );

AnnotationUsage<NamedAttributeNode> 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.<ClassDetails>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<AnnotationUsage<NamedSubgraph>> subClassSubgraphUsages = namedEntityGraphAnnotationUsage
.getAttributeValue( "subclassSubgraphs" );
assertThat( subClassSubgraphUsages ).size().isEqualTo( 0 );

}
}
);
}
}
}
35 changes: 35 additions & 0 deletions src/test/resources/mappings/dynamic/dynamic-named-entity-graph.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ SPDX-License-Identifier: Apache-2.0
~ Copyright: Red Hat Inc. and Hibernate Authors
-->
<entity-mappings xmlns="http://www.hibernate.org/xsd/orm/mapping"
version="3.2">
<entity name="Employee" metadata-complete="true">
<named-entity-graph name="employee" include-all-attributes="true">
<named-attribute-node name="name"/>
<named-attribute-node name="address" subgraph="employee.address"/>
<subgraph name="employee.address.city">
<named-attribute-node name="city"/>
</subgraph>
<subgraph name="employee.address.cityAndName" class="String">
<named-attribute-node name="city" subgraph="sub1"/>
<named-attribute-node name="name" subgraph="sub"/>
</subgraph>
</named-entity-graph>
<attributes>
<id name="id"/>
<basic name="name"/>
<basic name="surname"/>
<!-- <one-to-one name="address" fetch="LAZY"/>-->
</attributes>
</entity>
<!-- <entity name="Address" metadata-complete="true">-->
<!-- <attributes>-->
<!-- <id name="id"/>-->
<!-- <basic name="city"/>-->

<!-- </attributes>-->
<!-- </entity>-->
</entity-mappings>

0 comments on commit fb99451

Please sign in to comment.