Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,8 @@ the subtype Class.
Hibernate allows the creation of Jakarta Persistence fetch/load graphs by parsing a textual representation
of the graph. Generally speaking, the textual representation of a graph is a comma-separated
list of attribute names, optionally including any subgraph specifications.
`org.hibernate.graph.GraphParser` is the starting point for such parsing operations.
The starting point for such parsing operations is either `org.hibernate.graph.GraphParser`
or `SessionFactory#parseEntityGraph`

[NOTE]
====
Expand Down Expand Up @@ -370,6 +371,28 @@ include::{example-dir-fetching}/GraphParsingTest.java[tags=fetching-strategies-d
====


[[fetching-strategies-dynamic-fetching-entity-graph-parsing-annotation]]
==== @NamedEntityGraph with text representation

Hibernate also offers a `@org.hibernate.annotations.NamedEntityGraph` annotation, as a corollary
to the `@jakarta.persistence.NamedEntityGraph`, supporting the text representation
<<fetching-strategies-dynamic-fetching-entity-graph-parsing,discussed previously>>. The annotation
may be placed on an entity or on a package.


.@NamedEntityGraph example
====
[source, java, indent=0]
----
@Entity
@NamedEntityGraph( graph="title,isbn,author(name,phoneNumber)" )
class Book {
// ...
}
----
====


[[fetching-strategies-dynamic-fetching-profile]]
=== Dynamic fetching via Hibernate profiles

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ package org.hibernate.grammars.graph;


graph
: attributeList
: typeIndicator? attributeList
;

typeIndicator
: TYPE_NAME COLON
;

attributeList
: attributeNode (COMMA attributeNode)*
;
Expand All @@ -45,9 +49,6 @@ attributeQualifier
;

subGraph
: LPAREN (subType COLON)? attributeList RPAREN
: LPAREN typeIndicator? attributeList RPAREN
;

subType
: TYPE_NAME
;
100 changes: 89 additions & 11 deletions hibernate-core/src/main/java/org/hibernate/SessionFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,31 @@
*/
package org.hibernate;

import java.io.Serializable;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.naming.Referenceable;

import jakarta.persistence.EntityGraph;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.FindOption;
import jakarta.persistence.SynchronizationType;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.graph.GraphParser;
import org.hibernate.graph.InvalidGraphException;
import org.hibernate.graph.RootGraph;
import org.hibernate.graph.internal.RootGraphImpl;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.relational.SchemaManager;
import org.hibernate.stat.Statistics;

import jakarta.persistence.EntityGraph;
import jakarta.persistence.EntityManagerFactory;
import javax.naming.Referenceable;
import java.io.Serializable;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;

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

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

/**
*
* Create an {@link EntityGraph} for the given entity type.
*
* @param entityType The entity type for the graph
*
* @see #createGraphForDynamicEntity(String)
*
* @since 7.0
*/
default <T> RootGraph<T> createEntityGraph(Class<T> entityType) {
return new RootGraphImpl<>( null, (EntityDomainType<T>) getMetamodel().entity( entityType ) );
}

/**
* Create an {@link EntityGraph} which may be used from loading a
* {@linkplain org.hibernate.metamodel.RepresentationMode#MAP dynamic}
Expand All @@ -432,9 +450,69 @@ default <R> R fromStatelessTransaction(Function<? super StatelessSession,R> acti
* @since 7.0
*
* @see Session#find(EntityGraph, Object, FindOption...)
* @see #createEntityGraph(Class)
*/
RootGraph<Map<String,?>> createGraphForDynamicEntity(String entityName);

/**
* Creates a RootGraph for the given {@code rootEntityClass} and parses the graph text into
* it.
*
* @param rootEntityClass The entity class to use as the base of the created root-graph
* @param graphText The textual representation of the graph
*
* @throws InvalidGraphException if the textual representation is invalid.
*
* @see GraphParser#parse(Class, CharSequence, SessionFactory)
* @see #createEntityGraph(Class)
*
* @apiNote The string representation is expected to just be an attribute list. E.g.
* {@code "title, isbn, author(name, books)"}
*
* @since 7.0
*/
default <T> RootGraph<T> parseEntityGraph(Class<T> rootEntityClass, CharSequence graphText) {
return GraphParser.parse( rootEntityClass, graphText.toString(), unwrap( SessionFactoryImplementor.class ) );
}

/**
* Creates a RootGraph for the given {@code rootEntityName} and parses the graph text into
* it.
*
* @param rootEntityName The name of the entity to use as the base of the created root-graph
* @param graphText The textual representation of the graph
*
* @throws InvalidGraphException if the textual representation is invalid.
*
* @see GraphParser#parse(String, CharSequence, SessionFactory)
* @see #createEntityGraph(Class)
*
* @apiNote The string representation is expected to just be an attribute list. E.g.
* {@code "title, isbn, author(name, books)"}
*
* @since 7.0
*/
default <T> RootGraph<T> parseEntityGraph(String rootEntityName, CharSequence graphText) {
return GraphParser.parse( rootEntityName, graphText.toString(), unwrap( SessionFactoryImplementor.class ) );
}

/**
* Creates a RootGraph based on the passed string representation. Here, the
* string representation is expected to include the root entity name.
*
* @param graphText The textual representation of the graph
*
* @throws InvalidGraphException if the textual representation is invalid.
*
* @apiNote The string representation is expected to an attribute list prefixed
* with the name of the root entity. E.g. {@code "Book: title, isbn, author(name, books)"}
*
* @since 7.0
*/
default <T> RootGraph<T> parseEntityGraph(CharSequence graphText) {
return GraphParser.parse( graphText.toString(), unwrap( SessionFactoryImplementor.class ) );
}

/**
* Obtain the set of names of all {@link org.hibernate.annotations.FilterDef
* defined filters}.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.annotations;

import jakarta.persistence.EntityGraph;
import jakarta.persistence.EntityManager;
import org.hibernate.SessionFactory;
import org.hibernate.graph.GraphParser;

import java.lang.annotation.Repeatable;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;

import static java.lang.annotation.ElementType.PACKAGE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
* Defines a named {@linkplain EntityGraph entity graph}
* based on Hibernate's {@linkplain org.hibernate.graph.GraphParser entity graph language}.
* <p/>
* When applied to a root entity class, the root entity name is implied - e.g. {@code "title, isbn, author(name, books)"}
* <p/>
* When applied to a package, the root entity name must be specified - e.g. {@code "Book: title, isbn, author(name, books)"}
*
* @see EntityManager#getEntityGraph(String)
* @see org.hibernate.SessionFactory#parseEntityGraph(CharSequence)
* @see GraphParser#parse(CharSequence, SessionFactory)
*
* @see org.hibernate.graph.GraphParser
* @see jakarta.persistence.NamedEntityGraph
*
* @since 7.0
* @author Steve Ebersole
*/
@Target({TYPE, PACKAGE})
@Retention(RUNTIME)
@Repeatable(NamedEntityGraphs.class)
public @interface NamedEntityGraph {
/**
* The name used to identify the entity graph in calls to
* {@linkplain org.hibernate.Session#getEntityGraph(String)}.
* Entity graph names must be unique within the persistence unit.
* <p/>
* When applied to a root entity class, the name is optional and
* defaults to the entity-name of that entity.
*/
String name() default "";

/**
* The textual representation of the graph.
* <p/>
* When applied to a package, the syntax requires the entity name - e.g., {@code "Book: title, isbn, author(name, books)"}.
* <p/>
* When applied to an entity, the entity name should be omitted - e.g., {@code "title, isbn, author(name, books)"}.
* <p/>
* See {@linkplain org.hibernate.graph.GraphParser} for details about the syntax.
*/
String graph();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.annotations;

import java.lang.annotation.Target;
import java.lang.annotation.Retention;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.PACKAGE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
* A grouping of {@link NamedEntityGraph} definitions.
*
* @since 7.0
* @author Steve Ebersole
*/
@Target({TYPE, PACKAGE, ANNOTATION_TYPE})
@Retention(RUNTIME)
public @interface NamedEntityGraphs {
/**
* The grouping of Hibernate named native SQL queries.
*/
NamedEntityGraph[] value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package org.hibernate.boot.model;

import jakarta.persistence.NamedEntityGraph;
import org.hibernate.mapping.PersistentClass;

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

Expand All @@ -14,34 +15,64 @@
* @author Steve Ebersole
*/
public class NamedEntityGraphDefinition {
private final NamedEntityGraph annotation;
private final String jpaEntityName;
private final String entityName;
public enum Source { JPA, PARSED }

private final String name;

public NamedEntityGraphDefinition(NamedEntityGraph annotation, String jpaEntityName, String entityName) {
this.annotation = annotation;
this.jpaEntityName = jpaEntityName;
this.entityName = entityName;
private final String entityName;

private final Source source;
private final NamedGraphCreator graphCreator;

public NamedEntityGraphDefinition(jakarta.persistence.NamedEntityGraph annotation, String jpaEntityName, String entityName) {
this.name = isNotEmpty( annotation.name() ) ? annotation.name() : jpaEntityName;
if ( name == null ) {
throw new IllegalArgumentException( "Named entity graph name cannot be null" );
}

this.entityName = entityName;

source = Source.JPA;
graphCreator = new NamedGraphCreatorJpa( annotation, jpaEntityName );
}

public String getRegisteredName() {
return name;
public NamedEntityGraphDefinition(org.hibernate.annotations.NamedEntityGraph annotation, PersistentClass persistentClass) {
this.name = isNotEmpty( annotation.name() ) ? annotation.name() : persistentClass.getJpaEntityName();
if ( name == null ) {
throw new IllegalArgumentException( "Named entity graph name cannot be null" );
}

this.entityName = persistentClass.getEntityName();

source = Source.PARSED;
graphCreator = new NamedGraphCreatorParsed( persistentClass.getMappedClass(), annotation );
}

public String getJpaEntityName() {
return jpaEntityName;
public NamedEntityGraphDefinition(org.hibernate.annotations.NamedEntityGraph annotation) {
this.name = annotation.name();
if ( name == null ) {
throw new IllegalArgumentException( "Named entity graph name cannot be null" );
}

this.entityName = null;

source = Source.PARSED;
graphCreator = new NamedGraphCreatorParsed( annotation );
}

public String getRegisteredName() {
return name;
}

public String getEntityName() {
return entityName;
}

public NamedEntityGraph getAnnotation() {
return annotation;
public Source getSource() {
return source;
}

public NamedGraphCreator getGraphCreator() {
return graphCreator;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.model;

import org.hibernate.graph.spi.RootGraphImplementor;
import org.hibernate.metamodel.model.domain.EntityDomainType;

import java.util.function.Function;

/**
* @author Steve Ebersole
*/
@FunctionalInterface
public interface NamedGraphCreator {
<T> RootGraphImplementor<T> createEntityGraph(
Function<Class<T>, EntityDomainType<?>> entityDomainClassResolver,
Function<String, EntityDomainType<?>> entityDomainNameResolver);
}
Loading