diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/PersistenceSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/PersistenceSettings.java index 78a2f2e0a27e..fc255ca14fd9 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/PersistenceSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/PersistenceSettings.java @@ -78,6 +78,8 @@ public interface PersistenceSettings { * @see #SESSION_FACTORY_NAME_IS_JNDI * @see org.hibernate.internal.SessionFactoryRegistry * @see org.hibernate.boot.SessionFactoryBuilder#applyName(String) + * + * @since 7.0 */ String SESSION_FACTORY_JNDI_NAME = "hibernate.session_factory_jndi_name"; diff --git a/hibernate-core/src/main/java/org/hibernate/graph/AttributeNode.java b/hibernate-core/src/main/java/org/hibernate/graph/AttributeNode.java index 94e0fbaad964..3bcdadd3d67b 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/AttributeNode.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/AttributeNode.java @@ -6,8 +6,6 @@ import jakarta.persistence.Subgraph; -import jakarta.persistence.metamodel.ManagedType; -import org.hibernate.Incubating; import org.hibernate.metamodel.model.domain.PersistentAttribute; import java.util.Map; @@ -38,6 +36,8 @@ * @apiNote Historically, this interface declared operations with incorrect generic types, * leading to unsound code. This was in Hibernate 7, with possible breakage to older code. * + * @param The type of the {@linkplain #getAttributeDescriptor attribute} + * * @author Strong Liu * @author Steve Ebersole * @author Andrea Boriero @@ -68,11 +68,29 @@ public interface AttributeNode extends GraphNode, jakarta.persistence.Attr */ Map, ? extends SubGraph> getKeySubGraphs(); + /** + * All value subgraphs rooted at this node. + *

+ * Includes treated subgraphs. + * + * @apiNote This operation is declared with raw types by JPA + * + * @see #getSubGraphs() + */ @Override default @SuppressWarnings("rawtypes") Map getSubgraphs() { return unmodifiableMap( getSubGraphs() ); } + /** + * All key subgraphs rooted at this node. + *

+ * Includes treated subgraphs. + * + * @apiNote This operation is declared with raw types by JPA + * + * @see #getKeySubGraphs() + */ @Override default @SuppressWarnings("rawtypes") Map getKeySubgraphs() { return unmodifiableMap( getKeySubGraphs() ); @@ -81,13 +99,27 @@ public interface AttributeNode extends GraphNode, jakarta.persistence.Attr /** * Create and return a new value {@link SubGraph} rooted at this node, * or return an existing such {@link SubGraph} if there is one. + * + * @deprecated This operation is not properly type safe. + * Note that {@code graph.addAttributeNode(att).makeSubGraph()} is a + * synonym for {@code graph.addSubgraph(att)}. + * + * @see Graph#addSubgraph(jakarta.persistence.metamodel.Attribute) */ + @Deprecated(since = "7.0") SubGraph makeSubGraph(); /** * Create and return a new key {@link SubGraph} rooted at this node, * or return an existing such {@link SubGraph} if there is one. + * + * @deprecated This operation is not properly type safe. + * Note that {@code graph.addAttributeNode(att).makeKeySubGraph()} is a + * synonym for {@code graph.addMapKeySubgraph(att)}. + * + * @see Graph#addMapKeySubgraph(jakarta.persistence.metamodel.MapAttribute) */ + @Deprecated(since = "7.0") SubGraph makeKeySubGraph(); /** @@ -98,8 +130,15 @@ public interface AttributeNode extends GraphNode, jakarta.persistence.Attr * If the given type is a proper subtype of the value type, the result * is a treated subgraph. * + * @deprecated This operation is not properly type safe. + * Note that {@code graph.addAttributeNode(att).makeSubGraph(cl)} + * is a synonym for {@code graph.addTreatedSubgraph(att,cl)}. + * * @param subtype The type or treated type of the value type + * + * @see Graph#addTreatedSubgraph(jakarta.persistence.metamodel.Attribute, Class) */ + @Deprecated(since = "7.0") SubGraph makeSubGraph(Class subtype); /** @@ -110,33 +149,14 @@ public interface AttributeNode extends GraphNode, jakarta.persistence.Attr * If the given type is a proper subtype of the key type, the result * is a treated subgraph. * - * @param subtype The type or treated type of the key type - */ - SubGraph makeKeySubGraph(Class subtype); - - /** - * Create and return a new value {@link SubGraph} rooted at this node, - * with the given type, which may be a subtype of the value type, - * or return an existing such {@link SubGraph} if there is one. - *

- * If the given type is a proper subtype of the value type, the result - * is a treated subgraph. - * - * @param subtype The type or treated type of the value type - */ - @Incubating - SubGraph makeSubGraph(ManagedType subtype); - - /** - * Create and return a new value {@link SubGraph} rooted at this node, - * with the given type, which may be a subtype of the key type, - * or return an existing such {@link SubGraph} if there is one. - *

- * If the given type is a proper subtype of the key type, the result - * is a treated subgraph. + * @deprecated This operation is not properly type safe. + * Note that {@code graph.addAttributeNode(att).makeKeySubGraph(cl)} + * is a synonym for {@code graph.addTreatedMapKeySubgraph(att,cl)}. * * @param subtype The type or treated type of the key type + * + * @see Graph#addTreatedMapKeySubgraph(jakarta.persistence.metamodel.MapAttribute,Class) */ - @Incubating - SubGraph makeKeySubGraph(ManagedType subtype); + @Deprecated(since = "7.0") + SubGraph makeKeySubGraph(Class subtype); } diff --git a/hibernate-core/src/main/java/org/hibernate/graph/Graph.java b/hibernate-core/src/main/java/org/hibernate/graph/Graph.java index 78b89466ed79..f356be84d36f 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/Graph.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/Graph.java @@ -100,6 +100,8 @@ RootGraph makeRootGraph(String name, boolean mutable) /** * Find an existing {@link AttributeNode} by name within this container. + * + * @since 7.0 */ @Override AttributeNode getAttributeNode(String attributeName); @@ -107,6 +109,8 @@ RootGraph makeRootGraph(String name, boolean mutable) /** * Find an existing {@link AttributeNode} by corresponding attribute * reference, within this container. + * + * @since 7.0 */ @Override AttributeNode getAttributeNode(Attribute attribute); @@ -145,6 +149,8 @@ RootGraph makeRootGraph(String name, boolean mutable) * such {@link SubGraph} if there is one. * * @see jakarta.persistence.EntityGraph#addTreatedSubgraph(Class) + * + * @since 7.0 */ SubGraph addTreatedSubgraph(Class type); @@ -152,6 +158,8 @@ RootGraph makeRootGraph(String name, boolean mutable) * Create and return a new (mutable) {@link SubGraph} representing * the given subtype of the type of this node, or return an existing * such {@link SubGraph} if there is one. + * + * @since 7.0 */ @Incubating SubGraph addTreatedSubgraph(ManagedType type); @@ -168,9 +176,7 @@ RootGraph makeRootGraph(String name, boolean mutable) * @param attributeName The name of an attribute of the represented type */ @Override - default SubGraph addSubgraph(String attributeName) { - return addSubGraph( attributeName ); - } + SubGraph addSubgraph(String attributeName); /** * Create and return a new (mutable) {@link SubGraph} associated with @@ -244,6 +250,8 @@ SubGraph addSubgraph(Attribute attribute) * @param type A subtype of the attribute type * * @see #addSubgraph(Attribute, Class) + * + * @since 7.0 */ @Override SubGraph addTreatedSubgraph(Attribute attribute, Class type) @@ -260,6 +268,8 @@ SubGraph addTreatedSubgraph(Attribute attribute, Cl * * @param attribute An attribute of the represented type * @param type A subtype of the attribute type + * + * @since 7.0 */ @Incubating SubGraph addTreatedSubgraph(Attribute attribute, ManagedType type) @@ -337,6 +347,8 @@ SubGraph addSubGraph(PersistentAttribute attribu * {@link SubGraph} if there is one. * * @param attribute A collection-valued attribute of the represented type + * + * @since 7.0 */ @Override SubGraph addElementSubgraph(PluralAttribute attribute); @@ -352,6 +364,8 @@ SubGraph addSubGraph(PersistentAttribute attribu * * @param attribute A collection-valued attribute of the represented type * @param type A subtype of the element type + * + * @since 7.0 */ @Override SubGraph addTreatedElementSubgraph(PluralAttribute attribute, Class type) @@ -368,6 +382,8 @@ SubGraph addTreatedElementSubgraph(PluralAttribute SubGraph addTreatedElementSubgraph(PluralAttribute attribute, ManagedType type) @@ -468,6 +484,8 @@ SubGraph addKeySubGraph(String attributeName, Class type) * * @param attribute A map-valued attribute of the represented type * @param type A subtype of the key type + * + * @since 7.0 */ @Incubating SubGraph addTreatedMapKeySubgraph(MapAttribute attribute, ManagedType type) diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/AttributeNodeImpl.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/AttributeNodeImpl.java index fef40f45ca36..229a20c6ab12 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/AttributeNodeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/AttributeNodeImpl.java @@ -4,18 +4,25 @@ */ package org.hibernate.graph.internal; -import jakarta.persistence.metamodel.ManagedType; +import jakarta.persistence.metamodel.Attribute; import org.hibernate.graph.CannotContainSubGraphException; import org.hibernate.graph.spi.AttributeNodeImplementor; import org.hibernate.graph.spi.SubGraphImplementor; import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.metamodel.model.domain.ManagedDomainType; +import org.hibernate.metamodel.model.domain.MapPersistentAttribute; import org.hibernate.metamodel.model.domain.PersistentAttribute; +import org.hibernate.metamodel.model.domain.PluralPersistentAttribute; import org.hibernate.metamodel.model.domain.SimpleDomainType; import java.util.HashMap; import java.util.Map; +import static jakarta.persistence.metamodel.Attribute.PersistentAttributeType.EMBEDDED; +import static jakarta.persistence.metamodel.Attribute.PersistentAttributeType.MANY_TO_MANY; +import static jakarta.persistence.metamodel.Attribute.PersistentAttributeType.MANY_TO_ONE; +import static jakarta.persistence.metamodel.Attribute.PersistentAttributeType.ONE_TO_MANY; +import static jakarta.persistence.metamodel.Attribute.PersistentAttributeType.ONE_TO_ONE; import static java.util.Collections.emptyMap; @@ -25,30 +32,38 @@ * @author Steve Ebersole * @author Gavin King */ -public class AttributeNodeImpl +public class AttributeNodeImpl extends AbstractGraphNode - implements AttributeNodeImplementor { + implements AttributeNodeImplementor { private final PersistentAttribute attribute; - private final DomainType valueGraphType; + private final DomainType valueGraphType; private final SimpleDomainType keyGraphType; - private SubGraphImplementor valueSubgraph; + private SubGraphImplementor valueSubgraph; private SubGraphImplementor keySubgraph; static AttributeNodeImpl create(PersistentAttribute attribute, boolean mutable) { return new AttributeNodeImpl<>( attribute, mutable, attribute.getValueGraphType(), attribute.getKeyGraphType() ); } + static AttributeNodeImpl create(PluralPersistentAttribute attribute, boolean mutable) { + return new AttributeNodeImpl<>( attribute, mutable, attribute.getValueGraphType(), attribute.getKeyGraphType() ); + } + + static AttributeNodeImpl,V,K> create(MapPersistentAttribute attribute, boolean mutable) { + return new AttributeNodeImpl<>( attribute, mutable, attribute.getValueGraphType(), attribute.getKeyGraphType() ); + } + private AttributeNodeImpl( PersistentAttribute attribute, boolean mutable, - DomainType valueGraphType, SimpleDomainType keyGraphType) { + DomainType valueGraphType, SimpleDomainType keyGraphType) { super( mutable ); this.attribute = attribute; this.valueGraphType = valueGraphType; this.keyGraphType = keyGraphType; } - private AttributeNodeImpl(AttributeNodeImpl that, boolean mutable) { + private AttributeNodeImpl(AttributeNodeImpl that, boolean mutable) { super( mutable ); attribute = that.attribute; valueGraphType = that.valueGraphType; @@ -68,18 +83,29 @@ public PersistentAttribute getAttributeDescriptor() { } @Override - public SubGraphImplementor getSubGraph() { + public SubGraphImplementor addValueSubgraph() { + // this one is intentionally lenient and disfavored + if ( valueSubgraph == null ) { + valueSubgraph = new SubGraphImpl<>( asManagedType( valueGraphType ), true ); + } return valueSubgraph; } @Override - public SubGraphImplementor getKeySubGraph() { - return keySubgraph; + public SubGraphImplementor addSingularSubgraph() { + checkToOne(); + if ( valueSubgraph == null ) { + valueSubgraph = new SubGraphImpl<>( asManagedType( valueGraphType ), true ); + } + // Safe cast, in this case E = J + // TODO: would be more elegant to separate singularSubgraph vs elementSubgraph fields + //noinspection unchecked + return (SubGraphImplementor) valueSubgraph; } @Override - public SubGraphImplementor makeSubGraph() { - verifyMutability(); + public SubGraphImplementor addElementSubgraph() { + checkToMany(); if ( valueSubgraph == null ) { valueSubgraph = new SubGraphImpl<>( asManagedType( valueGraphType ), true ); } @@ -87,33 +113,51 @@ public SubGraphImplementor makeSubGraph() { } @Override - public SubGraphImplementor makeSubGraph(Class subtype) { - final ManagedDomainType managedType = asManagedType( valueGraphType ); - if ( !managedType.getBindableJavaType().isAssignableFrom( subtype ) ) { - throw new IllegalArgumentException( "Not a subtype: " + subtype.getName() ); + public SubGraphImplementor addKeySubgraph() { + checkMap(); + if ( keySubgraph == null ) { + keySubgraph = new SubGraphImpl<>( asManagedType( keyGraphType ), true ); } - @SuppressWarnings("unchecked") - final Class castSuptype = (Class) subtype; - final SubGraphImplementor result = makeSubGraph().addTreatedSubgraph( castSuptype ); - //noinspection unchecked - return (SubGraphImplementor) result; + return keySubgraph; } - @Override - public SubGraphImplementor makeSubGraph(ManagedType subtype) { - final ManagedDomainType managedType = asManagedType( valueGraphType ); - final Class javaType = subtype.getJavaType(); - if ( !managedType.getBindableJavaType().isAssignableFrom( javaType ) ) { - throw new IllegalArgumentException( "Not a subtype: " + javaType.getName() ); + private void checkToOne() { + final Attribute.PersistentAttributeType attributeType = attribute.getPersistentAttributeType(); + if ( attributeType != MANY_TO_ONE && attributeType != ONE_TO_ONE && attributeType != EMBEDDED ) { + throw new CannotContainSubGraphException( "Attribute '" + attribute.getName() + "' is not a to-one association" ); + } + } + + private void checkToMany() { + final Attribute.PersistentAttributeType attributeType = attribute.getPersistentAttributeType(); + if ( attributeType != MANY_TO_MANY && attributeType != ONE_TO_MANY ) { + throw new CannotContainSubGraphException( "Attribute '" + attribute.getName() + "' is not a to-many association" ); + } + } + + @Override @Deprecated + public SubGraphImplementor makeSubGraph() { + verifyMutability(); + if ( valueSubgraph == null ) { + valueSubgraph = new SubGraphImpl<>( asManagedType( valueGraphType ), true ); + } + return valueSubgraph; + } + + @Override @Deprecated + public SubGraphImplementor makeSubGraph(Class subtype) { + final ManagedDomainType managedType = asManagedType( valueGraphType ); + if ( !managedType.getBindableJavaType().isAssignableFrom( subtype ) ) { + throw new IllegalArgumentException( "Not a subtype: " + subtype.getName() ); } @SuppressWarnings("unchecked") - final ManagedDomainType castType = (ManagedDomainType) subtype; - final SubGraphImplementor result = makeSubGraph().addTreatedSubgraph( castType ); + final Class castSuptype = (Class) subtype; + final SubGraphImplementor result = makeSubGraph().addTreatedSubgraph( castSuptype ); //noinspection unchecked return (SubGraphImplementor) result; } - @Override + @Override @Deprecated public SubGraphImplementor makeKeySubGraph() { verifyMutability(); checkMap(); @@ -123,7 +167,7 @@ public SubGraphImplementor makeKeySubGraph() { return keySubgraph; } - @Override + @Override @Deprecated public SubGraphImplementor makeKeySubGraph(Class subtype) { checkMap(); final ManagedDomainType type = asManagedType( keyGraphType ); @@ -137,21 +181,6 @@ public SubGraphImplementor makeKeySubGraph(Class subtype) { return (SubGraphImplementor) result; } - @Override - public SubGraphImplementor makeKeySubGraph(ManagedType subtype) { - checkMap(); - final ManagedDomainType type = asManagedType( keyGraphType ); - final Class javaType = subtype.getJavaType(); - if ( !type.getBindableJavaType().isAssignableFrom( javaType ) ) { - throw new IllegalArgumentException( "Not a key subtype: " + javaType.getName() ); - } - @SuppressWarnings("unchecked") - final ManagedDomainType castType = (ManagedDomainType) subtype; - final SubGraphImplementor result = makeKeySubGraph().addTreatedSubgraph( castType ); - //noinspection unchecked - return (SubGraphImplementor) result; - } - private void checkMap() { if ( keyGraphType == null ) { throw new CannotContainSubGraphException( "Attribute '" + description() + "' is not a Map" ); @@ -179,16 +208,16 @@ public String toString() { } @Override - public AttributeNodeImplementor makeCopy(boolean mutable) { + public AttributeNodeImplementor makeCopy(boolean mutable) { return !mutable && !isMutable() ? this : new AttributeNodeImpl<>( this, mutable ); } @Override - public void merge(AttributeNodeImplementor other) { + public void merge(AttributeNodeImplementor other) { assert other.isMutable() == isMutable(); assert other.getAttributeDescriptor() == attribute; - final AttributeNodeImpl that = (AttributeNodeImpl) other; - final SubGraphImplementor otherValueSubgraph = that.valueSubgraph; + final AttributeNodeImpl that = (AttributeNodeImpl) other; + final SubGraphImplementor otherValueSubgraph = that.valueSubgraph; if ( otherValueSubgraph != null ) { if ( valueSubgraph == null ) { valueSubgraph = otherValueSubgraph.makeCopy( isMutable() ); @@ -216,7 +245,7 @@ public Map, SubGraphImplementor> getSubGraphs() { return emptyMap(); } else { - final HashMap, SubGraphImplementor> map = new HashMap<>( valueSubgraph.getSubGraphs() ); + final HashMap, SubGraphImplementor> map = new HashMap<>( valueSubgraph.getTreatedSubgraphs() ); map.put( attribute.getValueGraphType().getBindableJavaType(), valueSubgraph ); return map; } @@ -228,7 +257,7 @@ public Map, SubGraphImplementor> getKeySubGraphs() { return emptyMap(); } else { - final HashMap, SubGraphImplementor> map = new HashMap<>( keySubgraph.getSubGraphs() ); + final HashMap, SubGraphImplementor> map = new HashMap<>( keySubgraph.getTreatedSubgraphs() ); map.put( attribute.getKeyGraphType().getJavaType(), keySubgraph ); return map; } diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/AbstractGraph.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/GraphImpl.java similarity index 62% rename from hibernate-core/src/main/java/org/hibernate/graph/internal/AbstractGraph.java rename to hibernate-core/src/main/java/org/hibernate/graph/internal/GraphImpl.java index 0d8d2d8b4972..95ff3b94ea82 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/AbstractGraph.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/GraphImpl.java @@ -22,6 +22,7 @@ import org.hibernate.graph.spi.GraphImplementor; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.graph.spi.SubGraphImplementor; +import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.ManagedDomainType; import org.hibernate.metamodel.model.domain.MapPersistentAttribute; @@ -41,28 +42,28 @@ * @author Steve Ebersole * @author Gavin King */ -public abstract class AbstractGraph extends AbstractGraphNode implements GraphImplementor { +public abstract class GraphImpl extends AbstractGraphNode implements GraphImplementor { private final ManagedDomainType managedType; private Map, SubGraphImplementor> treatedSubgraphs; - private Map, AttributeNodeImplementor> attributeNodes; + private Map, AttributeNodeImplementor> attributeNodes; - public AbstractGraph(ManagedDomainType managedType, boolean mutable) { + public GraphImpl(ManagedDomainType managedType, boolean mutable) { super( mutable ); this.managedType = managedType; } - protected AbstractGraph(ManagedDomainType managedType, GraphImplementor graph, boolean mutable) { + protected GraphImpl(ManagedDomainType managedType, GraphImplementor graph, boolean mutable) { super( mutable ); this.managedType = managedType; var attributeNodesByAttribute = graph.getNodes(); - var subGraphMap = graph.getSubGraphs(); + var subGraphMap = graph.getTreatedSubgraphs(); attributeNodes = attributeNodesByAttribute.isEmpty() ? null : new HashMap<>( attributeNodesByAttribute.size() ); treatedSubgraphs = subGraphMap.isEmpty() ? null : new HashMap<>( subGraphMap.size() ); mergeInternal( graph ); } - protected AbstractGraph(GraphImplementor graph, boolean mutable) { + protected GraphImpl(GraphImplementor graph, boolean mutable) { this( graph.getGraphedType(), graph, mutable ); } @@ -77,8 +78,18 @@ private SubGraphImplementor getTreatedSubgraph(Class javaTyp } @SuppressWarnings("unchecked") - private AttributeNodeImplementor getNode(PersistentAttribute attribute) { - return attributeNodes == null ? null : (AttributeNodeImplementor) attributeNodes.get( attribute ); + private AttributeNodeImplementor getNode(PersistentAttribute attribute) { + return attributeNodes == null ? null : (AttributeNodeImplementor) attributeNodes.get( attribute ); + } + + @SuppressWarnings("unchecked") + private AttributeNodeImplementor getNode(PluralPersistentAttribute attribute) { + return attributeNodes == null ? null : (AttributeNodeImplementor) attributeNodes.get( attribute ); + } + + @SuppressWarnings("unchecked") + private AttributeNodeImplementor, V, K> getNode(MapPersistentAttribute attribute) { + return attributeNodes == null ? null : (AttributeNodeImplementor, V, K>) attributeNodes.get( attribute ); } private SubGraphImplementor getTreatedSubgraphForPut(Class javaType) { @@ -91,7 +102,27 @@ private SubGraphImplementor getTreatedSubgraphForPut(Class j } } - private AttributeNodeImplementor getNodeForPut(PersistentAttribute attribute) { + private AttributeNodeImplementor getNodeForPut(PersistentAttribute attribute) { + if ( attributeNodes == null ) { + attributeNodes = new HashMap<>(); + return null; + } + else { + return getNode( attribute ); + } + } + + private AttributeNodeImplementor getNodeForPut(PluralPersistentAttribute attribute) { + if ( attributeNodes == null ) { + attributeNodes = new HashMap<>(); + return null; + } + else { + return getNode( attribute ); + } + } + + private AttributeNodeImplementor, V, K> getNodeForPut(MapPersistentAttribute attribute) { if ( attributeNodes == null ) { attributeNodes = new HashMap<>(); return null; @@ -124,11 +155,11 @@ public void merge(GraphImplementor graph) { public void mergeInternal(GraphImplementor graph) { // skip verifyMutability() graph.getNodes().forEach( this::mergeNode ); - graph.getSubGraphs().values().forEach( this::mergeGraph ); + graph.getTreatedSubgraphs().values().forEach( this::mergeGraph ); } - private void mergeNode(PersistentAttribute attribute, AttributeNodeImplementor node) { - final AttributeNodeImplementor existingNode = getNodeForPut( attribute ); + private void mergeNode(PersistentAttribute attribute, AttributeNodeImplementor node) { + final AttributeNodeImplementor existingNode = getNodeForPut( attribute ); if ( existingNode == null ) { attributeNodes.put( attribute, node.makeCopy( isMutable() ) ); } @@ -150,11 +181,11 @@ private void mergeGraph(SubGraphImplementor subgraph) { } } - private static void mergeNode( - AttributeNodeImplementor node, AttributeNodeImplementor existingNode) { + private static void mergeNode( + AttributeNodeImplementor node, AttributeNodeImplementor existingNode) { if ( existingNode.getAttributeDescriptor() == node.getAttributeDescriptor() ) { @SuppressWarnings("unchecked") // safe, we just checked - final AttributeNodeImplementor castNode = (AttributeNodeImplementor) node; + final AttributeNodeImplementor castNode = (AttributeNodeImplementor) node; existingNode.merge( castNode ); } else { @@ -163,19 +194,19 @@ private static void mergeNode( } @Override - public List> getAttributeNodeList() { + public List> getAttributeNodeList() { return attributeNodes == null ? emptyList() : new ArrayList<>( attributeNodes.values() ); } @Override - public AttributeNodeImplementor findAttributeNode(String attributeName) { + public AttributeNodeImplementor findAttributeNode(String attributeName) { final PersistentAttribute attribute = findAttributeInSupertypes( attributeName ); @SuppressWarnings("unchecked") // The JPA API is unsafe by nature final PersistentAttribute persistentAttribute = (PersistentAttribute) attribute; - final AttributeNodeImplementor node = attribute == null ? null : findAttributeNode( persistentAttribute ); + final AttributeNodeImplementor node = attribute == null ? null : findAttributeNode( persistentAttribute ); if ( node == null && treatedSubgraphs != null ) { for ( SubGraphImplementor subgraph : treatedSubgraphs.values() ) { - final AttributeNodeImplementor subgraphNode = subgraph.findAttributeNode( attributeName ); + final AttributeNodeImplementor subgraphNode = subgraph.findAttributeNode( attributeName ); if ( subgraphNode != null ) { return subgraphNode; } @@ -188,32 +219,42 @@ public AttributeNodeImplementor findAttributeNode(String attributeName) } @Override - public AttributeNodeImplementor findAttributeNode(PersistentAttribute attribute) { + public AttributeNodeImplementor getAttributeNode(String attributeName) { + return findAttributeNode( attributeName ); + } + + @Override + public AttributeNodeImplementor findAttributeNode(PersistentAttribute attribute) { return getNode( attribute ); } + @Override + public AttributeNodeImplementor getAttributeNode(Attribute attribute) { + return getNode( (PersistentAttribute) attribute ); + } + @Override public List> getAttributeNodes() { return attributeNodes == null ? emptyList() : new ArrayList<>( attributeNodes.values() ); } @Override - public Map, AttributeNodeImplementor> getNodes() { + public Map, AttributeNodeImplementor> getNodes() { return attributeNodes == null ? emptyMap() : unmodifiableMap( attributeNodes ); } @Override - public AttributeNodeImplementor addAttributeNode(String attributeName) { + public AttributeNodeImplementor addAttributeNode(String attributeName) { return findOrCreateAttributeNode( attributeName ); } @Override - public AttributeNodeImplementor addAttributeNode(PersistentAttribute attribute) { + public AttributeNodeImplementor addAttributeNode(PersistentAttribute attribute) { return findOrCreateAttributeNode( attribute ); } @Override - public AttributeNodeImplementor addAttributeNode(Attribute attribute) { + public AttributeNodeImplementor addAttributeNode(Attribute attribute) { return addAttributeNode( (PersistentAttribute) attribute ); } @@ -250,11 +291,37 @@ public void removeAttributeNodes(Attribute.PersistentAttributeType nodeType) { } @Override - public AttributeNodeImplementor findOrCreateAttributeNode(PersistentAttribute attribute) { + public AttributeNodeImplementor findOrCreateAttributeNode(PersistentAttribute attribute) { + verifyMutability(); + final AttributeNodeImplementor node = getNodeForPut( attribute ); + if ( node == null ) { + final AttributeNodeImplementor newAttrNode = AttributeNodeImpl.create( attribute, isMutable() ); + attributeNodes.put( attribute, newAttrNode ); + return newAttrNode; + } + else { + return node; + } + } + + private AttributeNodeImplementor findOrCreateAttributeNode(PluralPersistentAttribute attribute) { + verifyMutability(); + final AttributeNodeImplementor node = getNodeForPut( attribute ); + if ( node == null ) { + final AttributeNodeImplementor newAttrNode = AttributeNodeImpl.create( attribute, isMutable() ); + attributeNodes.put( attribute, newAttrNode ); + return newAttrNode; + } + else { + return node; + } + } + + private AttributeNodeImplementor,V,K> findOrCreateAttributeNode(MapPersistentAttribute attribute) { verifyMutability(); - final AttributeNodeImplementor node = getNodeForPut( attribute ); + final AttributeNodeImplementor,V,K> node = getNodeForPut( attribute ); if ( node == null ) { - final AttributeNodeImplementor newAttrNode = AttributeNodeImpl.create( attribute, isMutable() ); + final AttributeNodeImplementor,V,K> newAttrNode = AttributeNodeImpl.create( attribute, isMutable() ); attributeNodes.put( attribute, newAttrNode ); return newAttrNode; } @@ -264,7 +331,7 @@ public AttributeNodeImplementor findOrCreateAttributeNode(PersistentAtt } @Override - public AttributeNodeImplementor findOrCreateAttributeNode(String attributeName) { + public AttributeNodeImplementor findOrCreateAttributeNode(String attributeName) { final PersistentAttribute attribute = getAttribute( attributeName ); @SuppressWarnings("unchecked") // The JPA API is unsafe by nature final PersistentAttribute persistentAttribute = (PersistentAttribute) attribute; @@ -285,96 +352,112 @@ public AttributeNodeImplementor findOrCreateAttributeNode(String attrib : attribute; } + @Override @SuppressWarnings("unchecked") // The JPA API is unsafe by nature + public SubGraphImplementor addSubgraph(String attributeName) { + return (SubGraphImplementor) findOrCreateAttributeNode( attributeName ).addValueSubgraph(); + } + + @Override + public SubGraphImplementor addSubgraph(String attributeName, Class type) { + return addSubgraph( attributeName ).addTreatedSubgraph( type ); + } + + @Override + public SubGraphImplementor addSubgraph(Attribute attribute) { + return findOrCreateAttributeNode( (PersistentAttribute) attribute ).addSingularSubgraph(); + } + @Override - @SuppressWarnings("unchecked") // The API is unsafe by nature public SubGraphImplementor addSubGraph(String attributeName) { - return (SubGraphImplementor) findOrCreateAttributeNode( attributeName ).makeSubGraph(); + return addSubgraph( attributeName ); } @Override public SubGraphImplementor addSubGraph(String attributeName, Class subtype) { - return findOrCreateAttributeNode( attributeName ).makeSubGraph( subtype ); + return addSubGraph( attributeName ).addTreatedSubgraph( subtype ); } @Override public SubGraphImplementor addSubGraph(PersistentAttribute attribute) { - return findOrCreateAttributeNode( attribute ).makeSubGraph( attribute.getJavaType() ); + return addSubgraph( attribute ); } @Override public SubGraphImplementor addSubGraph(PersistentAttribute attribute, Class subtype) { - return findOrCreateAttributeNode( attribute ).makeSubGraph( subtype ); + return addTreatedSubgraph( attribute, subtype ); } @Override - public SubGraphImplementor addTreatedSubgraph(PersistentAttribute attribute, ManagedType subtype) { - return findOrCreateAttributeNode( attribute ).makeSubGraph( subtype ); + public SubGraphImplementor addTreatedSubgraph(Attribute attribute, ManagedType type) { + return addSubgraph( attribute ).addTreatedSubgraph( type ); + } + + @Override @SuppressWarnings("unchecked") // The JPA API is unsafe by nature + public SubGraphImplementor addElementSubgraph(String attributeName) { + return (SubGraphImplementor) findOrCreateAttributeNode( attributeName ).addElementSubgraph(); } @Override - public SubGraphImplementor addElementSubGraph(PluralPersistentAttribute attribute, Class type) { - return findOrCreateAttributeNode( attribute ).makeSubGraph( type ); + public SubGraphImplementor addElementSubgraph(String attributeName, Class type) { + return addElementSubgraph( attributeName ).addTreatedSubgraph( type ); } @Override - public SubGraphImplementor addTreatedElementSubgraph(PluralPersistentAttribute attribute, ManagedType type) { - return findOrCreateAttributeNode( attribute ).makeSubGraph( type ); + public SubGraphImplementor addElementSubgraph(PluralAttribute attribute) { + return findOrCreateAttributeNode( (PluralPersistentAttribute) attribute ).addElementSubgraph(); } @Override - public SubGraphImplementor addKeySubGraph(MapPersistentAttribute attribute, Class subtype) { - return findOrCreateAttributeNode( attribute ).makeKeySubGraph( subtype ); + public SubGraphImplementor addTreatedElementSubgraph(PluralAttribute attribute, Class type) { + return addElementSubgraph( attribute ).addTreatedSubgraph( type ); } @Override - public SubGraphImplementor addTreatedMapKeySubgraph(MapPersistentAttribute attribute, ManagedType subtype) { - return findOrCreateAttributeNode( attribute ).makeKeySubGraph( subtype ); + public SubGraph addTreatedElementSubgraph(PluralAttribute attribute, ManagedType type) { + return addElementSubgraph( attribute ).addTreatedSubgraph( type ); } @Override @SuppressWarnings("unchecked") // The API is unsafe by nature - public SubGraphImplementor addKeySubGraph(String attributeName) { - return (SubGraphImplementor) findOrCreateAttributeNode( attributeName ).makeKeySubGraph(); + public SubGraphImplementor addKeySubgraph(String attributeName) { + return (SubGraphImplementor) findOrCreateAttributeNode( attributeName ).addKeySubgraph(); } @Override - public SubGraphImplementor addKeySubGraph(String attributeName, Class subtype) { - return findOrCreateAttributeNode( attributeName ).makeKeySubGraph( subtype ); + public SubGraphImplementor addKeySubgraph(String attributeName, Class type) { + return addKeySubgraph( attributeName ).addTreatedSubgraph( type ); } @Override - public SubGraphImplementor addMapKeySubgraph(MapAttribute attribute) { - return findOrCreateAttributeNode( attribute.getName() ).makeKeySubGraph( attribute.getKeyJavaType() ); + public SubGraphImplementor addKeySubGraph(String attributeName) { + return addKeySubgraph( attributeName ); } @Override - public SubGraphImplementor addTreatedMapKeySubgraph( - MapAttribute attribute, - Class type) { - return addMapKeySubgraph( attribute ).addTreatedSubgraph( type ); + public SubGraphImplementor addKeySubGraph(String attributeName, Class subtype) { + return addKeySubGraph( attributeName ).addTreatedSubgraph( subtype ); } - @Override @SuppressWarnings("unchecked") // The JPA API is unsafe by nature - public SubGraphImplementor addElementSubgraph(String attributeName) { - return (SubGraphImplementor) findOrCreateAttributeNode( attributeName ).makeSubGraph(); + @Override + public SubGraphImplementor addMapKeySubgraph(MapAttribute attribute) { + return findOrCreateAttributeNode( (MapPersistentAttribute) attribute ).addKeySubgraph(); } @Override - public SubGraphImplementor addElementSubgraph(PluralAttribute attribute) { - return findOrCreateAttributeNode( attribute.getName() ) - .makeSubGraph( asManagedType( attribute.getElementType() ) ); + public SubGraphImplementor addKeySubGraph(MapPersistentAttribute attribute, Class subtype) { + return addTreatedMapKeySubgraph( attribute, subtype ); } @Override - public SubGraphImplementor addElementSubgraph(String attributeName, Class type) { - return findOrCreateAttributeNode( attributeName ).makeSubGraph( type ); + public SubGraphImplementor addTreatedMapKeySubgraph(MapAttribute attribute, ManagedType type) { + return addMapKeySubgraph( attribute ).addTreatedSubgraph( type ); } @Override - public SubGraphImplementor addTreatedElementSubgraph( - PluralAttribute attribute, - Class type) { - return addElementSubgraph( attribute ).addTreatedSubgraph( type ); + public SubGraphImplementor addTreatedMapKeySubgraph( + MapAttribute attribute, + Class type) { + return addMapKeySubgraph( attribute ).addTreatedSubgraph( type ); } @Override @@ -409,7 +492,7 @@ public SubGraphImplementor addTreatedSubgraph(Class type) { } @Override - public Map, SubGraphImplementor> getSubGraphs() { + public Map, SubGraphImplementor> getTreatedSubgraphs() { return treatedSubgraphs == null ? emptyMap() : unmodifiableMap( treatedSubgraphs ); } @@ -423,6 +506,16 @@ static ManagedDomainType asManagedType(Type domainType) { } } + private ManagedDomainType asManagedType(DomainType domainType) { + if ( domainType instanceof ManagedDomainType managedDomainType ) { + return managedDomainType; + } + else { + throw new CannotContainSubGraphException( "Attribute is of type '" + domainType.getTypeName() + + "' which is not a managed type" ); + } + } + @Override public String toString() { final StringBuilder builder = new StringBuilder( "Graph[" ).append( managedType.getTypeName() ); diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/RootGraphImpl.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/RootGraphImpl.java index 2e8965910bfb..fb34bc78db8f 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/RootGraphImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/RootGraphImpl.java @@ -16,7 +16,7 @@ * * @author Steve Ebersole */ -public class RootGraphImpl extends AbstractGraph implements RootGraphImplementor { +public class RootGraphImpl extends GraphImpl implements RootGraphImplementor { private final String name; diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/SubGraphImpl.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/SubGraphImpl.java index 339ecbe359de..82170ad1f0e3 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/SubGraphImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/SubGraphImpl.java @@ -12,13 +12,13 @@ * * @author Steve Ebersole */ -public class SubGraphImpl extends AbstractGraph implements SubGraphImplementor { +public class SubGraphImpl extends GraphImpl implements SubGraphImplementor { public SubGraphImpl(ManagedDomainType managedType, boolean mutable) { super( managedType, mutable ); } - public SubGraphImpl(AbstractGraph original, boolean mutable) { + public SubGraphImpl(GraphImpl original, boolean mutable) { super( original, mutable ); } 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 5697f5bd3d78..e3873f9a048a 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 @@ -167,7 +167,7 @@ public SubGraphImplementor visitSubGraph(GraphLanguageParser.SubGraphContext ctx ); } - 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/PathQualifierType.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/PathQualifierType.java index 5d2f7e12ad5b..b1b6a1dfbd96 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 @@ -15,23 +15,23 @@ public enum PathQualifierType { KEY( (attributeNode, subtypeName, sessionFactory) -> subtypeName == null - ? attributeNode.makeKeySubGraph() - : attributeNode.makeKeySubGraph( getSubtype( subtypeName, sessionFactory ) ) + ? attributeNode.addKeySubgraph() + : attributeNode.addKeySubgraph().addTreatedSubgraph( managedType( subtypeName, sessionFactory ) ) ), VALUE( (attributeNode, subtypeName, sessionFactory) -> subtypeName == null - ? attributeNode.makeSubGraph() - : attributeNode.makeSubGraph( getSubtype( subtypeName, sessionFactory ) ) + ? attributeNode.addValueSubgraph() + : attributeNode.addValueSubgraph().addTreatedSubgraph( managedType( subtypeName, sessionFactory ) ) ); - private static ManagedDomainType getSubtype(String subtypeName, SessionFactoryImplementor sessionFactory) { + private static ManagedDomainType managedType(String subtypeName, SessionFactoryImplementor sessionFactory) { final JpaMetamodel metamodel = sessionFactory.getJpaMetamodel(); - ManagedDomainType managedType = metamodel.findManagedType( subtypeName ); + ManagedDomainType managedType = metamodel.findManagedType( subtypeName ); if ( managedType == null ) { managedType = metamodel.getHqlEntityReference( subtypeName ); } if ( managedType == null ) { - throw new IllegalArgumentException( "Unknown type " + subtypeName ); + throw new IllegalArgumentException( "Unknown managed type: " + subtypeName ); } return managedType; } diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/SubGraphGenerator.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/SubGraphGenerator.java index 094858dcfa88..f19aa465e64d 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/SubGraphGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/SubGraphGenerator.java @@ -14,7 +14,7 @@ @FunctionalInterface public interface SubGraphGenerator { SubGraphImplementor createSubGraph( - AttributeNodeImplementor attributeNode, + AttributeNodeImplementor attributeNode, String subTypeName, SessionFactoryImplementor sessionFactory); } diff --git a/hibernate-core/src/main/java/org/hibernate/graph/spi/AttributeNodeImplementor.java b/hibernate-core/src/main/java/org/hibernate/graph/spi/AttributeNodeImplementor.java index 9a1a0966797a..9f6961226283 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/spi/AttributeNodeImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/spi/AttributeNodeImplementor.java @@ -4,46 +4,66 @@ */ package org.hibernate.graph.spi; -import jakarta.persistence.metamodel.ManagedType; import org.hibernate.graph.AttributeNode; import java.util.Map; /** - * Integration version of the {@link AttributeNode} contract + * Integration version of the {@link AttributeNode} contract. + * + * @param The type of the attribute + * @param The element type, if this node represents a + * {@linkplain jakarta.persistence.metamodel.PluralAttribute plural attribute} + * @param The map key type, if this node represents a + * {@linkplain jakarta.persistence.metamodel.MapAttribute map attribute} * * @author Strong Liu * @author Steve Ebersole + * @author Gavin King */ -public interface AttributeNodeImplementor extends AttributeNode, GraphNodeImplementor { - - @Override - AttributeNodeImplementor makeCopy(boolean mutable); +public interface AttributeNodeImplementor extends AttributeNode, GraphNodeImplementor { @Override + AttributeNodeImplementor makeCopy(boolean mutable); + + /** + * Create a value subgraph, without knowing whether it represents a singular value or + * plural element, rooted at this attribute node. + * + * @apiNote This version is more lenient and is therefore disfavored. Prefer the use + * of {@link #addSingularSubgraph()} and {@link #addElementSubgraph()}. + */ + SubGraphImplementor addValueSubgraph(); + + /** + * Create a value subgraph representing a singular value rooted at this attribute node. + */ + SubGraphImplementor addSingularSubgraph(); + + /** + * Create a value subgraph representing a plural element rooted at this attribute node. + */ + SubGraphImplementor addElementSubgraph(); + + /** + * Create a key subgraph rooted at this attribute node. + */ + SubGraphImplementor addKeySubgraph(); + + @Override @Deprecated SubGraphImplementor makeSubGraph(); - @Override + @Override @Deprecated SubGraphImplementor makeKeySubGraph(); - @Override + @Override @Deprecated SubGraphImplementor makeSubGraph(Class subtype); - @Override + @Override @Deprecated SubGraphImplementor makeKeySubGraph(Class subtype); - @Override - SubGraphImplementor makeSubGraph(ManagedType subtype); - - @Override - SubGraphImplementor makeKeySubGraph(ManagedType subtype); - - void merge(AttributeNodeImplementor other); - - SubGraphImplementor getSubGraph(); - - SubGraphImplementor getKeySubGraph(); + void merge(AttributeNodeImplementor other); @Override Map, SubGraphImplementor> getSubGraphs(); diff --git a/hibernate-core/src/main/java/org/hibernate/graph/spi/GraphImplementor.java b/hibernate-core/src/main/java/org/hibernate/graph/spi/GraphImplementor.java index b1d6be723bb0..95e1a2cc92a1 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/spi/GraphImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/spi/GraphImplementor.java @@ -11,22 +11,20 @@ import jakarta.persistence.metamodel.ManagedType; import jakarta.persistence.metamodel.MapAttribute; import jakarta.persistence.metamodel.PluralAttribute; -import org.hibernate.Incubating; import org.hibernate.Internal; -import org.hibernate.graph.AttributeNode; import org.hibernate.graph.Graph; import org.hibernate.graph.SubGraph; import org.hibernate.metamodel.model.domain.MapPersistentAttribute; import org.hibernate.metamodel.model.domain.PersistentAttribute; -import org.hibernate.metamodel.model.domain.PluralPersistentAttribute; /** - * Integration version of the {@link Graph} contract + * Integration version of the {@link Graph} contract. * * @author Strong Liu * @author Steve Ebersole * @author Andrea Boriero + * @author Gavin King */ public interface GraphImplementor extends Graph, GraphNodeImplementor { @@ -47,124 +45,91 @@ public interface GraphImplementor extends Graph, GraphNodeImplementor { GraphImplementor makeCopy(boolean mutable); @Override - List> getAttributeNodeList(); + List> getAttributeNodeList(); - Map, AttributeNodeImplementor> getNodes(); + Map, AttributeNodeImplementor> getNodes(); - @Override - AttributeNodeImplementor findAttributeNode(String attributeName); + Map, SubGraphImplementor> getTreatedSubgraphs(); @Override - AttributeNodeImplementor findAttributeNode(PersistentAttribute attribute); - - AttributeNodeImplementor findOrCreateAttributeNode(String name); - - AttributeNodeImplementor findOrCreateAttributeNode(PersistentAttribute attribute); - - AttributeNodeImplementor addAttributeNode(PersistentAttribute attribute); + AttributeNodeImplementor getAttributeNode(String attributeName); @Override - SubGraphImplementor addSubGraph(String attributeName); + AttributeNodeImplementor getAttributeNode(Attribute attribute); @Override - SubGraphImplementor addSubGraph(String attributeName, Class subType); + AttributeNodeImplementor findAttributeNode(String attributeName); @Override - SubGraphImplementor addSubGraph(PersistentAttribute attribute); + AttributeNodeImplementor findAttributeNode(PersistentAttribute attribute); - @Override - SubGraphImplementor addSubGraph(PersistentAttribute attribute, Class subtype); + AttributeNodeImplementor findOrCreateAttributeNode(String name); - SubGraphImplementor addTreatedSubgraph(PersistentAttribute attribute, ManagedType subtype); + AttributeNodeImplementor findOrCreateAttributeNode(PersistentAttribute attribute); - @Incubating - SubGraphImplementor addElementSubGraph(PluralPersistentAttribute attribute, Class type); - - @Incubating - SubGraphImplementor addTreatedElementSubgraph(PluralPersistentAttribute attribute, ManagedType type); + AttributeNodeImplementor addAttributeNode(PersistentAttribute attribute); @Override - SubGraphImplementor addKeySubGraph(String attributeName); + AttributeNodeImplementor addAttributeNode(Attribute attribute); @Override - SubGraphImplementor addKeySubGraph(String attributeName, Class subtype); + SubGraphImplementor addTreatedSubgraph(Class type); + + SubGraphImplementor addTreatedSubgraph(ManagedType type); @Override - SubGraphImplementor addKeySubGraph(MapPersistentAttribute attribute, Class subtype); + SubGraphImplementor addSubgraph(String attributeName); - SubGraphImplementor addTreatedMapKeySubgraph(MapPersistentAttribute attribute, ManagedType subtype); + @Override + SubGraphImplementor addSubGraph(String attributeName); @Override - SubGraphImplementor addTreatedSubgraph(Class type); + SubGraphImplementor addSubGraph(String attributeName, Class subType); - SubGraphImplementor addTreatedSubgraph(ManagedType type); + @Override + SubGraphImplementor addSubGraph(PersistentAttribute attribute); - Map, SubGraphImplementor> getSubGraphs(); + @Override + SubGraphImplementor addSubGraph(PersistentAttribute attribute, Class subtype); @Override - default AttributeNode getAttributeNode(String attributeName) { - return findAttributeNode( attributeName ); - } + SubGraphImplementor addKeySubGraph(String attributeName); @Override - default AttributeNode getAttributeNode(Attribute attribute) { - return findAttributeNode( (PersistentAttribute) attribute ); - } + SubGraphImplementor addKeySubGraph(String attributeName, Class subtype); @Override - default AttributeNode addAttributeNode(Attribute attribute) { - return addAttributeNode( (PersistentAttribute) attribute ); - } + SubGraphImplementor addKeySubGraph(MapPersistentAttribute attribute, Class subtype); @Override - default SubGraphImplementor addSubgraph(String attributeName, Class type) { - return addSubGraph( attributeName ).addTreatedSubgraph( type ); - } + SubGraphImplementor addSubgraph(String attributeName, Class type); @Override - default SubGraphImplementor addSubgraph(Attribute attribute) { - return addSubGraph( (PersistentAttribute) attribute ); - } + SubGraphImplementor addSubgraph(Attribute attribute); @Override - default SubGraphImplementor addTreatedSubgraph(Attribute attribute, Class type) { - return addSubGraph( (PersistentAttribute) attribute ).addTreatedSubgraph( type ); - } + SubGraphImplementor addTreatedSubgraph(Attribute attribute, Class type); @Override - default SubGraph addTreatedSubgraph(Attribute attribute, ManagedType type) { - return addSubGraph( (PersistentAttribute) attribute ).addTreatedSubgraph( type ); - } + SubGraphImplementor addTreatedSubgraph(Attribute attribute, ManagedType type); @Override - default SubGraphImplementor addTreatedElementSubgraph(PluralAttribute attribute, Class type) { - return addElementSubGraph( (PluralPersistentAttribute) attribute, type ); - } + SubGraphImplementor addTreatedElementSubgraph(PluralAttribute attribute, Class type); @Override - default SubGraph addTreatedElementSubgraph(PluralAttribute attribute, ManagedType type) { - return addTreatedElementSubgraph( (PluralPersistentAttribute) attribute, type ); - } + SubGraph addTreatedElementSubgraph(PluralAttribute attribute, ManagedType type); @Override - default SubGraphImplementor addKeySubgraph(String attributeName) { - return addKeySubGraph( attributeName ); - } + SubGraphImplementor addKeySubgraph(String attributeName); @Override - default SubGraphImplementor addKeySubgraph(String attributeName, Class type) { - return addKeySubGraph( attributeName ).addTreatedSubgraph( type ); - } + SubGraphImplementor addKeySubgraph(String attributeName, Class type); @Override - default SubGraphImplementor addTreatedMapKeySubgraph(MapAttribute attribute, Class type) { - return addKeySubGraph( (MapPersistentAttribute) attribute, type ); - } + SubGraphImplementor addTreatedMapKeySubgraph(MapAttribute attribute, Class type); @Override - default SubGraph addTreatedMapKeySubgraph(MapAttribute attribute, ManagedType type) { - return addTreatedMapKeySubgraph( (MapPersistentAttribute) attribute, type ); - } + SubGraphImplementor addTreatedMapKeySubgraph(MapAttribute attribute, ManagedType type); @Override default boolean hasAttributeNode(String attributeName) { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/MapPersistentAttribute.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/MapPersistentAttribute.java index 71fa8f5266fe..fc1270d5d8d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/MapPersistentAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/MapPersistentAttribute.java @@ -19,4 +19,7 @@ public interface MapPersistentAttribute extends MapAttribute, Pl @Override SimpleDomainType getKeyType(); + + @Override + SimpleDomainType getKeyGraphType(); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java index 13359f68454e..6da10dc69761 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java @@ -561,8 +561,8 @@ private void applyNamedAttributeNodes( GraphImplementor graphNode) { for ( NamedAttributeNode namedAttributeNode : namedAttributeNodes ) { final String value = namedAttributeNode.value(); - final AttributeNodeImplementor attributeNode = - (AttributeNodeImplementor) graphNode.addAttributeNode( value ); + final AttributeNodeImplementor attributeNode = + (AttributeNodeImplementor) graphNode.addAttributeNode( value ); if ( isNotEmpty( namedAttributeNode.subgraph() ) ) { applyNamedSubgraphs( @@ -583,36 +583,52 @@ private void applyNamedAttributeNodes( } } - private void applyNamedSubgraphs( + private void applyNamedSubgraphs( NamedEntityGraph namedEntityGraph, String subgraphName, - AttributeNodeImplementor attributeNode, + AttributeNodeImplementor attributeNode, boolean isKeySubGraph) { for ( NamedSubgraph namedSubgraph : namedEntityGraph.subgraphs() ) { if ( subgraphName.equals( namedSubgraph.name() ) ) { - final boolean isDefaultSubgraphType = namedSubgraph.type().equals( void.class ); - final Class subGraphType = isDefaultSubgraphType ? null : namedSubgraph.type(); - final SubGraphImplementor subgraph = - makeAttributeNodeSubgraph( attributeNode, isKeySubGraph, subGraphType ); + final Class subgraphType = namedSubgraph.type(); + final SubGraphImplementor subgraph; + if ( subgraphType.equals( void.class ) ) { // unspecified + subgraph = attributeNode.addValueSubgraph(); + } + else { + subgraph = isKeySubGraph + ? makeAttributeNodeKeySubgraph( attributeNode, subgraphType ) + : makeAttributeNodeValueSubgraph( attributeNode, subgraphType ); + } applyNamedAttributeNodes( namedSubgraph.attributeNodes(), namedEntityGraph, subgraph ); } } } - private static SubGraphImplementor makeAttributeNodeSubgraph( - AttributeNodeImplementor attributeNode, - boolean isKeySubGraph, - Class subGraphType) { - if ( isKeySubGraph ) { - return subGraphType != null - ? attributeNode.makeKeySubGraph( subGraphType ) - : attributeNode.makeKeySubGraph(); + private static SubGraphImplementor makeAttributeNodeValueSubgraph( + AttributeNodeImplementor attributeNode, Class subgraphType) { + final Class attributeValueType = + attributeNode.getAttributeDescriptor().getValueGraphType().getBindableJavaType(); + if ( !attributeValueType.isAssignableFrom( subgraphType ) ) { + throw new AnnotationException( "Named subgraph type '" + subgraphType.getName() + + "' is not a subtype of the value type '" + attributeValueType.getName() + "'" ); } - else { - return subGraphType != null - ? attributeNode.makeSubGraph( subGraphType ) - : attributeNode.makeSubGraph(); + @SuppressWarnings("unchecked") // Safe, because we just checked + final Class castType = (Class) subgraphType; + return attributeNode.addValueSubgraph().addTreatedSubgraph( castType ); + } + + private static SubGraphImplementor makeAttributeNodeKeySubgraph( + AttributeNodeImplementor attributeNode, Class subgraphType) { + final Class attributeKeyType = + attributeNode.getAttributeDescriptor().getKeyGraphType().getBindableJavaType(); + if ( !attributeKeyType.isAssignableFrom( subgraphType ) ) { + throw new AnnotationException( "Named subgraph type '" + subgraphType.getName() + + "' is not a subtype of the key type '" + attributeKeyType.getName() + "'" ); } + @SuppressWarnings("unchecked") // Safe, because we just checked + final Class castType = (Class) subgraphType; + return attributeNode.addKeySubgraph().addTreatedSubgraph( castType ); } private Class resolveRequestedClass(String entityName) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AppliedGraphs.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AppliedGraphs.java index e2f454738e5c..222f5b48d305 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AppliedGraphs.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AppliedGraphs.java @@ -26,7 +26,7 @@ public static boolean containsCollectionFetches(QueryOptions queryOptions) { } private static boolean containsCollectionFetches(GraphImplementor graph) { - for ( AttributeNodeImplementor node : graph.getNodes().values() ) { + for ( AttributeNodeImplementor node : graph.getNodes().values() ) { if ( node.getAttributeDescriptor().isCollection() ) { return true; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchInitializer.java index d1499e2453db..e7a622de836b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchInitializer.java @@ -260,9 +260,10 @@ else if ( referencedModelPart.isOptional() && referencedModelPart.isLazy() ) { private boolean isLazyByGraph(RowProcessingState rowProcessingState) { final AppliedGraph appliedGraph = rowProcessingState.getQueryOptions().getAppliedGraph(); if ( appliedGraph != null && appliedGraph.getSemantic() == GraphSemantic.FETCH ) { - final AttributeNodeImplementor attributeNode = appliedGraph.getGraph() - .findAttributeNode( navigablePath.getLocalName() ); - if ( attributeNode != null && attributeNode.getAttributeDescriptor() == getInitializedPart().asAttributeMapping() ) { + final AttributeNodeImplementor attributeNode = + appliedGraph.getGraph().findAttributeNode( navigablePath.getLocalName() ); + if ( attributeNode != null + && attributeNode.getAttributeDescriptor() == getInitializedPart().asAttributeMapping() ) { return false; } return true; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardEntityGraphTraversalStateImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardEntityGraphTraversalStateImpl.java index b4831f05590f..8cc7fcb994e2 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardEntityGraphTraversalStateImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardEntityGraphTraversalStateImpl.java @@ -55,7 +55,7 @@ public TraversalResult traverse(FetchParent fetchParent, Fetchable fetchable, bo } final GraphImplementor previousContextRoot = currentGraphContext; - final AttributeNodeImplementor attributeNode = appliesTo( fetchParent ) + final AttributeNodeImplementor attributeNode = appliesTo( fetchParent ) ? currentGraphContext.findAttributeNode( fetchable.getFetchableName() ) : null; 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 0b32ffea7c31..0477bf5d076a 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 @@ -176,11 +176,11 @@ public void testLinkSubtypeParsing() { 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 +204,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; } diff --git a/migration-guide.adoc b/migration-guide.adoc index dc8f59edfdce..be56d0d6721b 100644 --- a/migration-guide.adoc +++ b/migration-guide.adoc @@ -15,12 +15,12 @@ earlier versions, see any other pertinent migration guides as well. 7.0 migrates to Jakarta Persistence 3.2 which is fairly disruptive, mainly around: -* type parameters +* Type parameters: ** Affects much of the Criteria API - especially roots, joins, paths ** Affects much of the Graph API - - *** org.hibernate.graph.Graph.addAttributeNode(java.lang.String) defines a return while + *** `org.hibernate.graph.Graph.addAttributeNode(java.lang.String)` defines a return while `jakarta.persistence.Graph.addAttributeNode(java.lang.String)` does not. -* new JPA features colliding with previous Hibernate extension features +* New JPA features colliding with previous Hibernate extension features: ** `Nulls` (JPA) v. `NullPrecedence` (Hibernate), including JPA's new `Order#getNullPrecedence()` returning `Nulls` colliding with Hibernate's `SqmSortSpecification#getNullPrecedence` returning `NullPrecedence`. Hibernate's form was renamed to `SqmSortSpecification#getHibernateNullPrecedence` to avoid the collision. @@ -86,18 +86,19 @@ private Employee manager; === Misplaced Annotations 7.0 does much more in-depth checking that annotations appear in the proper place. While previous versions -did not necessarily throw errors, in most cases these annotations were simply ignored. E.g. +did not necessarily throw errors, in most cases these annotations were simply ignored. +For example, this code now results in an error: [source,java] ---- @Entity class Book { - // defines FIELD access-type + // specifies FIELD access, properties should not be annotated @Id Integer id; - // previously ignored, this is an error now + // previously ignored, this is an error now @Column(name="category") String getType() { ... } } @@ -114,7 +115,8 @@ versions did not validate this particularly well. [[java-beans]] === JavaBean Conventions -Previous versions allowed some questionable (at best) attribute naming patterns. These are no longer supported. E.g. +Previous versions allowed some questionable (at best) attribute naming patterns. +For example, this property declaration is no longer allowed: [source,java] ---- @@ -204,16 +206,16 @@ Applications will need to replace usages of the removed `@Proxy` annotation. `@Proxy#proxyClass` has no direct replacement, but was also never needed/useful. -Here we focus on `@Proxy#laxy` attribute which, again, was hardly ever useful. +Here we focus on `@Proxy#lazy` attribute which, again, was hardly ever useful. By default (true), Hibernate would proxy an entity when possible and when asked for. "Asked for" includes calls to `Session#getReference` and lazy associations. All such cases though are already controllable by the application. * Instead of `Session#getReference`, use `Session#find` -* Use eager associations, using -** `FetchType.EAGER` (the default for to-one associations anyway), possibly combined with `@Fetch` -** `EntityGraph` -** `@FetchProfiles` +* Use eager association fetching, for example, +** `FetchType.EAGER` (the default for to-one associations anyway), possibly combined with `@Fetch`, +** `EntityGraph`, or a +** `@FetchProfile`. The effect can also often be mitigated using Hibernate's bytecode-based laziness (possibly combined with `@ConcreteProxy`). @@ -310,8 +312,8 @@ The previous behavior may be recovered by setting `hibernate.query.native.prefer [[ddl-implicit-datatype-timestamp]] == Default precision for `timestamp` on some databases -The default precision for Oracle timestamps was changed to 9 i.e. nanosecond precision. -The default precision for SQL Server timestamps was changed to 7 i.e. 100 nanosecond precision. +The default precision for Oracle timestamps was changed to 9, i.e. nanosecond precision. +The default precision for SQL Server timestamps was changed to 7, i.e. 100 nanosecond precision. Note that these changes only affect DDL generation. @@ -339,12 +341,12 @@ The migration requires to read data and re-save it. To retain backwards compatibility, configure the setting `hibernate.type.preferred_array_jdbc_type` to `VARBINARY`. [[xml-format-mapper-changes]] -== XML FormatMapper changes +== XML `FormatMapper` changes Previous versions of Hibernate ORM used an undefined/provider-specific format for serialization/deserialization of -collections, maps and byte arrays to/from XML, which is not portable. +collections, maps and byte arrays to/from XML, which was not portable. -XML FormatMapper implementations were changed to now use a portable format for collections, maps and byte arrays. +XML `FormatMapper` implementations now use a portable format for collections, maps, and byte arrays. This change is necessary to allow mapping basic arrays as `SqlTypes.XML_ARRAY`. The migration requires to read data and re-save it. @@ -365,7 +367,7 @@ To align with Jakarta Persistence (the 3.2 TCK tests this), Hibernate now consid However, because `hibernate.session_factory_name` is also a trigger to attempt to bind the SessionFactory into JNDI, this change to consider persistence-unit name, means that each `SessionFactory` created through Jakarta Persistence now -have a name and Hibernate attempted to bind these to JNDI. +has a name and Hibernate attempts to bind it to JNDI. To work around this we have introduced a new `hibernate.session_factory_jndi_name` setting that can be used to explicitly specify a name for JNDI binding. The new behavior is as follows (assuming `hibernate.session_factory_name_is_jndi` is not explicitly configured): @@ -461,6 +463,19 @@ Hibernate now reports an error in this situation. This includes auto-applied converters. To suppress the error for an auto-applied converter, use `@Convert(disableConversion=true)`. +== `org.hibernate.graph` package + +The `EntityGraph` API was enhanced in JPA 3.2, and made much more useful. +The incubating package `org.hibernate.graph` contains extensions to that API, which have been significantly impacted by the migration to JPA 3.2, and by the additional of new functionality. +Furthermore, some legacy operations were declared with incorrect generic type signatures (by both JPA, and by Hibernate). + +This package has been significantly re-engineered, and the impact of this effort includes: + +- some breaking changes to type signatures, and +- a number of deprecations of legacy operations which are now covered by JPA. + +We encourage migration to the use of the new JPA-standard operations. + == Deprecations * `@Comment` is deprecated in favor of the JPA 3.2 `comment` members