From 294ec278857e16bb3656168fcb04f92ce6022cd4 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 8 Jul 2023 16:38:33 +0200 Subject: [PATCH] HHH-16913 EntityGraph support for StatelessSession --- .../org/hibernate/SharedSessionContract.java | 45 ++++++++++++++ .../java/org/hibernate/StatelessSession.java | 35 +++++++++++ .../spi/SharedSessionDelegatorBaseImpl.java | 23 +++++++ .../internal/DefaultRefreshEventListener.java | 1 - .../AbstractSharedSessionContract.java | 35 +++++++++++ .../org/hibernate/internal/SessionImpl.java | 32 ---------- .../internal/StatelessSessionImpl.java | 61 ++++++++++++------- 7 files changed, 176 insertions(+), 56 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java index e078149fa2..3b3d6cea85 100644 --- a/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java @@ -8,7 +8,10 @@ package org.hibernate; import java.io.Closeable; import java.io.Serializable; +import java.util.List; +import jakarta.persistence.EntityGraph; +import org.hibernate.graph.RootGraph; import org.hibernate.jdbc.ReturningWork; import org.hibernate.jdbc.Work; import org.hibernate.procedure.ProcedureCall; @@ -240,6 +243,48 @@ public interface SharedSessionContract extends QueryProducer, Closeable, Seriali throw new UnsupportedOperationException(); } + /** + * Create a new mutable {@link EntityGraph} with only a root node. + * + * @since 6.3 + */ + RootGraph createEntityGraph(Class rootType); + + /** + * Create a new mutable copy of the named {@link EntityGraph}, + * or return {@code null} if there is no graph with the given + * name. + * + * @param graphName the name of the graph + * + * @see jakarta.persistence.EntityManagerFactory#addNamedEntityGraph(String, EntityGraph) + * + * @since 6.3 + */ + RootGraph createEntityGraph(String graphName); + + /** + * Retrieve the named {@link EntityGraph} as an immutable graph, + * or return {@code null} if there is no graph with the given + * name. + * + * @see jakarta.persistence.EntityManagerFactory#addNamedEntityGraph(String, EntityGraph) + * + * @param graphName the name of the graph + * + * @since 6.3 + */ + RootGraph getEntityGraph(String graphName); + + /** + * Retrieve all named {@link EntityGraph}s with the given type. + * + * @see jakarta.persistence.EntityManagerFactory#addNamedEntityGraph(String, EntityGraph) + * + * @since 6.3 + */ + List> getEntityGraphs(Class entityClass); + /** * The factory which created this session. */ diff --git a/hibernate-core/src/main/java/org/hibernate/StatelessSession.java b/hibernate-core/src/main/java/org/hibernate/StatelessSession.java index 4da0238476..94a3ab3ea4 100644 --- a/hibernate-core/src/main/java/org/hibernate/StatelessSession.java +++ b/hibernate-core/src/main/java/org/hibernate/StatelessSession.java @@ -6,6 +6,9 @@ */ package org.hibernate; +import jakarta.persistence.EntityGraph; +import org.hibernate.graph.GraphSemantic; + /** * A command-oriented API often used for performing bulk operations against * the database. A stateless session has no persistence context, and always @@ -163,6 +166,38 @@ public interface StatelessSession extends SharedSessionContract { */ T get(Class entityClass, Object id, LockMode lockMode); + /** + * Retrieve a row, fetching associations specified by the + * given {@link EntityGraph}. + * + * @param graph The {@link EntityGraph} + * @param graphSemantic a {@link GraphSemantic} specifying + * how the graph should be interpreted + * @param id The id of the entity to retrieve + * + * @return a detached entity instance + * + * @since 6.3 + */ + T get(EntityGraph graph, GraphSemantic graphSemantic, Object id); + + /** + * Retrieve a row, fetching associations specified by the + * given {@link EntityGraph}, and obtaining the specified + * lock mode. + * + * @param graph The {@link EntityGraph} + * @param graphSemantic a {@link GraphSemantic} specifying + * how the graph should be interpreted + * @param id The id of the entity to retrieve + * @param lockMode The lock mode to apply to the entity + * + * @return a detached entity instance + * + * @since 6.3 + */ + T get(EntityGraph graph, GraphSemantic graphSemantic, Object id, LockMode lockMode); + /** * Refresh the entity instance state from the database. * diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java index 17a444e8d4..4b48bd060c 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java @@ -6,6 +6,7 @@ */ package org.hibernate.engine.spi; +import jakarta.persistence.EntityGraph; import jakarta.persistence.FlushModeType; import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; @@ -22,6 +23,7 @@ import org.hibernate.engine.jdbc.LobCreator; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.jdbc.spi.JdbcCoordinator; import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.graph.RootGraph; import org.hibernate.jdbc.ReturningWork; import org.hibernate.jdbc.Work; import org.hibernate.persister.entity.EntityPersister; @@ -36,6 +38,7 @@ import org.hibernate.query.sql.spi.NativeQueryImplementor; import org.hibernate.resource.jdbc.spi.JdbcSessionContext; import org.hibernate.resource.transaction.spi.TransactionCoordinator; +import java.util.List; import java.util.Set; import java.util.TimeZone; import java.util.UUID; @@ -593,4 +596,24 @@ public class SharedSessionDelegatorBaseImpl implements SharedSessionContractImpl public TimeZone getJdbcTimeZone() { return delegate.getJdbcTimeZone(); } + + @Override + public RootGraph createEntityGraph(Class rootType) { + return delegate.createEntityGraph( rootType ); + } + + @Override + public RootGraph createEntityGraph(String graphName) { + return delegate.createEntityGraph( graphName ); + } + + @Override + public RootGraph getEntityGraph(String graphName) { + return delegate.getEntityGraph( graphName ); + } + + @Override + public List> getEntityGraphs(Class entityClass) { + return delegate.getEntityGraphs( entityClass ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java index 27359fd550..f4a14baa44 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java @@ -32,7 +32,6 @@ import org.hibernate.loader.ast.spi.CascadingFetchProfile; import org.hibernate.metamodel.spi.MappingMetamodelImplementor; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.pretty.MessageHelper; import org.hibernate.type.CollectionType; import org.hibernate.type.CompositeType; import org.hibernate.type.Type; diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index 73a4049f15..8783bf0976 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -17,6 +17,7 @@ import java.util.TimeZone; import java.util.UUID; import java.util.function.Function; +import jakarta.persistence.EntityGraph; import org.hibernate.CacheMode; import org.hibernate.EntityNameResolver; import org.hibernate.FlushMode; @@ -41,6 +42,8 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.transaction.internal.TransactionImpl; import org.hibernate.engine.transaction.spi.TransactionImplementor; +import org.hibernate.graph.internal.RootGraphImpl; +import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.id.uuid.StandardRandomStrategy; import org.hibernate.jdbc.ReturningWork; import org.hibernate.jdbc.Work; @@ -1373,6 +1376,38 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont return query; } + @Override + public RootGraphImplementor createEntityGraph(Class rootType) { + checkOpen(); + return new RootGraphImpl<>( null, getFactory().getJpaMetamodel().entity( rootType ) ); + } + + @Override + public RootGraphImplementor createEntityGraph(String graphName) { + checkOpen(); + final RootGraphImplementor named = getFactory().findEntityGraphByName( graphName ); + if ( named == null ) { + return null; + } + return named.makeRootGraph( graphName, true ); + } + + @Override + public RootGraphImplementor getEntityGraph(String graphName) { + checkOpen(); + final RootGraphImplementor named = getFactory().findEntityGraphByName( graphName ); + if ( named == null ) { + throw new IllegalArgumentException( "Could not locate EntityGraph with given name : " + graphName ); + } + return named; + } + + @Override + public List> getEntityGraphs(Class entityClass) { + checkOpen(); + return getFactory().findEntityGraphsByType( entityClass ); + } + private void writeObject(ObjectOutputStream oos) throws IOException { if ( log.isTraceEnabled() ) { log.trace( "Serializing " + getClass().getSimpleName() + " [" ); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 9fc876a5cf..91eb0fac5e 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -2860,38 +2860,6 @@ public class SessionImpl return getFactory().getJpaMetamodel(); } - @Override - public RootGraphImplementor createEntityGraph(Class rootType) { - checkOpen(); - return new RootGraphImpl<>( null, getFactory().getJpaMetamodel().entity( rootType ) ); - } - - @Override - public RootGraphImplementor createEntityGraph(String graphName) { - checkOpen(); - final RootGraphImplementor named = getEntityManagerFactory().findEntityGraphByName( graphName ); - if ( named == null ) { - return null; - } - return named.makeRootGraph( graphName, true ); - } - - @Override - public RootGraphImplementor getEntityGraph(String graphName) { - checkOpen(); - final RootGraphImplementor named = getEntityManagerFactory().findEntityGraphByName( graphName ); - if ( named == null ) { - throw new IllegalArgumentException( "Could not locate EntityGraph with given name : " + graphName ); - } - return named; - } - - @Override - public List> getEntityGraphs(Class entityClass) { - checkOpen(); - return getEntityManagerFactory().findEntityGraphsByType( entityClass ); - } - /** * Used by JDK serialization... * diff --git a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java index 718471b13a..bc16ca6562 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java @@ -8,6 +8,7 @@ package org.hibernate.internal; import java.util.Set; +import jakarta.persistence.EntityGraph; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.HibernateException; @@ -21,6 +22,7 @@ import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata; import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.internal.StatefulPersistenceContext; +import org.hibernate.engine.spi.EffectiveEntityGraph; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.PersistenceContext; @@ -28,6 +30,9 @@ import org.hibernate.engine.spi.PersistentAttributeInterceptable; import org.hibernate.engine.spi.PersistentAttributeInterceptor; import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper; import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform; +import org.hibernate.graph.GraphSemantic; +import org.hibernate.graph.spi.RootGraphImplementor; +import org.hibernate.loader.ast.spi.CascadingFetchProfile; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.proxy.LazyInitializer; @@ -63,24 +68,15 @@ import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; public class StatelessSessionImpl extends AbstractSharedSessionContract implements StatelessSession { private static final CoreMessageLogger LOG = CoreLogging.messageLogger( StatelessSessionImpl.class ); - private static final LoadQueryInfluencers NO_INFLUENCERS = new LoadQueryInfluencers() { - @Override @Deprecated - public String getInternalFetchProfile() { - return null; - } - - @Override @Deprecated - public void setInternalFetchProfile(String internalFetchProfile) { - } - }; - - private final PersistenceContext temporaryPersistenceContext = new StatefulPersistenceContext( this ); - + private final LoadQueryInfluencers influencers; + private final PersistenceContext temporaryPersistenceContext; private final boolean connectionProvided; public StatelessSessionImpl(SessionFactoryImpl factory, SessionCreationOptions options) { super( factory, options ); connectionProvided = options.getConnection() != null; + temporaryPersistenceContext = new StatefulPersistenceContext( this ); + influencers = new LoadQueryInfluencers( getFactory() ); } @Override @@ -238,6 +234,30 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen return result; } + @Override + public T get(EntityGraph graph, GraphSemantic graphSemantic, Object id) { + return get( graph, graphSemantic, id, LockMode.NONE ); + } + + @Override @SuppressWarnings("unchecked") + public T get( + EntityGraph graph, GraphSemantic graphSemantic, + Object id, LockMode lockMode) { + final RootGraphImplementor rootGraph = (RootGraphImplementor) graph; + checkOpen(); + + final EffectiveEntityGraph effectiveEntityGraph = + getLoadQueryInfluencers().getEffectiveEntityGraph(); + effectiveEntityGraph.applyGraph( rootGraph, graphSemantic ); + + try { + return (T) get( rootGraph.getGraphedType().getTypeName(), id, lockMode ); + } + finally { + effectiveEntityGraph.clear(); + } + } + private EntityPersister getEntityPersister(String entityName) { return getFactory().getMappingMetamodel().getEntityDescriptor( entityName ); } @@ -278,15 +298,10 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen } } - final String previousFetchProfile = getLoadQueryInfluencers().getInternalFetchProfile(); - Object result; - try { - getLoadQueryInfluencers().setInternalFetchProfile( "refresh" ); - result = persister.load( id, entity, getNullSafeLockMode( lockMode ), this ); - } - finally { - getLoadQueryInfluencers().setInternalFetchProfile( previousFetchProfile ); - } + final Object result = getLoadQueryInfluencers().fromInternalFetchProfile( + CascadingFetchProfile.REFRESH, + () -> persister.load( id, entity, getNullSafeLockMode( lockMode ), this ) + ); UnresolvableObjectException.throwIfNull( result, id, persister.getEntityName() ); if ( temporaryPersistenceContext.isLoadFinished() ) { temporaryPersistenceContext.clear(); @@ -619,7 +634,7 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen @Override public LoadQueryInfluencers getLoadQueryInfluencers() { - return NO_INFLUENCERS; + return influencers; } @Override