HHH-10055 - Lazy loading of collections in enhanced entity not working
(cherry picked from commit 9d6886198b
)
This commit is contained in:
parent
5a8fc3ee46
commit
9050b78ddc
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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" )
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
|
||||
}
|
|
@ -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() );
|
||||
|
||||
|
|
Loading…
Reference in New Issue