Skip to content

Commit 56f9d3b

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 3de89ba commit 56f9d3b

File tree

23 files changed

+1498
-506
lines changed

23 files changed

+1498
-506
lines changed

hibernate-core/src/main/antlr/org/hibernate/grammars/graph/GraphLanguageParser.g4

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,13 @@ package org.hibernate.grammars.graph;
2525

2626

2727
graph
28-
: attributeList
28+
: typeIndicator? attributeList
2929
;
3030

31+
typeIndicator
32+
: TYPE_NAME COLON
33+
;
34+
3135
attributeList
3236
: attributeNode (COMMA attributeNode)*
3337
;
@@ -45,9 +49,6 @@ attributeQualifier
4549
;
4650

4751
subGraph
48-
: LPAREN (subType COLON)? attributeList RPAREN
52+
: LPAREN typeIndicator? attributeList RPAREN
4953
;
5054

51-
subType
52-
: TYPE_NAME
53-
;

hibernate-core/src/main/java/org/hibernate/SessionFactory.java

Lines changed: 89 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,31 @@
44
*/
55
package org.hibernate;
66

7-
import java.io.Serializable;
8-
import java.sql.Connection;
9-
import java.util.List;
10-
import java.util.Map;
11-
import java.util.Set;
12-
import java.util.function.Consumer;
13-
import java.util.function.Function;
14-
import javax.naming.Referenceable;
15-
7+
import jakarta.persistence.EntityGraph;
168
import jakarta.persistence.EntityManager;
9+
import jakarta.persistence.EntityManagerFactory;
1710
import jakarta.persistence.FindOption;
1811
import jakarta.persistence.SynchronizationType;
1912
import org.hibernate.boot.spi.SessionFactoryOptions;
2013
import org.hibernate.engine.spi.FilterDefinition;
14+
import org.hibernate.engine.spi.SessionFactoryImplementor;
15+
import org.hibernate.graph.GraphParser;
16+
import org.hibernate.graph.InvalidGraphException;
2117
import org.hibernate.graph.RootGraph;
18+
import org.hibernate.graph.internal.RootGraphImpl;
19+
import org.hibernate.metamodel.model.domain.EntityDomainType;
2220
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
2321
import org.hibernate.relational.SchemaManager;
2422
import org.hibernate.stat.Statistics;
2523

26-
import jakarta.persistence.EntityGraph;
27-
import jakarta.persistence.EntityManagerFactory;
24+
import javax.naming.Referenceable;
25+
import java.io.Serializable;
26+
import java.sql.Connection;
27+
import java.util.List;
28+
import java.util.Map;
29+
import java.util.Set;
30+
import java.util.function.Consumer;
31+
import java.util.function.Function;
2832

2933
import static org.hibernate.internal.TransactionManagement.manageTransaction;
3034

@@ -414,6 +418,20 @@ default <R> R fromStatelessTransaction(Function<? super StatelessSession,R> acti
414418
*/
415419
RootGraph<?> findEntityGraphByName(String name);
416420

421+
/**
422+
*
423+
* Create an {@link EntityGraph} for the given entity type.
424+
*
425+
* @param entityType The entity type for the graph
426+
*
427+
* @see #createGraphForDynamicEntity(String)
428+
*
429+
* @since 7.0
430+
*/
431+
default <T> RootGraph<T> createEntityGraph(Class<T> entityType) {
432+
return new RootGraphImpl<>( null, (EntityDomainType<T>) getMetamodel().entity( entityType ) );
433+
}
434+
417435
/**
418436
* Create an {@link EntityGraph} which may be used from loading a
419437
* {@linkplain org.hibernate.metamodel.RepresentationMode#MAP dynamic}
@@ -432,9 +450,69 @@ default <R> R fromStatelessTransaction(Function<? super StatelessSession,R> acti
432450
* @since 7.0
433451
*
434452
* @see Session#find(EntityGraph, Object, FindOption...)
453+
* @see #createEntityGraph(Class)
435454
*/
436455
RootGraph<Map<String,?>> createGraphForDynamicEntity(String entityName);
437456

457+
/**
458+
* Creates a RootGraph for the given {@code rootEntityClass} and parses the graph text into
459+
* it.
460+
*
461+
* @param rootEntityClass The entity class to use as the base of the created root-graph
462+
* @param graphText The textual representation of the graph
463+
*
464+
* @throws InvalidGraphException if the textual representation is invalid.
465+
*
466+
* @see GraphParser#parse(Class, CharSequence, SessionFactory)
467+
* @see #createEntityGraph(Class)
468+
*
469+
* @apiNote The string representation is expected to just be an attribute list. E.g.
470+
* {@code "title, isbn, author(name, books)"}
471+
*
472+
* @since 7.0
473+
*/
474+
default <T> RootGraph<T> parseEntityGraph(Class<T> rootEntityClass, CharSequence graphText) {
475+
return GraphParser.parse( rootEntityClass, graphText.toString(), unwrap( SessionFactoryImplementor.class ) );
476+
}
477+
478+
/**
479+
* Creates a RootGraph for the given {@code rootEntityClass} and parses the graph text into
480+
* it.
481+
*
482+
* @param rootEntityName The name of the entity to use as the base of the created root-graph
483+
* @param graphText The textual representation of the graph
484+
*
485+
* @throws InvalidGraphException if the textual representation is invalid.
486+
*
487+
* @see GraphParser#parse(String, CharSequence, SessionFactory)
488+
* @see #createEntityGraph(Class)
489+
*
490+
* @apiNote The string representation is expected to just be an attribute list. E.g.
491+
* {@code "title, isbn, author(name, books)"}
492+
*
493+
* @since 7.0
494+
*/
495+
default <T> RootGraph<T> parseEntityGraph(String rootEntityName, CharSequence graphText) {
496+
return GraphParser.parse( rootEntityName, graphText.toString(), unwrap( SessionFactoryImplementor.class ) );
497+
}
498+
499+
/**
500+
* Creates a RootGraph based on the passed string representation. Here, the
501+
* string representation is expected to include the root entity name.
502+
*
503+
* @param graphText The textual representation of the graph
504+
*
505+
* @throws InvalidGraphException if the textual representation is invalid.
506+
*
507+
* @apiNote The string representation is expected to just be an attribute list prefixed
508+
* with the name of the root entity. E.g. {@code "Book: title, isbn, author(name, books)"}
509+
*
510+
* @since 7.0
511+
*/
512+
default <T> RootGraph<T> parseEntityGraph(CharSequence graphText) {
513+
return GraphParser.parse( graphText.toString(), unwrap( SessionFactoryImplementor.class ) );
514+
}
515+
438516
/**
439517
* Obtain the set of names of all {@link org.hibernate.annotations.FilterDef
440518
* defined filters}.

hibernate-core/src/main/java/org/hibernate/annotations/NamedEntityGraph.java

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,38 @@
44
*/
55
package org.hibernate.annotations;
66

7+
import jakarta.persistence.EntityGraph;
8+
import jakarta.persistence.EntityManager;
9+
import org.hibernate.SessionFactory;
10+
import org.hibernate.graph.GraphParser;
11+
712
import java.lang.annotation.Repeatable;
813
import java.lang.annotation.Target;
914
import java.lang.annotation.Retention;
1015

11-
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
1216
import static java.lang.annotation.ElementType.PACKAGE;
1317
import static java.lang.annotation.ElementType.TYPE;
1418
import static java.lang.annotation.RetentionPolicy.RUNTIME;
1519

1620
/**
17-
* Same as {@linkplain jakarta.persistence.NamedEntityGraph}, but leveraging
18-
* Hibernate's ability to specify the graph as text.
21+
* Defines a named {@linkplain EntityGraph entity graph}
22+
* based on Hibernate's {@linkplain org.hibernate.graph.GraphParser entity graph language}.
23+
* <p/>
24+
* When applied to a root entity class, the root entity name is implied - e.g. {@code "title, isbn, author(name, books)"}
25+
* <p/>
26+
* When applied to a package, the root entity name must be specified - e.g. {@code "Book: title, isbn, author(name, books)"}
27+
*
28+
* @see EntityManager#getEntityGraph(String)
29+
* @see org.hibernate.SessionFactory#parseEntityGraph(CharSequence)
30+
* @see GraphParser#parse(CharSequence, SessionFactory)
1931
*
2032
* @see org.hibernate.graph.GraphParser
33+
* @see jakarta.persistence.NamedEntityGraph
2134
*
2235
* @since 7.0
2336
* @author Steve Ebersole
2437
*/
25-
@Target({TYPE, PACKAGE, ANNOTATION_TYPE})
38+
@Target({TYPE, PACKAGE})
2639
@Retention(RUNTIME)
2740
@Repeatable(NamedEntityGraphs.class)
2841
public @interface NamedEntityGraph {
@@ -37,20 +50,8 @@
3750
String name() default "";
3851

3952
/**
40-
* The entity-name of the root of the entity {@linkplain #graph graph}.
41-
* <p/>
42-
* When applied to a root entity class, this is optional and
43-
* defaults to the entity-name of that entity.
44-
*
45-
* @apiNote Entity-name is used here rather than entity-class to allow
46-
* for dynamic models.
47-
*/
48-
String rootEntityName() default "";
49-
50-
/**
51-
* The textual representation of the graph, relative to the named
52-
* {@linkplain #rootEntityName() root entity}.
53-
* See {@linkplain org.hibernate.graph.GraphParser} for details.
53+
* The textual representation of the graph - e.g., {@code "title, isbn, author(name, books)"}.
54+
* See {@linkplain org.hibernate.graph.GraphParser} for details about the syntax.
5455
*/
5556
String graph();
5657
}

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

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package org.hibernate.boot.model;
66

77
import jakarta.persistence.NamedEntityGraph;
8+
import org.hibernate.mapping.PersistentClass;
89

910
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
1011

@@ -14,34 +15,64 @@
1415
* @author Steve Ebersole
1516
*/
1617
public class NamedEntityGraphDefinition {
17-
private final NamedEntityGraph annotation;
18-
private final String jpaEntityName;
19-
private final String entityName;
18+
public enum Source { JPA, PARSED }
19+
2020
private final String name;
2121

22-
public NamedEntityGraphDefinition(NamedEntityGraph annotation, String jpaEntityName, String entityName) {
23-
this.annotation = annotation;
24-
this.jpaEntityName = jpaEntityName;
25-
this.entityName = entityName;
22+
private final String entityName;
23+
24+
private final Source source;
25+
private final NamedGraphCreator graphCreator;
26+
27+
public NamedEntityGraphDefinition(jakarta.persistence.NamedEntityGraph annotation, String jpaEntityName, String entityName) {
2628
this.name = isNotEmpty( annotation.name() ) ? annotation.name() : jpaEntityName;
2729
if ( name == null ) {
2830
throw new IllegalArgumentException( "Named entity graph name cannot be null" );
2931
}
32+
33+
this.entityName = entityName;
34+
35+
source = Source.JPA;
36+
graphCreator = new NamedGraphCreatorJpa( annotation, jpaEntityName );
3037
}
3138

32-
public String getRegisteredName() {
33-
return name;
39+
public NamedEntityGraphDefinition(org.hibernate.annotations.NamedEntityGraph annotation, PersistentClass persistentClass) {
40+
this.name = isNotEmpty( annotation.name() ) ? annotation.name() : persistentClass.getJpaEntityName();
41+
if ( name == null ) {
42+
throw new IllegalArgumentException( "Named entity graph name cannot be null" );
43+
}
44+
45+
this.entityName = persistentClass.getEntityName();
46+
47+
source = Source.PARSED;
48+
graphCreator = new NamedGraphCreatorParsed( persistentClass.getMappedClass(), annotation );
3449
}
3550

36-
public String getJpaEntityName() {
37-
return jpaEntityName;
51+
public NamedEntityGraphDefinition(org.hibernate.annotations.NamedEntityGraph annotation) {
52+
this.name = annotation.name();
53+
if ( name == null ) {
54+
throw new IllegalArgumentException( "Named entity graph name cannot be null" );
55+
}
56+
57+
this.entityName = null;
58+
59+
source = Source.PARSED;
60+
graphCreator = new NamedGraphCreatorParsed( annotation );
61+
}
62+
63+
public String getRegisteredName() {
64+
return name;
3865
}
3966

4067
public String getEntityName() {
4168
return entityName;
4269
}
4370

44-
public NamedEntityGraph getAnnotation() {
45-
return annotation;
71+
public Source getSource() {
72+
return source;
73+
}
74+
75+
public NamedGraphCreator getGraphCreator() {
76+
return graphCreator;
4677
}
4778
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.boot.model;
6+
7+
import org.hibernate.graph.spi.RootGraphImplementor;
8+
import org.hibernate.metamodel.model.domain.EntityDomainType;
9+
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
10+
11+
import java.util.function.Function;
12+
13+
/**
14+
* @author Steve Ebersole
15+
*/
16+
@FunctionalInterface
17+
public interface NamedGraphCreator {
18+
<T> RootGraphImplementor<T> createEntityGraph(
19+
Function<Class<T>, EntityDomainType<?>> entityDomainClassResolver,
20+
Function<String, EntityDomainType<?>> entityDomainNameResolver,
21+
RuntimeModelCreationContext runtimeModelCreationContext);
22+
}

0 commit comments

Comments
 (0)