Skip to content

Commit 3e61743

Browse files
committed
HHH-19216 - NamedEntityGraph annotation supporting Hibernate parseable format
HHH-19237 - Expand graph language to optionally specify entity HHH-19217 - Expose GraphParser#parse on SessionFactory
1 parent 104edf2 commit 3e61743

28 files changed

+630
-326
lines changed

hibernate-core/src/main/java/org/hibernate/boot/model/NamedGraphCreatorParsed.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66

77
import org.antlr.v4.runtime.CharStreams;
88
import org.antlr.v4.runtime.CommonTokenStream;
9+
import org.hibernate.UnknownEntityTypeException;
910
import org.hibernate.annotations.NamedEntityGraph;
1011
import org.hibernate.grammars.graph.GraphLanguageLexer;
1112
import org.hibernate.grammars.graph.GraphLanguageParser;
1213
import org.hibernate.graph.InvalidGraphException;
14+
import org.hibernate.graph.internal.parse.EntityNameResolver;
1315
import org.hibernate.graph.internal.parse.GraphParsing;
1416
import org.hibernate.graph.spi.RootGraphImplementor;
1517
import org.hibernate.metamodel.model.domain.EntityDomainType;
@@ -42,22 +44,34 @@ public <T> RootGraphImplementor<T> createEntityGraph(
4244
final GraphLanguageParser parser = new GraphLanguageParser( new CommonTokenStream( lexer ) );
4345
final GraphLanguageParser.GraphContext graphContext = parser.graph();
4446

47+
final EntityNameResolver entityNameResolver = new EntityNameResolver() {
48+
@Override
49+
public <T> EntityDomainType<T> resolveEntityName(String entityName) {
50+
//noinspection unchecked
51+
final EntityDomainType<T> entityDomainType = (EntityDomainType<T>) entityDomainNameResolver.apply( entityName );
52+
if ( entityDomainType != null ) {
53+
return entityDomainType;
54+
}
55+
throw new UnknownEntityTypeException( entityName );
56+
}
57+
};
58+
4559
if ( entityType == null ) {
4660
if ( graphContext.typeIndicator() == null ) {
4761
throw new InvalidGraphException( "Expecting graph text to include an entity name : " + annotation.graph() );
4862
}
4963
final String jpaEntityName = graphContext.typeIndicator().TYPE_NAME().toString();
5064
//noinspection unchecked
5165
final EntityDomainType<T> entityDomainType = (EntityDomainType<T>) entityDomainNameResolver.apply( jpaEntityName );
52-
return GraphParsing.parse( entityDomainType, graphContext.attributeList(), runtimeModelCreationContext.getSessionFactory() );
66+
return GraphParsing.parse( entityDomainType, graphContext.attributeList(), entityNameResolver );
5367
}
5468
else {
5569
if ( graphContext.typeIndicator() != null ) {
5670
throw new InvalidGraphException( "Expecting graph text to not include an entity name : " + annotation.graph() );
5771
}
5872
//noinspection unchecked
5973
final EntityDomainType<T> entityDomainType = (EntityDomainType<T>) entityDomainClassResolver.apply( (Class<T>) entityType );
60-
return GraphParsing.parse( entityDomainType, graphContext.attributeList(), runtimeModelCreationContext.getSessionFactory() );
74+
return GraphParsing.parse( entityDomainType, graphContext.attributeList(), entityNameResolver );
6175
}
6276
}
6377
}

hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotationBinder.java

Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import org.hibernate.annotations.Imported;
2323
import org.hibernate.annotations.JavaTypeRegistration;
2424
import org.hibernate.annotations.JdbcTypeRegistration;
25-
import org.hibernate.annotations.NamedEntityGraphs;
2625
import org.hibernate.annotations.TypeRegistration;
2726
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
2827
import org.hibernate.boot.model.NamedEntityGraphDefinition;
@@ -41,7 +40,6 @@
4140
import org.hibernate.type.descriptor.java.BasicJavaType;
4241
import org.hibernate.type.descriptor.jdbc.JdbcType;
4342

44-
import java.lang.annotation.Annotation;
4543
import java.util.HashMap;
4644
import java.util.List;
4745
import java.util.Map;
@@ -153,37 +151,14 @@ public static void bindPackage(ClassLoaderService cls, String packageName, Metad
153151
}
154152

155153
private static void bindNamedEntityGraphs(ClassDetails packageInfoClassDetails, MetadataBuildingContext context) {
156-
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
157-
// @NamedEntityGraphs
158-
159-
final List<? extends Annotation> pluralMetaAnnotated = packageInfoClassDetails.getMetaAnnotated(
160-
NamedEntityGraphs.class,
161-
context.getMetadataCollector().getSourceModelBuildingContext()
162-
);
163-
for ( Annotation pluralMetaAnnotatedTarget : pluralMetaAnnotated ) {
164-
final NamedEntityGraphs pluralMetaAnnotation = pluralMetaAnnotatedTarget.getClass().getDeclaredAnnotation( NamedEntityGraphs.class );
165-
for ( org.hibernate.annotations.NamedEntityGraph namedEntityGraph : pluralMetaAnnotation.value() ) {
166-
final NamedEntityGraphDefinition graphDefinition = new NamedEntityGraphDefinition( namedEntityGraph );
167-
context.getMetadataCollector().addNamedEntityGraph( graphDefinition );
168-
}
169-
}
170-
171-
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
172-
// @NamedEntityGraph
173-
174-
final List<? extends Annotation> singleMetaAnnotated = packageInfoClassDetails.getMetaAnnotated(
175-
org.hibernate.annotations.NamedEntityGraph.class,
176-
context.getMetadataCollector().getSourceModelBuildingContext()
154+
packageInfoClassDetails.forEachRepeatedAnnotationUsages(
155+
HibernateAnnotations.NAMED_ENTITY_GRAPH,
156+
context.getMetadataCollector().getSourceModelBuildingContext(),
157+
(annotation) -> {
158+
final NamedEntityGraphDefinition graphDefinition = new NamedEntityGraphDefinition( annotation );
159+
context.getMetadataCollector().addNamedEntityGraph( graphDefinition );
160+
}
177161
);
178-
for ( Annotation singleMetaAnnotation : singleMetaAnnotated ) {
179-
final org.hibernate.annotations.NamedEntityGraph[] namedEntityGraphs = singleMetaAnnotation.getClass()
180-
.getAnnotationsByType( org.hibernate.annotations.NamedEntityGraph.class );
181-
for ( org.hibernate.annotations.NamedEntityGraph namedEntityGraph : namedEntityGraphs ) {
182-
final NamedEntityGraphDefinition graphDefinition = new NamedEntityGraphDefinition( namedEntityGraph );
183-
context.getMetadataCollector().addNamedEntityGraph( graphDefinition );
184-
}
185-
186-
}
187162
}
188163

189164
public static void bindQueries(AnnotationTarget annotationTarget, MetadataBuildingContext context) {

hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1562,9 +1562,6 @@ private void processNamedEntityGraphs() {
15621562
}
15631563

15641564
private void processParsedNamedGraphs() {
1565-
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1566-
// @NamedEntityGraphs
1567-
15681565
annotatedClass.forEachRepeatedAnnotationUsages(
15691566
HibernateAnnotations.NAMED_ENTITY_GRAPH,
15701567
getSourceModelContext(),
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.graph.internal.parse;
6+
7+
import org.hibernate.metamodel.model.domain.EntityDomainType;
8+
9+
/**
10+
* @author Steve Ebersole
11+
*/
12+
@FunctionalInterface
13+
public interface EntityNameResolver {
14+
<T> EntityDomainType<T> resolveEntityName(String entityName);
15+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.graph.internal.parse;
6+
7+
import org.hibernate.engine.spi.SessionFactoryImplementor;
8+
import org.hibernate.metamodel.model.domain.EntityDomainType;
9+
10+
/**
11+
* @author Steve Ebersole
12+
*/
13+
public class EntityNameResolverSessionFactory implements EntityNameResolver {
14+
private final SessionFactoryImplementor sessionFactory;
15+
16+
public EntityNameResolverSessionFactory(SessionFactoryImplementor sessionFactory) {
17+
this.sessionFactory = sessionFactory;
18+
}
19+
20+
@Override
21+
public <T> EntityDomainType<T> resolveEntityName(String entityName) {
22+
//noinspection unchecked
23+
return (EntityDomainType<T>) sessionFactory.getJpaMetamodel().findEntityType( entityName );
24+
}
25+
}

hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,24 @@
2424
* @author Steve Ebersole
2525
*/
2626
public class GraphParser extends GraphLanguageParserBaseVisitor<GraphNode<?>> {
27-
private final SessionFactoryImplementor sessionFactory;
27+
private final EntityNameResolver entityNameResolver;
2828

2929
private final Stack<GraphImplementor<?>> graphStack = new StandardStack<>();
3030
private final Stack<AttributeNodeImplementor<?,?,?>> attributeNodeStack = new StandardStack<>();
3131
private final Stack<SubGraphGenerator> graphSourceStack = new StandardStack<>();
3232

33+
public GraphParser(EntityNameResolver entityNameResolver) {
34+
this.entityNameResolver = entityNameResolver;
35+
}
36+
37+
/**
38+
* @apiNote It is important that this form only be used after the session-factory is fully
39+
* initialized, especially the {@linkplain SessionFactoryImplementor#getJpaMetamodel()} JPA metamodel}.
40+
*
41+
* @see GraphParser#GraphParser(EntityNameResolver)
42+
*/
3343
public GraphParser(SessionFactoryImplementor sessionFactory) {
34-
this.sessionFactory = sessionFactory;
44+
this( new EntityNameResolverSessionFactory( sessionFactory ) );
3545
}
3646

3747
public Stack<GraphImplementor<?>> getGraphStack() {
@@ -138,7 +148,7 @@ public SubGraphImplementor<?> visitSubGraph(GraphLanguageParser.SubGraphContext
138148
final SubGraphImplementor<?> subGraph = subGraphCreator.createSubGraph(
139149
attributeNode,
140150
subTypeName,
141-
sessionFactory
151+
entityNameResolver
142152
);
143153

144154
graphStack.push( subGraph );

hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,16 @@ public static <T> RootGraphImplementor<T> parse(
113113
EntityDomainType<T> rootType,
114114
GraphLanguageParser.AttributeListContext attributeListContext,
115115
SessionFactoryImplementor sessionFactory) {
116+
return parse( rootType, attributeListContext, new EntityNameResolverSessionFactory( sessionFactory ) );
117+
}
118+
119+
public static <T> RootGraphImplementor<T> parse(
120+
EntityDomainType<T> rootType,
121+
GraphLanguageParser.AttributeListContext attributeListContext,
122+
EntityNameResolver entityNameResolver) {
116123
final RootGraphImpl<T> targetGraph = new RootGraphImpl<>( null, rootType );
117124

118-
final GraphParser visitor = new GraphParser( sessionFactory );
125+
final GraphParser visitor = new GraphParser( entityNameResolver );
119126
visitor.getGraphStack().push( targetGraph );
120127
try {
121128
visitor.visitAttributeList( attributeListContext );

hibernate-core/src/main/java/org/hibernate/graph/internal/parse/PathQualifierType.java

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,30 @@
55
package org.hibernate.graph.internal.parse;
66

77

8-
import org.hibernate.engine.spi.SessionFactoryImplementor;
9-
import org.hibernate.metamodel.model.domain.JpaMetamodel;
8+
import org.hibernate.metamodel.model.domain.EntityDomainType;
109
import org.hibernate.metamodel.model.domain.ManagedDomainType;
1110

1211
/**
1312
* @author Steve Ebersole
1413
*/
1514
public enum PathQualifierType {
1615

17-
KEY( (attributeNode, subtypeName, sessionFactory) -> subtypeName == null
16+
KEY( (attributeNode, subtypeName, entityNameResolver) -> subtypeName == null
1817
? attributeNode.addKeySubgraph()
19-
: attributeNode.addKeySubgraph().addTreatedSubgraph( managedType( subtypeName, sessionFactory ) )
18+
: attributeNode.addKeySubgraph().addTreatedSubgraph( managedType( subtypeName, entityNameResolver ) )
2019
),
2120

22-
VALUE( (attributeNode, subtypeName, sessionFactory) -> subtypeName == null
21+
VALUE( (attributeNode, subtypeName, entityNameResolver) -> subtypeName == null
2322
? attributeNode.addValueSubgraph()
24-
: attributeNode.addValueSubgraph().addTreatedSubgraph( managedType( subtypeName, sessionFactory ) )
23+
: attributeNode.addValueSubgraph().addTreatedSubgraph( managedType( subtypeName, entityNameResolver ) )
2524
);
2625

27-
private static <T> ManagedDomainType<T> managedType(String subtypeName, SessionFactoryImplementor sessionFactory) {
28-
final JpaMetamodel metamodel = sessionFactory.getJpaMetamodel();
29-
ManagedDomainType<T> managedType = metamodel.findManagedType( subtypeName );
30-
if ( managedType == null ) {
31-
managedType = metamodel.getHqlEntityReference( subtypeName );
32-
}
33-
if ( managedType == null ) {
26+
private static <T> ManagedDomainType<T> managedType(String subtypeName, EntityNameResolver entityNameResolver) {
27+
final EntityDomainType<T> entityDomainType = entityNameResolver.resolveEntityName( subtypeName );
28+
if ( entityDomainType == null ) {
3429
throw new IllegalArgumentException( "Unknown managed type: " + subtypeName );
3530
}
36-
return managedType;
31+
return entityDomainType;
3732
}
3833

3934
private final SubGraphGenerator subGraphCreator;

hibernate-core/src/main/java/org/hibernate/graph/internal/parse/SubGraphGenerator.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*/
55
package org.hibernate.graph.internal.parse;
66

7-
import org.hibernate.engine.spi.SessionFactoryImplementor;
87
import org.hibernate.graph.spi.AttributeNodeImplementor;
98
import org.hibernate.graph.spi.SubGraphImplementor;
109

@@ -16,5 +15,5 @@ public interface SubGraphGenerator {
1615
SubGraphImplementor<?> createSubGraph(
1716
AttributeNodeImplementor<?,?,?> attributeNode,
1817
String subTypeName,
19-
SessionFactoryImplementor sessionFactory);
18+
EntityNameResolver entityNameResolver);
2019
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.entitygraph.named.parsed;
6+
7+
import org.hibernate.DuplicateMappingException;
8+
import org.hibernate.boot.MetadataSources;
9+
import org.hibernate.boot.spi.MetadataImplementor;
10+
import org.hibernate.engine.spi.SessionFactoryImplementor;
11+
import org.hibernate.graph.InvalidGraphException;
12+
import org.hibernate.orm.test.entitygraph.named.parsed.entity.Book;
13+
import org.hibernate.orm.test.entitygraph.named.parsed.entity.DomesticPublishingHouse;
14+
import org.hibernate.orm.test.entitygraph.named.parsed.entity.Duplicator;
15+
import org.hibernate.orm.test.entitygraph.named.parsed.entity.ForeignPublishingHouse;
16+
import org.hibernate.orm.test.entitygraph.named.parsed.entity.InvalidParsedGraphEntity;
17+
import org.hibernate.orm.test.entitygraph.named.parsed.entity.Isbn;
18+
import org.hibernate.orm.test.entitygraph.named.parsed.entity.Person;
19+
import org.hibernate.orm.test.entitygraph.named.parsed.entity.Publisher;
20+
import org.hibernate.orm.test.entitygraph.named.parsed.entity.PublishingHouse;
21+
import org.hibernate.testing.orm.junit.DomainModel;
22+
import org.hibernate.testing.orm.junit.DomainModelScope;
23+
import org.hibernate.testing.orm.junit.ServiceRegistry;
24+
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
25+
import org.hibernate.testing.orm.junit.SessionFactory;
26+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
27+
import org.junit.jupiter.api.Test;
28+
29+
import static org.hibernate.orm.test.entitygraph.parser.AssertionHelper.assertBasicAttributes;
30+
import static org.junit.jupiter.api.Assertions.fail;
31+
32+
/**
33+
* Test for Hibernate's {@link org.hibernate.annotations.NamedEntityGraph @NamedEntityGraph}
34+
*
35+
* @author Steve Ebersole
36+
*/
37+
@SuppressWarnings("JUnitMalformedDeclaration")
38+
public class ClassLevelTests {
39+
40+
@Test
41+
@DomainModel(annotatedClasses = {
42+
Book.class,
43+
Person.class,
44+
Publisher.class,
45+
PublishingHouse.class,
46+
DomesticPublishingHouse.class,
47+
ForeignPublishingHouse.class,
48+
Isbn.class
49+
50+
})
51+
@SessionFactory(exportSchema = false)
52+
void testRegistrations(SessionFactoryScope factoryScope) {
53+
final SessionFactoryImplementor sessionFactory = factoryScope.getSessionFactory();
54+
55+
assertBasicAttributes( sessionFactory.findEntityGraphByName( "book-title-isbn" ), "title", "isbn" );
56+
assertBasicAttributes( sessionFactory.findEntityGraphByName( "book-title-isbn-author" ), "title", "isbn", "author" );
57+
assertBasicAttributes( sessionFactory.findEntityGraphByName( "book-title-isbn-editor" ), "title", "isbn", "editor" );
58+
59+
assertBasicAttributes( sessionFactory.findEntityGraphByName( "publishing-house-bio" ), "name", "ceo", "boardMembers" );
60+
}
61+
62+
@Test
63+
@DomainModel(annotatedClasses = InvalidParsedGraphEntity.class)
64+
void testInvalidParsedGraph(DomainModelScope modelScope) {
65+
final MetadataImplementor domainModel = modelScope.getDomainModel();
66+
try {
67+
try (org.hibernate.SessionFactory sessionFactory = domainModel.buildSessionFactory()) {
68+
fail( "Expecting an exception" );
69+
}
70+
catch (InvalidGraphException expected) {
71+
}
72+
}
73+
catch (InvalidGraphException expected) {
74+
}
75+
}
76+
77+
@Test
78+
@ServiceRegistry
79+
void testDuplicateNames(ServiceRegistryScope registryScope) {
80+
final MetadataSources metadataSources = new MetadataSources( registryScope.getRegistry() )
81+
.addAnnotatedClasses( Duplicator.class );
82+
try {
83+
metadataSources.buildMetadata();
84+
fail( "Expecting a failure" );
85+
}
86+
catch (DuplicateMappingException expected) {
87+
}
88+
}
89+
90+
}

0 commit comments

Comments
 (0)