Skip to content
Merged
2 changes: 1 addition & 1 deletion design/working/attributes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class ForeignCustomer extends Customer {
----

The general idea is that the attributes are ordered alphabetically, super attributes first. Subtypes are sorted
depth-first and the attributes are sorted alphabetically within each sub-type. So the order of the attributes
depth-first and the attributes are sorted alphabetically within each subtype. So the order of the attributes
depends on where we start.

Starting from Customer we have: `[address, name, quota, attr1, attr2, taxId, attr3, attr4, vat]`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ An EntityGraph is the root of a "load plan" and must correspond to an EntityType
[[fetching-strategies-dynamic-fetching-entity-subgraph]]
==== Jakarta Persistence (key) subgraphs

A sub-graph is used to control the fetching of sub-attributes of the AttributeNode it is applied to.
A subgraph is used to control the fetching of sub-attributes of the AttributeNode it is applied to.
It is generally defined via the {jpaJavadocUrlPrefix}NamedSubgraph.html[`@NamedSubgraph`] annotation.

If we have a `Project` parent entity which has an `employees` child associations,
Expand Down Expand Up @@ -264,32 +264,32 @@ include::{extrasdir}/fetching-strategies-dynamic-fetching-entity-subgraph-exampl
----
====

Specifying a sub-graph is only valid for an attribute (or its "key") whose type is a ManagedType. So
Specifying a subgraph is only valid for an attribute (or its "key") whose type is a ManagedType. So
while an EntityGraph must correspond to an EntityType, a Subgraph is legal for any ManagedType. An
attribute's key is defined as either:

* For a singular attribute, the attribute's type must be an IdentifiableType and that IdentifiableType must
have a composite identifier. The "key sub-graph" is applied to the identifier type. The
non-key sub-graph applies to the attribute's value, which must be a ManagedType.
have a composite identifier. The "key subgraph" is applied to the identifier type. The
non-key subgraph applies to the attribute's value, which must be a ManagedType.
* For a plural attribute, the attribute must be a Map and the Map's key value must be a ManagedType.
The "key sub-graph" is applied to the Map's key type. In this case, the non-key sub-graph applies
The "key subgraph" is applied to the Map's key type. In this case, the non-key subgraph applies
to the plural attribute's value/element.


[[fetching-strategies-dynamic-fetching-entity-subgraph-subtype]]
==== Jakarta Persistence SubGraph sub-typing

SubGraphs can also be sub-type specific. Given an attribute whose value is an inheritance hierarchy,
we can refer to attributes of a specific sub-type using the forms of sub-graph definition that accept
the sub-type Class.
Subgraphs can also be subtype specific. Given an attribute whose value is an inheritance hierarchy,
we can refer to attributes of a specific subtype using the forms of subgraph definition that accept
the subtype Class.


[[fetching-strategies-dynamic-fetching-entity-graph-parsing]]
==== Creating and applying Jakarta Persistence graphs from text representations

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 sub-graph specifications.
list of attribute names, optionally including any subgraph specifications.
`org.hibernate.graph.GraphParser` is the starting point for such parsing operations.

[NOTE]
Expand All @@ -312,7 +312,7 @@ This example actually functions exactly as <<fetching-strategies-dynamic-fetchin
just using a parsed graph rather than a named graph.


The syntax also supports defining "key sub-graphs". To specify a key sub-graph, `.key` is added
The syntax also supports defining "key subgraphs". To specify a key subgraph, `.key` is added
to the end of the attribute name.

.Parsing an entity key graph
Expand All @@ -331,7 +331,7 @@ include::{example-dir-fetching}/GraphParsingTest.java[tags=fetching-strategies-d
----
====

Parsing can also handle sub-type specific sub-graphs. For example, given an entity hierarchy of
Parsing can also handle subtype specific subgraphs. For example, given an entity hierarchy of
`LegalEntity` <- (`Corporation` | `Person` | `NonProfit`) and an attribute named `responsibleParty` whose
type is the `LegalEntity` base type we might have:

Expand All @@ -342,7 +342,7 @@ responsibleParty(Corporation: ceo)
----
====

We can even duplicate the attribute names to apply different sub-type sub-graphs:
We can even duplicate the attribute names to apply different subtype subgraphs:

====
[source, java, indent=0]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2655,7 +2655,7 @@ else if ( attributeSource instanceof PluralAttributeSource ) {
throw new AssertionFailure(
String.format(
Locale.ENGLISH,
"Unexpected AttributeSource sub-type [%s] as part of composite [%s]",
"Unexpected AttributeSource subtype [%s] as part of composite [%s]",
attributeSource.getClass().getName(),
attributeSource.getAttributeRole().getFullPath()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.hibernate.boot.model.IdentifierGeneratorDefinition;

/**
* Common contract for composite identifiers. Specific sub-types include aggregated
* Common contract for composite identifiers. Specific subtypes include aggregated
* (think {@link jakarta.persistence.EmbeddedId}) and non-aggregated (think
* {@link jakarta.persistence.IdClass}).
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public interface IdentifiableTypeSource extends AttributeSourceContainer {
/**
* Access the subtype sources for types extending from this type source,
*
* @return Sub-type sources
* @return Subtype sources
*/
Collection<IdentifiableTypeSource> getSubTypes();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
package org.hibernate.graph;

import java.util.Map;
import jakarta.persistence.Subgraph;

import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.PersistentAttribute;

/**
Expand All @@ -20,27 +20,16 @@ public interface AttributeNode<J> extends GraphNode<J>, jakarta.persistence.Attr

PersistentAttribute<?, J> getAttributeDescriptor();

Map<Class<? extends J>, SubGraph<? extends J>> getSubGraphs();
Map<Class<? extends J>, SubGraph<? extends J>> getKeySubGraphs();
Map<Class<?>, ? extends SubGraph<?>> getSubGraphs();
Map<Class<?>, ? extends SubGraph<?>> getKeySubGraphs();

@Override
@SuppressWarnings({"unchecked", "rawtypes"})
default Map<Class, Subgraph> getSubgraphs() {
return (Map) getSubGraphs();
}
SubGraph<?> makeSubGraph();
SubGraph<?> makeKeySubGraph();

@Override
@SuppressWarnings({"unchecked", "rawtypes"})
default Map<Class, Subgraph> getKeySubgraphs() {
return (Map) getKeySubGraphs();
}
<S> SubGraph<S> makeSubGraph(Class<S> type);
<S> SubGraph<S> makeKeySubGraph(Class<S> type);

<S extends J> void addSubGraph(Class<S> subType, SubGraph<S> subGraph);
<S extends J> void addKeySubGraph(Class<S> subType, SubGraph<S> subGraph);
<S> SubGraph<S> makeSubGraph(ManagedDomainType<S> subtype);
<S> SubGraph<S> makeKeySubGraph(ManagedDomainType<S> subtype);

SubGraph<J> makeSubGraph();
SubGraph<J> makeKeySubGraph();

<S extends J> SubGraph<S> makeSubGraph(Class<S> subtype);
<S extends J> SubGraph<S> makeKeySubGraph(Class<S> subtype);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import org.hibernate.HibernateException;

/**
* Indicates an attempt was made to add a (key)? sub-graph to an
* attribute type that does not support (key)? sub-graphs.
* Indicates an attempt was made to add a (key)? subgraph to an
* attribute type that does not support (key)? subgraphs.
*
* @author Steve Ebersole
*/
Expand Down
101 changes: 54 additions & 47 deletions hibernate-core/src/main/java/org/hibernate/graph/EntityGraphs.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,40 +31,38 @@ public final class EntityGraphs {
* Merges multiple entity graphs into a single graph that specifies the
* fetching/loading of all attributes the input graphs specify.
*
* @param <T> Root entity type of the query and graph.
* @param <T> Root entity type of the query and graph.
*
* @param em EntityManager to use to create the new merged graph.
* @param entityManager EntityManager to use to create the new merged graph.
* @param rootType Root type of the entity for which the graph is being merged.
* @param graphs Graphs to merge.
* @param graphs Graphs to merge.
*
* @return The merged graph.
* @return The merged graph.
*/
@SuppressWarnings("unchecked")
public static <T> EntityGraph<T> merge(EntityManager em, Class<T> rootType, EntityGraph<T>... graphs) {
return merge( (SessionImplementor) em, rootType, (Object[]) graphs );
@SafeVarargs
public static <T> EntityGraph<T> merge(EntityManager entityManager, Class<T> rootType, EntityGraph<T>... graphs) {
return mergeInternal( (SessionImplementor) entityManager, rootType, graphs );
}

@SafeVarargs
public static <T> EntityGraph<T> merge(Session session, Class<T> rootType, Graph<T>... graphs) {
return merge( (SessionImplementor) session, rootType, (Object[]) graphs );
return mergeInternal( (SessionImplementor) session, rootType, graphs );
}

@SafeVarargs
public static <T> EntityGraph<T> merge(SessionImplementor session, Class<T> rootType, GraphImplementor<T>... graphs) {
return merge( session, rootType, (Object[]) graphs );
return mergeInternal( session, rootType, graphs );
}

@SuppressWarnings("unchecked")
private static <T> EntityGraph<T> merge(SessionImplementor session, Class<T> rootType, Object... graphs) {
RootGraphImplementor<T> merged = session.createEntityGraph( rootType );

private static <T> EntityGraph<T> mergeInternal(
SessionImplementor session, Class<T> rootType, jakarta.persistence.Graph<T>[] graphs) {
final RootGraphImplementor<T> merged = session.createEntityGraph( rootType );
if ( graphs != null ) {
for ( Object graph : graphs ) {
for ( jakarta.persistence.Graph<T> graph : graphs ) {
merged.merge( (GraphImplementor<T>) graph );
}

}

return merged;
}

Expand All @@ -75,12 +73,14 @@ private static <T> EntityGraph<T> merge(SessionImplementor session, Class<T> roo
* @param query The JPA Query
* @param graph The graph to apply
* @param semantic The semantic to use when applying the graph
*
* @deprecated Use {@link org.hibernate.query.SelectionQuery#setEntityGraph(EntityGraph, GraphSemantic)}
*/
@SuppressWarnings("unchecked")
public static List executeList(Query query, EntityGraph graph, GraphSemantic semantic) {
@Deprecated(since = "7.0")
public static @SuppressWarnings("rawtypes") List executeList(Query query, EntityGraph<?> graph, GraphSemantic semantic) {
return query.unwrap( org.hibernate.query.Query.class )
.applyGraph( (RootGraph) graph, semantic )
.list();
.applyGraph( (RootGraph<?>) graph, semantic )
.getResultList();
}

/**
Expand All @@ -94,10 +94,14 @@ public static List executeList(Query query, EntityGraph graph, GraphSemantic sem
* @apiNote This signature assumes that the Query's return is an entity and that
* the graph applies to that entity's type. JPA does not necessarily
* require that, but it is by far the most common usage.
*
* @deprecated Use {@link org.hibernate.query.SelectionQuery#setEntityGraph(EntityGraph, GraphSemantic)}
*/
@SuppressWarnings({"unused", "unchecked"})
@Deprecated(since = "7.0")
public static <R> List<R> executeList(TypedQuery<R> query, EntityGraph<R> graph, GraphSemantic semantic) {
return executeList( (Query) query, graph, semantic );
@SuppressWarnings("unchecked")
org.hibernate.query.Query<R> unwrapped = query.unwrap( org.hibernate.query.Query.class );
return unwrapped.setEntityGraph( graph, semantic ).getResultList();
}

/**
Expand All @@ -110,12 +114,12 @@ public static <R> List<R> executeList(TypedQuery<R> query, EntityGraph<R> graph,
* @param semanticJpaHintName See {@link GraphSemantic#fromHintName}
*
* @return The result list
*
* @deprecated Use {@link org.hibernate.query.SelectionQuery#setEntityGraph(EntityGraph, GraphSemantic)}
*/
@SuppressWarnings({"unused", "unchecked"})
public static List executeList(Query query, EntityGraph graph, String semanticJpaHintName) {
return query.unwrap( org.hibernate.query.Query.class )
.applyGraph( (RootGraph) graph, GraphSemantic.fromHintName( semanticJpaHintName ) )
.list();
@Deprecated(since = "7.0")
public static @SuppressWarnings("rawtypes") List executeList(Query query, EntityGraph<?> graph, String semanticJpaHintName) {
return executeList( query, graph, GraphSemantic.fromHintName( semanticJpaHintName ) );
}

/**
Expand All @@ -129,10 +133,12 @@ public static List executeList(Query query, EntityGraph graph, String semanticJp
* @apiNote This signature assumes that the Query's return is an entity and that
* the graph applies to that entity's type. JPA does not necessarily
* require that, but it is by far the most common usage.
*
* @deprecated Use {@link org.hibernate.query.SelectionQuery#setEntityGraph(EntityGraph, GraphSemantic)}
*/
@SuppressWarnings({"unused", "unchecked"})
@Deprecated(since = "7.0")
public static <R> List<R> executeList(TypedQuery<R> query, EntityGraph<R> graph, String semanticJpaHintName) {
return executeList( (Query) query, graph, semanticJpaHintName );
return executeList( query, graph, GraphSemantic.fromHintName( semanticJpaHintName ) );
}

/**
Expand All @@ -146,12 +152,12 @@ public static <R> List<R> executeList(TypedQuery<R> query, EntityGraph<R> graph,
* entity graph applied to a query is {@link GraphSemantic#FETCH}.
* This is simply knowledge from JPA EG discussions, nothing that
* is specifically mentioned or discussed in the spec.
*
* @deprecated Use {@link org.hibernate.query.SelectionQuery#setEntityGraph(EntityGraph, GraphSemantic)}
*/
@SuppressWarnings({"unused", "unchecked"})
public static List executeList(Query query, EntityGraph graph) {
return query.unwrap( org.hibernate.query.Query.class )
.applyFetchGraph( (RootGraph) graph )
.list();
@Deprecated(since = "7.0")
public static @SuppressWarnings("rawtypes") List executeList(Query query, EntityGraph<?> graph) {
return executeList( query, graph, GraphSemantic.FETCH );
}

/**
Expand All @@ -164,16 +170,14 @@ public static List executeList(Query query, EntityGraph graph) {
* @apiNote This signature assumes that the Query's return is an entity and that
* the graph applies to that entity's type. JPA does not necessarily
* require that, but it is by far the most common usage.
*
* @deprecated Use {@link org.hibernate.query.SelectionQuery#setEntityGraph(EntityGraph, GraphSemantic)}
*/
@SuppressWarnings("unused")
@Deprecated(since = "7.0")
public static <R> List<R> executeList(TypedQuery<R> query, EntityGraph<R> graph) {
return executeList( query, graph, GraphSemantic.FETCH );
}

// todo : ? - we could add JPA's other Query execution methods
// but really, I think unwrapping as Hibernate's Query and using our
// "proprietary" methods is better (this class is "proprietary" too).

/**
* Compares two entity graphs and returns {@code true} if they are equal,
* ignoring attribute order.
Expand All @@ -191,8 +195,8 @@ public static <T> boolean areEqual(EntityGraph<T> a, EntityGraph<T> b) {
return false;
}

List<AttributeNode<?>> aNodes = a.getAttributeNodes();
List<AttributeNode<?>> bNodes = b.getAttributeNodes();
final List<AttributeNode<?>> aNodes = a.getAttributeNodes();
final List<AttributeNode<?>> bNodes = b.getAttributeNodes();

if ( aNodes.size() != bNodes.size() ) {
return false;
Expand Down Expand Up @@ -226,7 +230,8 @@ public static boolean areEqual(AttributeNode<?> a, AttributeNode<?> b) {
return false;
}
if ( a.getAttributeName().equals( b.getAttributeName() ) ) {
return areEqual( a.getSubgraphs(), b.getSubgraphs() ) && areEqual( a.getKeySubgraphs(), b.getKeySubgraphs() );
return areEqual( a.getSubgraphs(), b.getSubgraphs() )
&& areEqual( a.getKeySubgraphs(), b.getKeySubgraphs() );
}
else {
return false;
Expand All @@ -237,7 +242,9 @@ public static boolean areEqual(AttributeNode<?> a, AttributeNode<?> b) {
* Compares two entity subgraph maps and returns {@code true} if they are equal,
* ignoring order.
*/
public static boolean areEqual(@SuppressWarnings("rawtypes") Map<Class, Subgraph> a, @SuppressWarnings("rawtypes") Map<Class, Subgraph> b) {
public static boolean areEqual(
@SuppressWarnings("rawtypes") Map<Class, Subgraph> a,
@SuppressWarnings("rawtypes") Map<Class, Subgraph> b) {
if ( a == b ) {
return true;
}
Expand All @@ -246,9 +253,9 @@ public static boolean areEqual(@SuppressWarnings("rawtypes") Map<Class, Subgraph
}

@SuppressWarnings("rawtypes")
Set<Class> aKeys = a.keySet();
final Set<Class> aKeys = a.keySet();
@SuppressWarnings("rawtypes")
Set<Class> bKeys = b.keySet();
final Set<Class> bKeys = b.keySet();

if ( aKeys.equals( bKeys ) ) {
for ( Class<?> clazz : aKeys ) {
Expand Down Expand Up @@ -282,16 +289,16 @@ public static boolean areEqual(@SuppressWarnings("rawtypes") Subgraph a, @Suppre
}

@SuppressWarnings("unchecked")
List<AttributeNode<?>> aNodes = a.getAttributeNodes();
final List<AttributeNode<?>> aNodes = a.getAttributeNodes();
@SuppressWarnings("unchecked")
List<AttributeNode<?>> bNodes = b.getAttributeNodes();
final List<AttributeNode<?>> bNodes = b.getAttributeNodes();

if ( aNodes.size() != bNodes.size() ) {
return false;
}

for ( AttributeNode<?> aNode : aNodes ) {
String attributeName = aNode.getAttributeName();
final String attributeName = aNode.getAttributeName();
AttributeNode<?> bNode = null;
for ( AttributeNode<?> bCandidate : bNodes ) {
if ( attributeName.equals( bCandidate.getAttributeName() ) ) {
Expand Down
Loading
Loading