HHH-10055 - Lazy loading of collections in enhanced entity not working

This commit is contained in:
Steve Ebersole 2015-09-10 14:05:37 -05:00
parent d0b0d7ef87
commit 9d6886198b
9 changed files with 497 additions and 36 deletions

View File

@ -0,0 +1,180 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.bytecode.enhance.spi.interceptor;
import java.util.Locale;
import org.hibernate.FlushMode;
import org.hibernate.LazyInitializationException;
import org.hibernate.Session;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.SessionFactoryRegistry;
import org.jboss.logging.Logger;
/**
* @author Steve Ebersole
*/
public class Helper {
private static final Logger log = Logger.getLogger( Helper.class );
interface Consumer {
SessionImplementor getLinkedSession();
boolean allowLoadOutsideTransaction();
String getSessionFactoryUuid();
}
interface LazyInitializationWork<T> {
T doWork(SessionImplementor session, boolean isTemporarySession);
// informational details
String getEntityName();
String getAttributeName();
}
private final Consumer consumer;
public Helper(Consumer consumer) {
this.consumer = consumer;
}
public <T> T performWork(LazyInitializationWork<T> lazyInitializationWork) {
SessionImplementor session = consumer.getLinkedSession();
boolean isTempSession = false;
boolean isJta = false;
// first figure out which Session to use
if ( session == null ) {
if ( consumer.allowLoadOutsideTransaction() ) {
session = openTemporarySessionForLoading( lazyInitializationWork );
isTempSession = true;
}
else {
throwLazyInitializationException( Cause.NO_SESSION, lazyInitializationWork );
}
}
else if ( !session.isOpen() ) {
if ( consumer.allowLoadOutsideTransaction() ) {
session = openTemporarySessionForLoading( lazyInitializationWork );
isTempSession = true;
}
else {
throwLazyInitializationException( Cause.CLOSED_SESSION, lazyInitializationWork );
}
}
else if ( !session.isConnected() ) {
if ( consumer.allowLoadOutsideTransaction() ) {
session = openTemporarySessionForLoading( lazyInitializationWork );
isTempSession = true;
}
else {
throwLazyInitializationException( Cause.DISCONNECTED_SESSION, lazyInitializationWork );
}
}
// If we are using a temporary Session, begin a transaction if necessary
if ( isTempSession ) {
isJta = session.getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta();
if ( !isJta ) {
// Explicitly handle the transactions only if we're not in
// a JTA environment. A lazy loading temporary session can
// be created even if a current session and transaction are
// open (ex: session.clear() was used). We must prevent
// multiple transactions.
( (Session) session ).beginTransaction();
}
}
try {
// do the actual work
return lazyInitializationWork.doWork( session, isTempSession );
}
finally {
if ( isTempSession ) {
try {
// Commit the JDBC transaction is we started one.
if ( !isJta ) {
( (Session) session ).getTransaction().commit();
}
}
catch (Exception e) {
log.warn(
"Unable to commit JDBC transaction on temporary session used to load lazy " +
"collection associated to no session"
);
}
// Close the just opened temp Session
try {
( (Session) session ).close();
}
catch (Exception e) {
log.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
}
}
}
}
enum Cause {
NO_SESSION,
CLOSED_SESSION,
DISCONNECTED_SESSION,
NO_SF_UUID
}
private void throwLazyInitializationException(Cause cause, LazyInitializationWork work) {
final String reason;
switch ( cause ) {
case NO_SESSION: {
reason = "no session and settings disallow loading outside the Session";
break;
}
case CLOSED_SESSION: {
reason = "session is closed and settings disallow loading outside the Session";
break;
}
case DISCONNECTED_SESSION: {
reason = "session is disconnected and settings disallow loading outside the Session";
break;
}
case NO_SF_UUID: {
reason = "could not determine SessionFactory UUId to create temporary Session for loading";
break;
}
default: {
reason = "<should never get here>";
}
}
final String message = String.format(
Locale.ROOT,
"Unable to perform requested lazy initialization [%s.%s] - %s",
work.getEntityName(),
work.getAttributeName(),
reason
);
throw new LazyInitializationException( message );
}
private SessionImplementor openTemporarySessionForLoading(LazyInitializationWork lazyInitializationWork) {
if ( consumer.getSessionFactoryUuid() == null ) {
throwLazyInitializationException( Cause.NO_SF_UUID, lazyInitializationWork );
}
final SessionFactoryImplementor sf = (SessionFactoryImplementor)
SessionFactoryRegistry.INSTANCE.getSessionFactory( consumer.getSessionFactoryUuid() );
final SessionImplementor session = (SessionImplementor) sf.openSession();
session.getPersistenceContext().setDefaultReadOnly( true );
session.setFlushMode( FlushMode.MANUAL );
return session;
}
}

View File

@ -7,63 +7,131 @@
package org.hibernate.bytecode.enhance.spi.interceptor;
import java.io.Serializable;
import java.util.Collection;
import java.util.Set;
import javax.naming.NamingException;
import org.hibernate.LazyInitializationException;
import org.hibernate.LockMode;
import org.hibernate.bytecode.enhance.internal.tracker.SimpleFieldTracker;
import org.hibernate.bytecode.enhance.spi.CollectionTracker;
import org.hibernate.bytecode.enhance.spi.interceptor.Helper.Consumer;
import org.hibernate.bytecode.enhance.spi.interceptor.Helper.LazyInitializationWork;
import org.hibernate.bytecode.instrumentation.spi.LazyPropertyInitializer;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
import org.hibernate.engine.spi.SelfDirtinessTracker;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.Status;
import org.hibernate.persister.entity.EntityPersister;
import org.jboss.logging.Logger;
/**
* Interceptor that loads attributes lazily
*
* @author Luis Barreiro
*/
public class LazyAttributeLoader implements PersistentAttributeInterceptor {
public class LazyAttributeLoader implements PersistentAttributeInterceptor, Consumer {
private static final Logger log = Logger.getLogger( LazyAttributeLoader.class );
private transient SessionImplementor session;
private final Set<String> lazyFields;
private final String entityName;
private String sessionFactoryUuid;
private boolean allowLoadOutsideTransaction;
private final SimpleFieldTracker initializedFields = new SimpleFieldTracker();
public LazyAttributeLoader(SessionImplementor session, Set<String> lazyFields, String entityName) {
this.session = session;
this.lazyFields = lazyFields;
this.entityName = entityName;
this.allowLoadOutsideTransaction = session.getFactory().getSessionFactoryOptions().isInitializeLazyStateOutsideTransactionsEnabled();
if ( this.allowLoadOutsideTransaction ) {
try {
this.sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent();
}
catch (NamingException e) {
log.debug( "Unable to determine SF UUID in preparation for `allowLoadOutsideTransaction`" );
}
}
}
protected final Object intercept(Object target, String fieldName, Object value) {
if ( !isAttributeLoaded( fieldName ) ) {
if ( session == null ) {
throw new LazyInitializationException( "entity with lazy properties is not associated with a session" );
}
else if ( !session.isOpen() || !session.isConnected() ) {
throw new LazyInitializationException( "session is not connected" );
}
Object loadedValue = ( (LazyPropertyInitializer) session.getFactory()
.getEntityPersister( entityName ) ).initializeLazyProperty(
fieldName,
target,
session
);
initializedFields.add( fieldName );
takeCollectionSizeSnapshot( target, fieldName, loadedValue );
return loadedValue;
protected final Object intercept(Object target, String attributeName, Object value) {
if ( !isAttributeLoaded( attributeName ) ) {
return loadAttribute( target, attributeName );
}
return value;
}
private Object loadAttribute(final Object target, final String attributeName) {
return new Helper( this ).performWork(
new LazyInitializationWork() {
@Override
public Object doWork(SessionImplementor session, boolean isTemporarySession) {
final EntityPersister persister = session.getFactory().getEntityPersister( getEntityName() );
if ( isTemporarySession ) {
final Serializable id = persister.getIdentifier( target, null );
// Add an entry for this entity in the PC of the temp Session
// NOTE : a few arguments that would be nice to pass along here...
// 1) loadedState if we know any
final Object[] loadedState = null;
// 2) does a row exist in the db for this entity?
final boolean existsInDb = true;
// NOTE2: the final boolean is 'lazyPropertiesAreUnfetched' which is another
// place where a "single lazy fetch group" shows up
session.getPersistenceContext().addEntity(
target,
Status.READ_ONLY,
loadedState,
session.generateEntityKey( id, persister ),
persister.getVersion( target ),
LockMode.NONE,
existsInDb,
persister,
true,
true
);
}
final LazyPropertyInitializer initializer = (LazyPropertyInitializer) persister;
final Object loadedValue = initializer.initializeLazyProperty(
attributeName,
target,
session
);
initializedFields.add( attributeName );
takeCollectionSizeSnapshot( target, attributeName, loadedValue );
return loadedValue;
}
@Override
public String getEntityName() {
return entityName;
}
@Override
public String getAttributeName() {
return attributeName;
}
}
);
}
public final void setSession(SessionImplementor session) {
this.session = session;
}
public final void unsetSession() {
this.session = null;
}
public boolean isAttributeLoaded(String fieldName) {
return lazyFields == null || !lazyFields.contains( fieldName ) || initializedFields.contains( fieldName );
}
@ -220,4 +288,19 @@ public class LazyAttributeLoader implements PersistentAttributeInterceptor {
}
return newValue;
}
@Override
public SessionImplementor getLinkedSession() {
return session;
}
@Override
public boolean allowLoadOutsideTransaction() {
return allowLoadOutsideTransaction;
}
@Override
public String getSessionFactoryUuid() {
return sessionFactoryUuid;
}
}

View File

@ -598,7 +598,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
protected void prepareForPossibleLoadingOutsideTransaction() {
if ( session != null ) {
allowLoadOutsideTransaction = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled();
allowLoadOutsideTransaction = session.getFactory().getSessionFactoryOptions().isInitializeLazyStateOutsideTransactionsEnabled();
if ( allowLoadOutsideTransaction && sessionFactoryUuid == null ) {
try {

View File

@ -31,6 +31,7 @@ import org.hibernate.NonUniqueObjectException;
import org.hibernate.PersistentObjectException;
import org.hibernate.TransientObjectException;
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoader;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.collection.spi.PersistentCollection;
@ -46,6 +47,8 @@ import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.EntityUniqueKey;
import org.hibernate.engine.spi.ManagedEntity;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.Status;
@ -218,9 +221,21 @@ public class StatefulPersistenceContext implements PersistenceContext {
}
((HibernateProxy) o).getHibernateLazyInitializer().unsetSession();
}
for ( Entry<Object, EntityEntry> objectEntityEntryEntry : entityEntryContext.reentrantSafeEntityEntries() ) {
// todo : I dont think this need be reentrant safe
if ( objectEntityEntryEntry.getKey() instanceof PersistentAttributeInterceptable ) {
final PersistentAttributeInterceptor interceptor = ( (PersistentAttributeInterceptable) objectEntityEntryEntry.getKey() ).$$_hibernate_getInterceptor();
if ( interceptor instanceof LazyAttributeLoader ) {
( (LazyAttributeLoader) interceptor ).unsetSession();
}
}
}
for ( Map.Entry<PersistentCollection, CollectionEntry> aCollectionEntryArray : IdentityMap.concurrentEntries( collectionEntries ) ) {
aCollectionEntryArray.getKey().unsetSession( getSession() );
}
arrayHolders.clear();
entitiesByKey.clear();
entitiesByUniqueKey.clear();

View File

@ -45,6 +45,7 @@ import org.hibernate.cache.spi.entry.ReferenceCacheEntryImpl;
import org.hibernate.cache.spi.entry.StandardCacheEntryImpl;
import org.hibernate.cache.spi.entry.StructuredCacheEntry;
import org.hibernate.cache.spi.entry.UnstructuredCacheEntry;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.dialect.lock.LockingStrategy;
import org.hibernate.engine.OptimisticLockStyle;
import org.hibernate.engine.internal.CacheHelper;
@ -56,6 +57,7 @@ import org.hibernate.engine.jdbc.batch.internal.BasicBatchKey;
import org.hibernate.engine.spi.CachedNaturalIdValueSource;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.CascadingActions;
import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityEntryFactory;
import org.hibernate.engine.spi.EntityKey;
@ -91,6 +93,7 @@ import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.spi.PersisterCreationContext;
import org.hibernate.persister.walking.internal.EntityIdentifierDefinitionHelper;
import org.hibernate.persister.walking.spi.AttributeDefinition;
@ -115,6 +118,7 @@ import org.hibernate.tuple.ValueGeneration;
import org.hibernate.tuple.entity.EntityMetamodel;
import org.hibernate.tuple.entity.EntityTuplizer;
import org.hibernate.type.AssociationType;
import org.hibernate.type.CollectionType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
@ -887,12 +891,46 @@ public abstract class AbstractEntityPersister
}
public Object initializeLazyProperty(String fieldName, Object entity, SessionImplementor session)
throws HibernateException {
public Object initializeLazyProperty(String fieldName, Object entity, SessionImplementor session) {
final EntityEntry entry = session.getPersistenceContext().getEntry( entity );
if ( hasCollections() ) {
final Type type = getPropertyType( fieldName );
if ( type.isCollectionType() ) {
// we have a condition where a collection attribute is being access via enhancement:
// we can circumvent all the rest and just return the PersistentCollection
final CollectionType collectionType = (CollectionType) type;
final CollectionPersister persister = factory.getCollectionPersister( collectionType.getRole() );
// Get/create the collection, and make sure it is initialized! This initialized part is
// different from proxy-based scenarios where we have to create the PersistentCollection
// reference "ahead of time" to add as a reference to the proxy. For bytecode solutions
// we are not creating the PersistentCollection ahead of time, but instead we are creating
// it on first request through the enhanced entity.
// see if there is already a collection instance associated with the session
// NOTE : can this ever happen?
final Serializable key = getCollectionKey( persister, entity, entry, session );
PersistentCollection collection = session.getPersistenceContext().getCollection( new CollectionKey( persister, key ) );
if ( collection == null ) {
collection = collectionType.instantiate( session, persister, key );
collection.setOwner( entity );
session.getPersistenceContext().addUninitializedCollection( persister, collection, key );
}
// Initialize it
session.initializeCollection( collection, false );
if ( collectionType.isArrayType() ) {
session.getPersistenceContext().addCollectionHolder( collection );
}
// EARLY EXIT!!!
return collection;
}
}
final Serializable id = session.getContextEntityIdentifier( entity );
final EntityEntry entry = session.getPersistenceContext().getEntry( entity );
if ( entry == null ) {
throw new HibernateException( "entity is not associated with the session: " + id );
}
@ -924,6 +962,27 @@ public abstract class AbstractEntityPersister
}
protected Serializable getCollectionKey(
CollectionPersister persister,
Object owner,
EntityEntry ownerEntry,
SessionImplementor session) {
final CollectionType collectionType = persister.getCollectionType();
if ( ownerEntry != null ) {
// this call only works when the owner is associated with the Session, which is not always the case
return collectionType.getKeyOfOwner( owner, session );
}
if ( collectionType.getLHSPropertyName() == null ) {
// collection key is defined by the owning entity identifier
return persister.getOwnerEntityPersister().getIdentifier( owner, session );
}
else {
return (Serializable) persister.getOwnerEntityPersister().getPropertyValue( owner, collectionType.getLHSPropertyName() );
}
}
private Object initializeLazyPropertiesFromDatastore(
final String fieldName,
final Object entity,

View File

@ -21,6 +21,7 @@ import org.hibernate.test.bytecode.enhancement.join.HHH3949TestTask2;
import org.hibernate.test.bytecode.enhancement.join.HHH3949TestTask3;
import org.hibernate.test.bytecode.enhancement.join.HHH3949TestTask4;
import org.hibernate.test.bytecode.enhancement.lazy.LazyBasicFieldNotInitializedTestTask;
import org.hibernate.test.bytecode.enhancement.lazy.LazyCollectionLoadingTestTask;
import org.hibernate.test.bytecode.enhancement.lazy.LazyLoadingIntegrationTestTask;
import org.hibernate.test.bytecode.enhancement.lazy.LazyLoadingTestTask;
import org.hibernate.test.bytecode.enhancement.lazy.basic.LazyBasicFieldAccessTestTask;
@ -63,6 +64,12 @@ public class EnhancerTest extends BaseUnitTestCase {
EnhancerTestUtils.runEnhancerTestTask( LazyBasicFieldAccessTestTask.class );
}
@Test
@TestForIssue( jiraKey = "HHH-10055" )
public void testLazyCollectionHandling() {
EnhancerTestUtils.runEnhancerTestTask( LazyCollectionLoadingTestTask.class );
}
@Test
@TestForIssue( jiraKey = "HHH-10055" )
@FailureExpected( jiraKey = "HHH-10055" )

View File

@ -15,7 +15,8 @@ public abstract class AbstractHHH3949TestTask extends AbstractEnhancerTestTask {
public void prepare() {
Configuration cfg = new Configuration();
cfg.setProperty( Environment.ENABLE_LAZY_LOAD_NO_TRANS, "true" );
// cfg.setProperty( Environment.ENABLE_LAZY_LOAD_NO_TRANS, "true" );
cfg.setProperty( Environment.ENABLE_LAZY_LOAD_NO_TRANS, "false" );
cfg.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "false" );
super.prepare( cfg );

View File

@ -0,0 +1,101 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.bytecode.enhancement.lazy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.test.bytecode.enhancement.AbstractEnhancerTestTask;
import org.hibernate.test.bytecode.enhancement.EnhancerTestUtils;
import org.junit.Assert;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* Simple test for lazy collection handling in the new bytecode support.
* Prior to HHH-10055 lazy collections were simply not handled. The tests
* initially added for HHH-10055 cover the more complicated case of handling
* lazy collection initialization outside of a transaction; that is a bigger
* fix, and I first want to get collection handling to work here in general.
*
* @author Steve Ebersole
*/
public class LazyCollectionLoadingTestTask extends AbstractEnhancerTestTask {
private static final int CHILDREN_SIZE = 10;
private Long parentID;
private Long lastChildID;
public Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {Parent.class, Child.class};
}
public void prepare() {
Configuration cfg = new Configuration();
cfg.setProperty( Environment.ENABLE_LAZY_LOAD_NO_TRANS, "false" );
cfg.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "false" );
super.prepare( cfg );
Session s = getFactory().openSession();
s.beginTransaction();
Parent parent = new Parent();
parent.setChildren( new ArrayList<Child>() );
for ( int i = 0; i < CHILDREN_SIZE; i++ ) {
final Child child = new Child();
child.setParent( parent );
s.persist( child );
lastChildID = child.getId();
}
s.persist( parent );
parentID = parent.getId();
s.getTransaction().commit();
s.close();
}
public void execute() {
Session s = getFactory().openSession();
s.beginTransaction();
Parent parent = s.load( Parent.class, parentID );
assertThat( parent, notNullValue() );
assertThat( parent, not( instanceOf( HibernateProxy.class ) ) );
assertThat( parent, not( instanceOf( HibernateProxy.class ) ) );
assertFalse( Hibernate.isPropertyInitialized( parent, "children" ) );
EnhancerTestUtils.checkDirtyTracking( parent );
List children1 = parent.getChildren();
List children2 = parent.getChildren();
assertTrue( Hibernate.isPropertyInitialized( parent, "children" ) );
EnhancerTestUtils.checkDirtyTracking( parent );
assertThat( children1, sameInstance( children2 ) );
assertThat( children1.size(), equalTo( CHILDREN_SIZE ) );
s.getTransaction().commit();
s.close();
}
protected void cleanup() {
}
}

View File

@ -57,31 +57,46 @@ public class LazyCollectionWithClearedSessionTestTask extends AbstractEnhancerTe
// first load the store, making sure collection is not initialized
Store store = s.get( Store.class, 1 );
assertNotNull( store );
assertFalse( Hibernate.isInitialized( store.getInventories() ) );
assertFalse( Hibernate.isPropertyInitialized( store, "inventories" ) );
assertEquals( 1, getFactory().getStatistics().getSessionOpenCount() );
assertEquals( 0, getFactory().getStatistics().getSessionCloseCount() );
// then clear session and try to initialize collection
s.clear();
assertNotNull( store );
assertFalse( Hibernate.isPropertyInitialized( store, "inventories" ) );
store.getInventories().size();
assertTrue( Hibernate.isInitialized( store.getInventories() ) );
assertTrue( Hibernate.isPropertyInitialized( store, "inventories" ) );
// the extra Session is the temp Session needed to perform the init
assertEquals( 2, getFactory().getStatistics().getSessionOpenCount() );
assertEquals( 1, getFactory().getStatistics().getSessionCloseCount() );
// clear Session again. The collection should still be recognized as initialized from above
s.clear();
assertNotNull( store );
assertTrue( Hibernate.isPropertyInitialized( store, "inventories" ) );
assertEquals( 2, getFactory().getStatistics().getSessionOpenCount() );
assertEquals( 1, getFactory().getStatistics().getSessionCloseCount() );
// lets clear the Session again and this time reload the Store
s.clear();
store = s.get( Store.class, 1 );
s.clear();
assertNotNull( store );
assertFalse( Hibernate.isInitialized( store.getInventories() ) );
// collection should be back to uninitialized since we have a new entity instance
assertFalse( Hibernate.isPropertyInitialized( store, "inventories" ) );
assertEquals( 2, getFactory().getStatistics().getSessionOpenCount() );
assertEquals( 1, getFactory().getStatistics().getSessionCloseCount() );
store.getInventories().size();
assertTrue( Hibernate.isPropertyInitialized( store, "inventories" ) );
// the extra Session is the temp Session needed to perform the init
assertEquals( 3, getFactory().getStatistics().getSessionOpenCount() );
assertEquals( 2, getFactory().getStatistics().getSessionCloseCount() );
// clear Session again. The collection should still be recognized as initialized from above
s.clear();
store.getInventories().iterator();
assertTrue( Hibernate.isInitialized( store.getInventories() ) );
assertNotNull( store );
assertTrue( Hibernate.isPropertyInitialized( store, "inventories" ) );
assertEquals( 3, getFactory().getStatistics().getSessionOpenCount() );
assertEquals( 2, getFactory().getStatistics().getSessionCloseCount() );