HHH-17966 EAGER collections in StatelessSession (#8186)

This commit is contained in:
Gavin King 2024-04-16 12:36:29 +02:00 committed by GitHub
parent 431cf992ec
commit 7e108433ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 108 additions and 20 deletions

View File

@ -26,7 +26,6 @@ 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.event.spi.AutoFlushEvent;
import org.hibernate.event.spi.EventManager;
import org.hibernate.graph.RootGraph;
import org.hibernate.jdbc.ReturningWork;

View File

@ -48,7 +48,7 @@ public abstract class AbstractCollectionEvent extends AbstractEvent {
protected static CollectionPersister getLoadedCollectionPersister( PersistentCollection<?> collection, EventSource source ) {
CollectionEntry ce = source.getPersistenceContextInternal().getCollectionEntry( collection );
return ( ce == null ? null : ce.getLoadedPersister() );
return ce == null ? null : ce.getLoadedPersister();
}
protected static Object getLoadedOwnerOrNull( PersistentCollection<?> collection, EventSource source ) {
@ -68,19 +68,24 @@ public abstract class AbstractCollectionEvent extends AbstractEvent {
CollectionPersister collectionPersister,
Object affectedOwner,
EventSource source ) {
// collectionPersister should not be null, but we don't want to throw
// an exception if it is null
String entityName = collectionPersister == null
? null
: collectionPersister.getOwnerEntityPersister().getEntityName();
if ( affectedOwner != null ) {
EntityEntry ee = source.getPersistenceContextInternal().getEntry( affectedOwner );
if ( ee != null && ee.getEntityName() != null) {
entityName = ee.getEntityName();
final EntityEntry entry =
source.getPersistenceContextInternal()
.getEntry( affectedOwner );
if ( entry != null && entry.getEntityName() != null ) {
return entry.getEntityName();
}
}
return entityName;
}
if ( collectionPersister != null ) {
return collectionPersister.getOwnerEntityPersister().getEntityName();
}
else {
// collectionPersister should not be null,
// but we don't want to throw an exception
// if it is null
return null;
}
}
public PersistentCollection<?> getCollection() {

View File

@ -23,6 +23,7 @@ import org.hibernate.cache.spi.access.EntityDataAccess;
import org.hibernate.collection.spi.CollectionSemantics;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.internal.StatefulPersistenceContext;
import org.hibernate.engine.spi.CollectionEntry;
import org.hibernate.engine.spi.EffectiveEntityGraph;
import org.hibernate.engine.spi.EntityHolder;
import org.hibernate.engine.spi.EntityKey;
@ -71,6 +72,7 @@ import static org.hibernate.engine.internal.Versioning.setVersion;
import static org.hibernate.event.internal.DefaultInitializeCollectionEventListener.handlePotentiallyEmptyCollection;
import static org.hibernate.generator.EventType.INSERT;
import static org.hibernate.internal.util.NullnessUtil.castNonNull;
import static org.hibernate.pretty.MessageHelper.collectionInfoString;
import static org.hibernate.pretty.MessageHelper.infoString;
import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
@ -529,16 +531,35 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
}
@Override
public void initializeCollection(
PersistentCollection<?> collection,
boolean writing) throws HibernateException {
throw new SessionException( "collections cannot be fetched by a stateless session" );
public void initializeCollection(PersistentCollection<?> collection, boolean writing)
throws HibernateException {
final PersistenceContext persistenceContext = getPersistenceContextInternal();
final CollectionEntry ce = persistenceContext.getCollectionEntry( collection );
if ( ce == null ) {
throw new HibernateException( "no entry for collection" );
}
if ( !collection.wasInitialized() ) {
final CollectionPersister loadedPersister = ce.getLoadedPersister();
final Object loadedKey = ce.getLoadedKey();
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Initializing collection {0}",
collectionInfoString( loadedPersister, collection, loadedKey, this ) );
}
loadedPersister.initialize( loadedKey, this );
handlePotentiallyEmptyCollection( collection, persistenceContext, loadedKey, loadedPersister );
if ( LOG.isTraceEnabled() ) {
LOG.trace( "Collection initialized" );
}
//TODO: statistics!
// final StatisticsImplementor statistics = getFactory().getStatistics();
// if ( statistics.isStatisticsEnabled() ) {
// statistics.fetchCollection( loadedPersister.getRole() );
// }
}
}
@Override
public Object instantiate(
String entityName,
Object id) throws HibernateException {
public Object instantiate(String entityName, Object id) throws HibernateException {
return instantiate( getEntityPersister( entityName ), id );
}

View File

@ -0,0 +1,63 @@
package org.hibernate.orm.test.stateless;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import org.hibernate.Hibernate;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
import java.util.HashSet;
import java.util.Set;
import static jakarta.persistence.FetchType.EAGER;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@SessionFactory
@DomainModel(annotatedClasses = EagerCollectionInStatelessTest.WithEagerCollection.class)
public class EagerCollectionInStatelessTest {
@Test
void test(SessionFactoryScope scope) {
scope.inStatelessTransaction(s-> {
WithEagerCollection entity = new WithEagerCollection();
entity.eager.add("Hello");
entity.eager.add("World");
entity.lazy.add("Goodbye");
entity.lazy.add("World");
s.insert(entity);
});
scope.inStatelessSession(s-> {
WithEagerCollection entity = s.get(WithEagerCollection.class, 69L);
assertTrue(Hibernate.isInitialized(entity.eager));
assertFalse(Hibernate.isInitialized(entity.lazy));
s.fetch(entity.lazy);
assertTrue(Hibernate.isInitialized(entity.lazy));
assertEquals(2, entity.eager.size());
assertEquals(2, entity.lazy.size());
});
scope.inStatelessSession(s-> {
WithEagerCollection entity =
s.createSelectionQuery("where id= 69L", WithEagerCollection.class)
.getSingleResult();
assertTrue(Hibernate.isInitialized(entity.eager));
assertFalse(Hibernate.isInitialized(entity.lazy));
s.fetch(entity.lazy);
assertTrue(Hibernate.isInitialized(entity.lazy));
assertEquals(2, entity.eager.size());
assertEquals(2, entity.lazy.size());
});
}
@Entity
static class WithEagerCollection {
@Id long id = 69L;
@ElementCollection(fetch = EAGER)
Set<String> eager = new HashSet<>();
@ElementCollection
Set<String> lazy = new HashSet<>();
}
}