diff --git a/hibernate-core/src/main/java/org/hibernate/Session.java b/hibernate-core/src/main/java/org/hibernate/Session.java index 5836ff1c458a..1aaa7abe3f3c 100644 --- a/hibernate-core/src/main/java/org/hibernate/Session.java +++ b/hibernate-core/src/main/java/org/hibernate/Session.java @@ -4,10 +4,12 @@ */ package org.hibernate; +import java.util.Collection; import java.util.List; import java.util.function.Consumer; import jakarta.persistence.FindOption; +import jakarta.persistence.metamodel.EntityType; import org.hibernate.graph.RootGraph; import org.hibernate.jdbc.Work; import org.hibernate.query.Query; @@ -1267,6 +1269,44 @@ public interface Session extends SharedSessionContract, EntityManager { */ LobHelper getLobHelper(); + /** + * Obtain the collection of all managed entities which belong to this + * persistence context. + * + * @since 7.0 + */ + @Incubating + Collection getManagedEntities(); + + /** + * Obtain a collection of all managed instances of the entity type with the + * given entity name which belong to this persistence context. + * + * @since 7.0 + */ + @Incubating + Collection getManagedEntities(String entityName); + + /** + * Obtain a collection of all managed entities of the given type which belong + * to this persistence context. This operation is not polymorphic, and does + * not return instances of subtypes of the given entity type. + * + * @since 7.0 + */ + @Incubating + Collection getManagedEntities(Class entityType); + + /** + * Obtain a collection of all managed entities of the given type which belong + * to this persistence context. This operation is not polymorphic, and does + * not return instances of subtypes of the given entity type. + * + * @since 7.0 + */ + @Incubating + Collection getManagedEntities(EntityType entityType); + /** * Obtain a {@link Session} builder with the ability to copy certain * information from this session. diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index b2af60fd08a8..06049e9a912b 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -72,10 +72,12 @@ import org.checkerframework.checker.nullness.qual.Nullable; +import static java.util.Collections.emptyMap; import static org.hibernate.engine.internal.ManagedTypeHelper.asHibernateProxy; import static org.hibernate.engine.internal.ManagedTypeHelper.asManagedEntity; import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable; +import static org.hibernate.internal.util.collections.CollectionHelper.mapOfSize; import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; /** @@ -183,7 +185,7 @@ the following fields are used in all circumstances, and are not worth (or not su private Map getOrInitializeEntitiesByKey() { if ( entitiesByKey == null ) { - entitiesByKey = CollectionHelper.mapOfSize( INIT_COLL_SIZE ); + entitiesByKey = mapOfSize( INIT_COLL_SIZE ); } return entitiesByKey; } @@ -484,17 +486,10 @@ private void processLoadedEntityHolder( postLoadEvent.setEntity( holder.getEntity() ) .setId( holder.getEntityKey().getIdentifier() ) .setPersister( holder.getDescriptor() ); - listenerGroup.fireEventOnEachListener( - postLoadEvent, - PostLoadEventListener::onPostLoad - ); + listenerGroup.fireEventOnEachListener( postLoadEvent, PostLoadEventListener::onPostLoad ); } if ( callback != null ) { - callback.invokeAfterLoadActions( - holder.getEntity(), - holder.getDescriptor(), - getSession() - ); + callback.invokeAfterLoadActions( holder.getEntity(), holder.getDescriptor(), getSession() ); } holder.resetEntityInitialier(); } @@ -599,7 +594,7 @@ public Object getEntity(EntityUniqueKey euk) { @Override public void addEntity(EntityUniqueKey euk, Object entity) { if ( entitiesByUniqueKey == null ) { - entitiesByUniqueKey = CollectionHelper.mapOfSize( INIT_COLL_SIZE ); + entitiesByUniqueKey = mapOfSize( INIT_COLL_SIZE ); } entitiesByUniqueKey.put( euk, entity ); } @@ -733,7 +728,8 @@ public EntityEntry addReferenceEntry( @Override public boolean containsCollection(PersistentCollection collection) { - return collectionEntries != null && collectionEntries.containsKey( collection.$$_hibernate_getInstanceId(), collection ); + return collectionEntries != null + && collectionEntries.containsKey( collection.$$_hibernate_getInstanceId(), collection ); } @Override @@ -818,9 +814,8 @@ public Object unproxy(Object maybeProxy) throws HibernateException { final LazyInitializer lazyInitializer = extractLazyInitializer( maybeProxy ); if ( lazyInitializer != null ) { if ( lazyInitializer.isUninitialized() ) { - throw new PersistentObjectException( - "object was an uninitialized proxy for " + lazyInitializer.getEntityName() - ); + throw new PersistentObjectException( "object was an uninitialized proxy for " + + lazyInitializer.getEntityName() ); } //unwrap the object and return return lazyInitializer.getImplementation(); @@ -839,10 +834,11 @@ public Object unproxyAndReassociate(final Object maybeProxy) throws HibernateExc return lazyInitializer.getImplementation(); } else if ( isPersistentAttributeInterceptable( maybeProxy ) ) { - final PersistentAttributeInterceptable interceptable = asPersistentAttributeInterceptable( maybeProxy ); - final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor(); - if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) { - ( (EnhancementAsProxyLazinessInterceptor) interceptor ).forceInitialize( maybeProxy, null ); + final PersistentAttributeInterceptor interceptor = + asPersistentAttributeInterceptable( maybeProxy ) + .$$_hibernate_getInterceptor(); + if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor lazinessInterceptor ) { + lazinessInterceptor.forceInitialize( maybeProxy, null ); } return maybeProxy; } @@ -893,7 +889,8 @@ public Object narrowProxy(Object proxy, EntityPersister persister, EntityKey key // Otherwise, create the narrowed proxy - final HibernateProxy narrowedProxy = asHibernateProxy( persister.createProxy( key.getIdentifier(), session ) ); + final HibernateProxy narrowedProxy = + asHibernateProxy( persister.createProxy( key.getIdentifier(), session ) ); // set the read-only/modifiable mode in the new proxy to what it was in the original proxy narrowedProxy.getHibernateLazyInitializer().setReadOnly( lazyInitializer.isReadOnly() ); return narrowedProxy; @@ -1309,9 +1306,9 @@ public Object removeProxy(EntityKey key) { @Override public Map getEntitiesByKey() { if ( entitiesByKey == null ) { - return Collections.emptyMap(); + return emptyMap(); } - final HashMap result = CollectionHelper.mapOfSize( entitiesByKey.size() ); + final HashMap result = mapOfSize( entitiesByKey.size() ); for ( Entry entry : entitiesByKey.entrySet() ) { if ( entry.getValue().entity != null ) { result.put( entry.getKey(), entry.getValue().entity ); @@ -1330,7 +1327,7 @@ public Map getEntitySnapshotsByKey() { @Override public Map getOrInitializeEntitySnapshotsByKey() { if ( entitySnapshotsByKey == null ) { - entitySnapshotsByKey = CollectionHelper.mapOfSize( INIT_COLL_SIZE ); + entitySnapshotsByKey = mapOfSize( INIT_COLL_SIZE ); } return entitySnapshotsByKey; } @@ -1384,11 +1381,6 @@ public int getNumberOfManagedEntities() { return entityEntryContext.getNumberOfManagedEntities(); } -// @Override -// public Map getEntityEntries() { -// return null; -// } - /** * @deprecated We should not expose this directly: the other accessors that have been created as a replacement * have better chances of skipping initializing this map, which is a good performance improvement. @@ -1420,7 +1412,7 @@ public void forEachCollectionEntry(BiConsumer, Collectio @Override public Map> getCollectionsByKey() { - return collectionsByKey == null ? Collections.emptyMap() : collectionsByKey; + return collectionsByKey == null ? emptyMap() : collectionsByKey; } @Override @@ -1997,7 +1989,7 @@ public static StatefulPersistenceContext deserialize( LOG.trace( "Starting deserialization of [" + count + "] entitiesByUniqueKey entries" ); } if ( count != 0 ) { - rtn.entitiesByUniqueKey = CollectionHelper.mapOfSize(Math.max(count, INIT_COLL_SIZE)); + rtn.entitiesByUniqueKey = mapOfSize(Math.max(count, INIT_COLL_SIZE)); for ( int i = 0; i < count; i++ ) { rtn.entitiesByUniqueKey.put( EntityUniqueKey.deserialize( ois, session ), ois.readObject() ); } @@ -2007,7 +1999,7 @@ public static StatefulPersistenceContext deserialize( if ( traceEnabled ) { LOG.trace( "Starting deserialization of [" + count + "] entitySnapshotsByKey entries" ); } - rtn.entitySnapshotsByKey = CollectionHelper.mapOfSize(Math.max(count, INIT_COLL_SIZE)); + rtn.entitySnapshotsByKey = mapOfSize(Math.max(count, INIT_COLL_SIZE)); for ( int i = 0; i < count; i++ ) { rtn.entitySnapshotsByKey.put( EntityKey.deserialize( ois, sfi ), ois.readObject() ); } @@ -2018,7 +2010,7 @@ public static StatefulPersistenceContext deserialize( if ( traceEnabled ) { LOG.trace( "Starting deserialization of [" + count + "] entitiesByKey entries" ); } - rtn.entitiesByKey = CollectionHelper.mapOfSize(Math.max(count, INIT_COLL_SIZE)); + rtn.entitiesByKey = mapOfSize(Math.max(count, INIT_COLL_SIZE)); for ( int i = 0; i < count; i++ ) { final EntityKey ek = EntityKey.deserialize( ois, sfi ); final EntityPersister persister = sfi.getMappingMetamodel().getEntityDescriptor( (String) ois.readObject() ); @@ -2048,7 +2040,7 @@ public static StatefulPersistenceContext deserialize( if ( traceEnabled ) { LOG.trace( "Starting deserialization of [" + count + "] collectionsByKey entries" ); } - rtn.collectionsByKey = CollectionHelper.mapOfSize(Math.max(count, INIT_COLL_SIZE)); + rtn.collectionsByKey = mapOfSize(Math.max(count, INIT_COLL_SIZE)); for ( int i = 0; i < count; i++ ) { rtn.collectionsByKey.put( CollectionKey.deserialize( ois, session ), @@ -2230,7 +2222,7 @@ public void clearCollectionsByKey() { @Override public PersistentCollection addCollectionByKey(CollectionKey collectionKey, PersistentCollection persistentCollection) { if ( collectionsByKey == null ) { - collectionsByKey = CollectionHelper.mapOfSize( INIT_COLL_SIZE ); + collectionsByKey = mapOfSize( INIT_COLL_SIZE ); } return collectionsByKey.put( collectionKey, persistentCollection ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java index 439f5eef7ded..db83ab70338c 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java @@ -4,12 +4,14 @@ */ package org.hibernate.engine.spi; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TimeZone; import java.util.UUID; +import jakarta.persistence.metamodel.EntityType; import org.hibernate.CacheMode; import org.hibernate.Filter; import org.hibernate.FlushMode; @@ -1115,6 +1117,26 @@ public LobHelper getLobHelper() { return delegate.getLobHelper(); } + @Override + public Collection getManagedEntities() { + return delegate.getManagedEntities(); + } + + @Override + public Collection getManagedEntities(String entityName) { + return delegate.getManagedEntities( entityName ); + } + + @Override + public Collection getManagedEntities(Class entityType) { + return delegate.getManagedEntities( entityType ); + } + + @Override + public Collection getManagedEntities(EntityType entityType) { + return delegate.getManagedEntities( entityType ); + } + @Override public void addEventListeners(SessionEventListener... listeners) { delegate.addEventListeners( listeners ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java index 131c20d061ed..7ae375c70e88 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java @@ -4,12 +4,14 @@ */ package org.hibernate.engine.spi; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.function.Supplier; import jakarta.persistence.CacheRetrieveMode; import jakarta.persistence.CacheStoreMode; +import jakarta.persistence.metamodel.EntityType; import org.hibernate.CacheMode; import org.hibernate.Filter; import org.hibernate.FlushMode; @@ -420,6 +422,26 @@ public LobHelper getLobHelper() { return this.lazySession.get().getLobHelper(); } + @Override + public Collection getManagedEntities() { + return this.lazySession.get().getManagedEntities(); + } + + @Override + public Collection getManagedEntities(String entityName) { + return this.lazySession.get().getManagedEntities( entityName ); + } + + @Override + public Collection getManagedEntities(Class entityType) { + return this.lazySession.get().getManagedEntities( entityType ); + } + + @Override + public Collection getManagedEntities(EntityType entityType) { + return this.lazySession.get().getManagedEntities( entityType ); + } + @Override public SharedSessionBuilder sessionWithOptions() { return this.lazySession.get().sessionWithOptions(); 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 48c49e2f1215..475d01ae0685 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -16,6 +16,7 @@ import java.sql.Connection; import java.sql.NClob; import java.sql.SQLException; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -24,6 +25,7 @@ import jakarta.persistence.PessimisticLockScope; import jakarta.persistence.Timeout; +import jakarta.persistence.metamodel.EntityType; import org.hibernate.BatchSize; import org.hibernate.CacheMode; import org.hibernate.ConnectionAcquisitionMode; @@ -64,6 +66,7 @@ import org.hibernate.engine.spi.ActionQueue.TransactionCompletionProcesses; import org.hibernate.engine.spi.EffectiveEntityGraph; import org.hibernate.engine.spi.EntityEntry; +import org.hibernate.engine.spi.EntityHolder; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.PersistenceContext; @@ -106,6 +109,7 @@ import org.hibernate.event.spi.ReplicateEvent; import org.hibernate.event.spi.ReplicateEventListener; import org.hibernate.loader.internal.CacheLoadHelper; +import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.ManagedDomainType; import org.hibernate.resource.transaction.spi.TransactionObserver; import org.hibernate.event.monitor.spi.EventMonitor; @@ -2983,6 +2987,38 @@ public Metamodel getMetamodel() { return getFactory().getJpaMetamodel(); } + @Override + public Collection getManagedEntities() { + return persistenceContext.getEntityHoldersByKey() + .values().stream().map( EntityHolder::getManagedObject ) + .toList(); + } + + @Override + public Collection getManagedEntities(String entityName) { + return persistenceContext.getEntityHoldersByKey().entrySet().stream() + .filter( entry -> entry.getKey().getEntityName().equals( entityName ) ) + .map( entry -> entry.getValue().getManagedObject() ) + .toList(); + } + + @Override + public Collection getManagedEntities(Class entityType) { + return persistenceContext.getEntityHoldersByKey().entrySet().stream() + .filter( entry -> entry.getKey().getPersister().getMappedClass().equals( entityType ) ) + .map( entry -> (E) entry.getValue().getManagedObject() ) + .toList(); + } + + @Override + public Collection getManagedEntities(EntityType entityType) { + final String entityName = ( (EntityDomainType) entityType ).getHibernateEntityName(); + return persistenceContext.getEntityHoldersByKey().entrySet().stream() + .filter( entry -> entry.getKey().getEntityName().equals( entityName ) ) + .map( entry -> (E) entry.getValue().getManagedObject() ) + .toList(); + } + /** * Used by JDK serialization... * diff --git a/hibernate-jcache/src/test/java/org/hibernate/orm/test/caching/GetManagedEntitiesTest.java b/hibernate-jcache/src/test/java/org/hibernate/orm/test/caching/GetManagedEntitiesTest.java new file mode 100644 index 000000000000..37ec712e3268 --- /dev/null +++ b/hibernate-jcache/src/test/java/org/hibernate/orm/test/caching/GetManagedEntitiesTest.java @@ -0,0 +1,42 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.caching; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import org.hibernate.Session; +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Jpa(annotatedClasses = GetManagedEntitiesTest.ManagedEntity.class) +public class GetManagedEntitiesTest { + + @Test void test(EntityManagerFactoryScope scope) { + scope.inTransaction( em -> { + for (int i=0; i<5; i++) { + em.persist( new ManagedEntity() ); + } + } ); + scope.inTransaction( em -> { + assertEquals( 5, em.createQuery( "from ME", ManagedEntity.class ).getResultList().size() ); + final Session session = em.unwrap( Session.class ); + assertEquals( 5, session.getManagedEntities().size() ); + assertEquals( 5, session.getManagedEntities( ManagedEntity.class ).size() ); + assertEquals( 5, session.getManagedEntities( ManagedEntity.class.getName() ).size() ); + session.getManagedEntities( ManagedEntity.class ).forEach(session::refresh); + session.getManagedEntities( ManagedEntity.class ).forEach(session::detach); + } ); + } + + @Entity(name = "ME") + static class ManagedEntity { + @Id @GeneratedValue + Long id; + } +}