diff --git a/hibernate-core/src/main/antlr/org/hibernate/grammars/graph/GraphLanguageParser.g4 b/hibernate-core/src/main/antlr/org/hibernate/grammars/graph/GraphLanguageParser.g4 index a0e6c33b9974..7f2ff942e343 100644 --- a/hibernate-core/src/main/antlr/org/hibernate/grammars/graph/GraphLanguageParser.g4 +++ b/hibernate-core/src/main/antlr/org/hibernate/grammars/graph/GraphLanguageParser.g4 @@ -21,15 +21,28 @@ package org.hibernate.grammars.graph; */ } - graph - : typeIndicator? attributeList - ; + : typeIndicator? graphElementList + ; + +graphElementList + : graphElement (COMMA graphElement)* + ; + +graphElement + : attributeNode + | subTypeSubGraph + ; typeIndicator : TYPE_NAME COLON ; +subTypeSubGraph + : LPAREN typeIndicator attributeList RPAREN + ; + + attributeList : attributeNode (COMMA attributeNode)* ; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java index a264f3521fd2..8a45f42da743 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java @@ -68,7 +68,7 @@ public EntityDomainType resolveEntityName(String entityName) { //noinspection unchecked final EntityDomainType entityDomainType = (EntityDomainType) entityDomainNameResolver.apply( jpaEntityName ); final String name = this.name == null ? jpaEntityName : this.name; - return GraphParsing.parse( name, entityDomainType, graphContext.attributeList(), entityNameResolver ); + return GraphParsing.parse( name, entityDomainType, graphContext.graphElementList(), entityNameResolver ); } else { if ( graphContext.typeIndicator() != null ) { @@ -77,7 +77,7 @@ public EntityDomainType resolveEntityName(String entityName) { //noinspection unchecked final EntityDomainType entityDomainType = (EntityDomainType) entityDomainClassResolver.apply( (Class) entityType ); final String name = this.name == null ? entityDomainType.getName() : this.name; - return GraphParsing.parse( name, entityDomainType, graphContext.attributeList(), entityNameResolver ); + return GraphParsing.parse( name, entityDomainType, graphContext.graphElementList(), entityNameResolver ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/EntityNameResolver.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/EntityNameResolver.java index 10073e2468b4..d03bc96bf164 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/EntityNameResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/EntityNameResolver.java @@ -5,6 +5,7 @@ package org.hibernate.graph.internal.parse; import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.metamodel.model.domain.ManagedDomainType; /** * @author Steve Ebersole @@ -12,4 +13,12 @@ @FunctionalInterface public interface EntityNameResolver { EntityDomainType resolveEntityName(String entityName); + + static ManagedDomainType managedType(String subtypeName, EntityNameResolver entityNameResolver) { + final EntityDomainType entityDomainType = entityNameResolver.resolveEntityName( subtypeName ); + if ( entityDomainType == null ) { + throw new IllegalArgumentException( "Unknown managed type: " + subtypeName ); + } + return entityDomainType; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java index 74c8d71c1bd4..7304732c78db 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java @@ -27,7 +27,7 @@ public class GraphParser extends GraphLanguageParserBaseVisitor> { private final EntityNameResolver entityNameResolver; private final Stack> graphStack = new StandardStack<>(); - private final Stack> attributeNodeStack = new StandardStack<>(); + private final Stack> attributeNodeStack = new StandardStack<>(); private final Stack graphSourceStack = new StandardStack<>(); public GraphParser(EntityNameResolver entityNameResolver) { @@ -37,7 +37,6 @@ public GraphParser(EntityNameResolver entityNameResolver) { /** * @apiNote It is important that this form only be used after the session-factory is fully * initialized, especially the {@linkplain SessionFactoryImplementor#getJpaMetamodel()} JPA metamodel}. - * * @see GraphParser#GraphParser(EntityNameResolver) */ public GraphParser(SessionFactoryImplementor sessionFactory) { @@ -49,7 +48,50 @@ public Stack> getGraphStack() { } @Override - public AttributeNodeImplementor visitAttributeNode(GraphLanguageParser.AttributeNodeContext attributeNodeContext) { + public SubGraphImplementor visitSubTypeSubGraph(GraphLanguageParser.SubTypeSubGraphContext subTypeSubGraphContext) { + final String subTypeName = subTypeSubGraphContext.typeIndicator() == null ? + null : + subTypeSubGraphContext.typeIndicator().TYPE_NAME().getText(); + + if ( PARSING_LOGGER.isDebugEnabled() ) { + PARSING_LOGGER.debugf( + "%s Starting subtype graph : %s", + StringHelper.repeat( ">>", attributeNodeStack.depth() + 2 ), + subTypeName + ); + } + + var currentGraph = graphStack.getCurrent(); + + var subTypeSubGraph = currentGraph.addTreatedSubgraph( + EntityNameResolver.managedType( + subTypeName, + entityNameResolver + ) + ); + + graphStack.push( subTypeSubGraph ); + + try { + subTypeSubGraphContext.attributeList().accept( this ); + } + finally { + graphStack.pop(); + } + + if ( PARSING_LOGGER.isDebugEnabled() ) { + PARSING_LOGGER.debugf( + "%s Finished subtype graph : %s", + StringHelper.repeat( "<<", attributeNodeStack.depth() + 2 ), + subTypeSubGraph.getGraphedType().getTypeName() + ); + } + + return subTypeSubGraph; + } + + @Override + public AttributeNodeImplementor visitAttributeNode(GraphLanguageParser.AttributeNodeContext attributeNodeContext) { final String attributeName = attributeNodeContext.attributePath().ATTR_NAME().getText(); final SubGraphGenerator subGraphCreator; @@ -66,7 +108,10 @@ public AttributeNodeImplementor visitAttributeNode(GraphLanguageParser.At subGraphCreator = PathQualifierType.VALUE.getSubGraphCreator(); } else { - final String qualifierName = attributeNodeContext.attributePath().attributeQualifier().ATTR_NAME().getText(); + final String qualifierName = attributeNodeContext.attributePath() + .attributeQualifier() + .ATTR_NAME() + .getText(); if ( PARSING_LOGGER.isDebugEnabled() ) { PARSING_LOGGER.debugf( @@ -81,7 +126,7 @@ public AttributeNodeImplementor visitAttributeNode(GraphLanguageParser.At subGraphCreator = pathQualifierType.getSubGraphCreator(); } - final AttributeNodeImplementor attributeNode = resolveAttributeNode( attributeName ); + final AttributeNodeImplementor attributeNode = resolveAttributeNode( attributeName ); if ( attributeNodeContext.subGraph() != null ) { attributeNodeStack.push( attributeNode ); @@ -108,11 +153,11 @@ public AttributeNodeImplementor visitAttributeNode(GraphLanguageParser.At return attributeNode; } - private AttributeNodeImplementor resolveAttributeNode(String attributeName) { + private AttributeNodeImplementor resolveAttributeNode(String attributeName) { final GraphImplementor currentGraph = graphStack.getCurrent(); assert currentGraph != null; - final AttributeNodeImplementor attributeNode = currentGraph.findOrCreateAttributeNode( attributeName ); + final AttributeNodeImplementor attributeNode = currentGraph.findOrCreateAttributeNode( attributeName ); assert attributeNode != null; return attributeNode; @@ -132,7 +177,9 @@ private PathQualifierType resolvePathQualifier(String qualifier) { @Override public SubGraphImplementor visitSubGraph(GraphLanguageParser.SubGraphContext subGraphContext) { - final String subTypeName = subGraphContext.typeIndicator() == null ? null : subGraphContext.typeIndicator().TYPE_NAME().getText(); + final String subTypeName = subGraphContext.typeIndicator() == null ? + null : + subGraphContext.typeIndicator().TYPE_NAME().getText(); if ( PARSING_LOGGER.isDebugEnabled() ) { PARSING_LOGGER.debugf( @@ -142,7 +189,7 @@ public SubGraphImplementor visitSubGraph(GraphLanguageParser.SubGraphContext ); } - final AttributeNodeImplementor attributeNode = attributeNodeStack.getCurrent(); + final AttributeNodeImplementor attributeNode = attributeNodeStack.getCurrent(); final SubGraphGenerator subGraphCreator = graphSourceStack.getCurrent(); final SubGraphImplementor subGraph = subGraphCreator.createSubGraph( diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java index 2c8be74ef601..5d1e28cd35d2 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java @@ -7,6 +7,7 @@ import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.checkerframework.checker.nullness.qual.Nullable; + import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.grammars.graph.GraphLanguageLexer; import org.hibernate.grammars.graph.GraphLanguageParser; @@ -41,7 +42,7 @@ public static RootGraphImplementor parse( } final EntityDomainType entityType = sessionFactory.getJpaMetamodel().entity( entityClass ); - return parse( entityType, graphContext.attributeList(), sessionFactory ); + return parse( entityType, graphContext.graphElementList(), sessionFactory ); } public static RootGraphImplementor parse( @@ -62,7 +63,7 @@ public static RootGraphImplementor parse( throw new InvalidGraphException( "Expecting graph text to not include an entity name : " + graphText ); } - return parse( entityDomainType, graphContext.attributeList(), sessionFactory ); + return parse( entityDomainType, graphContext.graphElementList(), sessionFactory ); } public static RootGraphImplementor parse( @@ -84,8 +85,9 @@ public static RootGraphImplementor parse( } //noinspection unchecked - final EntityDomainType entityType = (EntityDomainType) sessionFactory.getJpaMetamodel().entity( entityName ); - return parse( entityType, graphContext.attributeList(), sessionFactory ); + final EntityDomainType entityType = (EntityDomainType) sessionFactory.getJpaMetamodel() + .entity( entityName ); + return parse( entityType, graphContext.graphElementList(), sessionFactory ); } public static RootGraphImplementor parse( @@ -106,35 +108,36 @@ public static RootGraphImplementor parse( final String entityName = graphContext.typeIndicator().TYPE_NAME().getText(); //noinspection unchecked - final EntityDomainType entityType = (EntityDomainType) sessionFactory.getJpaMetamodel().entity( entityName ); - return parse( entityType, graphContext.attributeList(), sessionFactory ); + final EntityDomainType entityType = (EntityDomainType) sessionFactory.getJpaMetamodel() + .entity( entityName ); + return parse( entityType, graphContext.graphElementList(), sessionFactory ); } public static RootGraphImplementor parse( EntityDomainType rootType, - GraphLanguageParser.AttributeListContext attributeListContext, + GraphLanguageParser.GraphElementListContext graphElementListContext, SessionFactoryImplementor sessionFactory) { - return parse( rootType, attributeListContext, new EntityNameResolverSessionFactory( sessionFactory ) ); + return parse( rootType, graphElementListContext, new EntityNameResolverSessionFactory( sessionFactory ) ); } public static RootGraphImplementor parse( EntityDomainType rootType, - GraphLanguageParser.AttributeListContext attributeListContext, + GraphLanguageParser.GraphElementListContext graphElementListContext, EntityNameResolver entityNameResolver) { - return parse( null, rootType, attributeListContext, entityNameResolver ); + return parse( null, rootType, graphElementListContext, entityNameResolver ); } public static RootGraphImplementor parse( @Nullable String name, EntityDomainType rootType, - GraphLanguageParser.AttributeListContext attributeListContext, + GraphLanguageParser.GraphElementListContext graphElementListContext, EntityNameResolver entityNameResolver) { final RootGraphImpl targetGraph = new RootGraphImpl<>( name, rootType ); final GraphParser visitor = new GraphParser( entityNameResolver ); visitor.getGraphStack().push( targetGraph ); try { - visitor.visitAttributeList( attributeListContext ); + visitor.visitGraphElementList( graphElementListContext ); } finally { visitor.getGraphStack().pop(); @@ -167,7 +170,7 @@ public static void parseInto( visitor.getGraphStack().push( targetGraph ); try { - visitor.visitAttributeList( graphContext.attributeList() ); + visitor.visitGraphElementList( graphContext.graphElementList() ); } finally { visitor.getGraphStack().pop(); diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/PathQualifierType.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/PathQualifierType.java index 3687da5df37f..0fc19b6d3c84 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/PathQualifierType.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/PathQualifierType.java @@ -5,8 +5,7 @@ package org.hibernate.graph.internal.parse; -import org.hibernate.metamodel.model.domain.EntityDomainType; -import org.hibernate.metamodel.model.domain.ManagedDomainType; +import static org.hibernate.graph.internal.parse.EntityNameResolver.managedType; /** * @author Steve Ebersole @@ -23,14 +22,6 @@ public enum PathQualifierType { : attributeNode.addValueSubgraph().addTreatedSubgraph( managedType( subtypeName, entityNameResolver ) ) ); - private static ManagedDomainType managedType(String subtypeName, EntityNameResolver entityNameResolver) { - final EntityDomainType entityDomainType = entityNameResolver.resolveEntityName( subtypeName ); - if ( entityDomainType == null ) { - throw new IllegalArgumentException( "Unknown managed type: " + subtypeName ); - } - return entityDomainType; - } - private final SubGraphGenerator subGraphCreator; PathQualifierType(SubGraphGenerator subgraphCreator) { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/parser/EntityGraphParserTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/parser/EntityGraphParserTest.java index d53bbd42882e..679bb6216bc2 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/parser/EntityGraphParserTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/parser/EntityGraphParserTest.java @@ -65,6 +65,45 @@ public void testLinkParsing() { AssertionHelper.assertBasicAttributes( sub.get( GraphParsingTestEntity.class ), "name", "description" ); } + @Test + public void testSubtypeAndTwoBasicAttributesParsing() { + var graph = parseGraph( "name, (GraphParsingTestSubEntity:sub), description" ); + assertNotNull( graph ); + + AssertionHelper.assertBasicAttributes( graph, "name", "description" ); + + var treatedSubgraphs = graph.getTreatedSubgraphs(); + assertEquals( 1, treatedSubgraphs.size() ); + + var subEntityGraph = treatedSubgraphs.get( GraphParsingTestSubEntity.class ); + var subEntityGraphAttributes = subEntityGraph.getAttributeNodes(); + assertNotNull( subEntityGraphAttributes ); + assertEquals( 1, subEntityGraphAttributes.size() ); + + var subEntityGraphAttributeNode = subEntityGraphAttributes.get( 0 ); + assertNotNull( subEntityGraphAttributeNode ); + assertEquals( "sub", subEntityGraphAttributeNode.getAttributeName() ); + } + + @Test + public void testSubtypeParsing() { + var graph = parseGraph( "(GraphParsingTestSubEntity:sub)" ); + assertNotNull( graph ); + + var treatedSubgraphs = graph.getTreatedSubgraphs(); + assertEquals( 1, treatedSubgraphs.size() ); + + var subEntityGraph = treatedSubgraphs.get( GraphParsingTestSubEntity.class ); + var subEntityGraphAttributes = subEntityGraph.getAttributeNodes(); + + assertNotNull( subEntityGraphAttributes ); + assertEquals( 1, subEntityGraphAttributes.size() ); + + var attributeNode = subEntityGraphAttributes.get( 0 ); + assertNotNull( attributeNode ); + assertEquals( "sub", attributeNode.getAttributeName() ); + } + @Test public void testMapKeyParsing() { EntityGraph graph = parseGraph( "map.key(name, description)" ); @@ -173,14 +212,15 @@ public void testMixParsingWithSimplifiedMaps() { @Test public void testLinkSubtypeParsing() { - RootGraphImplementor graph = parseGraph( "linkToOne(name, description), linkToOne(GraphParsingTestSubEntity: sub)" ); + RootGraphImplementor graph = parseGraph( + "linkToOne(name, description), linkToOne(GraphParsingTestSubEntity: sub)" ); assertNotNull( graph ); - List> attrs = graph.getAttributeNodeList(); + List> attrs = graph.getAttributeNodeList(); assertNotNull( attrs ); assertEquals( 1, attrs.size() ); - AttributeNodeImplementor linkToOneNode = attrs.get( 0 ); + AttributeNodeImplementor linkToOneNode = attrs.get( 0 ); assertNotNull( linkToOneNode ); assertEquals( "linkToOne", linkToOneNode.getAttributeName() ); @@ -204,7 +244,7 @@ public void testHHH10378IsNotFixedYet() { assertEquals( subGraph.getGraphedType().getJavaType(), GraphParsingTestSubEntity.class ); - final AttributeNodeImplementor subTypeAttrNode = subGraph.findOrCreateAttributeNode( "sub" ); + final AttributeNodeImplementor subTypeAttrNode = subGraph.findOrCreateAttributeNode( "sub" ); assert subTypeAttrNode != null; } @@ -221,7 +261,10 @@ public void testHHH12696MapSubgraphsKeyFirst() { checkMapKeyAndValueSubgraphs( graph, mapAttributeName, keySubgraph, valueSubgraph ); } - private void checkMapKeyAndValueSubgraphs(EntityGraph graph, final String mapAttributeName, Subgraph keySubgraph, + private void checkMapKeyAndValueSubgraphs( + EntityGraph graph, + final String mapAttributeName, + Subgraph keySubgraph, Subgraph valueSubgraph) { int count = 0; for ( AttributeNode node : graph.getAttributeNodes() ) {