Skip to content

Commit

Permalink
HHH-10708 - Accessing a lazy collection in an enhanced class deletes …
Browse files Browse the repository at this point in the history
…it afterwards

(cherry picked from commit 0e1b79d)

Conflicts:
	hibernate-core/src/main/java/org/hibernate/engine/internal/Collections.java

HHH-10708 : Corrections due to backporting
  • Loading branch information
sebersole authored and gbadner committed May 20, 2016
1 parent 9768cf2 commit 0f918b4
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.CachedNaturalIdValueSource;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityEntryExtraState;
Expand Down Expand Up @@ -320,6 +321,15 @@ public Object getLoadedValue(String propertyName) {
}
}

@Override
public void overwriteLoadedStateCollectionValue(String propertyName, PersistentCollection collection) {
assert propertyName != null;
assert loadedState != null;

final int propertyIndex = ( (UniqueKeyLoadable) persister ).getPropertyIndex( propertyName );
loadedState[propertyIndex] = collection;
}

@Override
public boolean requiresDirtyCheck(Object entity) {
return isModifiableEntity()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public final class Collections {
* @param session The session
*/
public static void processUnreachableCollection(PersistentCollection coll, SessionImplementor session) {
if ( coll.getOwner()==null ) {
if ( coll.getOwner() == null ) {
processNeverReferencedCollection( coll, session );
}
else {
Expand Down Expand Up @@ -73,7 +73,7 @@ private static void processDereferencedCollection(PersistentCollection coll, Ses
// the owning entity may have been deleted and its identifier unset due to
// identifier-rollback; in which case, try to look up its identifier from
// the persistence context
if ( session.getFactory().getSettings().isIdentifierRollbackEnabled() ) {
if ( session.getFactory().getSessionFactoryOptions().isIdentifierRollbackEnabled() ) {
final EntityEntry ownerEntry = persistenceContext.getEntry( coll.getOwner() );
if ( ownerEntry != null ) {
ownerId = ownerEntry.getId();
Expand Down Expand Up @@ -156,40 +156,73 @@ public static void processReachableCollection(
);
}

// The CollectionEntry.isReached() stuff is just to detect any silly users
// who set up circular or shared references between/to collections.
if ( ce.isReached() ) {
// We've been here before
throw new HibernateException(
"Found shared references to a collection: " + type.getRole()
);
}
ce.setReached( true );

final SessionFactoryImplementor factory = session.getFactory();
final CollectionPersister persister = factory.getCollectionPersister( type.getRole() );

ce.setCurrentPersister( persister );
//TODO: better to pass the id in as an argument?
ce.setCurrentKey( type.getKeyOfOwner( entity, session ) );

if ( LOG.isDebugEnabled() ) {
if ( collection.wasInitialized() ) {
LOG.debugf(
"Collection found: %s, was: %s (initialized)",
MessageHelper.collectionInfoString( persister, collection, ce.getCurrentKey(), session ),
MessageHelper.collectionInfoString( ce.getLoadedPersister(), collection, ce.getLoadedKey(), session )
final boolean isBytecodeEnhanced = persister.getOwnerEntityPersister().getInstrumentationMetadata().isEnhancedForLazyLoading();
if ( isBytecodeEnhanced && !collection.wasInitialized() ) {
// skip it
LOG.debugf(
"Skipping uninitialized bytecode-lazy collection: %s",
MessageHelper.collectionInfoString( persister, collection, ce.getCurrentKey(), session )
);
ce.setReached( true );
ce.setProcessed( true );
}
else {
// The CollectionEntry.isReached() stuff is just to detect any silly users
// who set up circular or shared references between/to collections.
if ( ce.isReached() ) {
// We've been here beforeQuery
throw new HibernateException(
"Found shared references to a collection: " + type.getRole()
);
}
else {
LOG.debugf(
"Collection found: %s, was: %s (uninitialized)",
MessageHelper.collectionInfoString( persister, collection, ce.getCurrentKey(), session ),
MessageHelper.collectionInfoString( ce.getLoadedPersister(), collection, ce.getLoadedKey(), session )
);
ce.setReached( true );

if ( LOG.isDebugEnabled() ) {
if ( collection.wasInitialized() ) {
LOG.debugf(
"Collection found: %s, was: %s (initialized)",
MessageHelper.collectionInfoString(
persister,
collection,
ce.getCurrentKey(),
session
),
MessageHelper.collectionInfoString(
ce.getLoadedPersister(),
collection,
ce.getLoadedKey(),
session
)
);
}
else {
LOG.debugf(
"Collection found: %s, was: %s (uninitialized)",
MessageHelper.collectionInfoString(
persister,
collection,
ce.getCurrentKey(),
session
),
MessageHelper.collectionInfoString(
ce.getLoadedPersister(),
collection,
ce.getLoadedKey(),
session
)
);
}
}
}

prepareCollectionForUpdate( collection, ce, factory );
prepareCollectionForUpdate( collection, ce, factory );
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,37 +169,43 @@ public void preFlush(PersistentCollection collection) throws HibernateException
loadedKey = collection.getKey();
}

boolean nonMutableChange = collection.isDirty() &&
getLoadedPersister()!=null &&
!getLoadedPersister().isMutable();
if (nonMutableChange) {
boolean nonMutableChange = collection.isDirty()
&& getLoadedPersister() != null
&& !getLoadedPersister().isMutable();
if ( nonMutableChange ) {
throw new HibernateException(
"changed an immutable collection instance: " +
MessageHelper.collectionInfoString( getLoadedPersister().getRole(), getLoadedKey() )
);
);
}

dirty(collection);
dirty( collection );

if ( LOG.isDebugEnabled() && collection.isDirty() && getLoadedPersister() != null ) {
LOG.debugf( "Collection dirty: %s",
MessageHelper.collectionInfoString( getLoadedPersister().getRole(), getLoadedKey() ) );
LOG.debugf(
"Collection dirty: %s",
MessageHelper.collectionInfoString( getLoadedPersister().getRole(), getLoadedKey() )
);
}

setDoupdate(false);
setDoremove(false);
setDorecreate(false);
setReached(false);
setProcessed(false);
setReached( false );
setProcessed( false );

setDoupdate( false );
setDoremove( false );
setDorecreate( false );
}

public void postInitialize(PersistentCollection collection) throws HibernateException {
snapshot = getLoadedPersister().isMutable() ?
collection.getSnapshot( getLoadedPersister() ) :
null;
snapshot = getLoadedPersister().isMutable()
? collection.getSnapshot( getLoadedPersister() )
: null;
collection.setSnapshot(loadedKey, role, snapshot);
if (getLoadedPersister().getBatchSize() > 1) {
((AbstractPersistentCollection) collection).getSession().getPersistenceContext().getBatchFetchQueue().removeBatchLoadableCollection(this);
if ( getLoadedPersister().getBatchSize() > 1 ) {
( (AbstractPersistentCollection) collection ).getSession()
.getPersistenceContext()
.getBatchFetchQueue()
.removeBatchLoadableCollection( this );
}
}

Expand Down Expand Up @@ -273,7 +279,7 @@ private void setLoadedPersister(CollectionPersister persister) {
}

void afterDeserialize(SessionFactoryImplementor factory) {
loadedPersister = ( factory == null ? null : factory.getCollectionPersister(role) );
loadedPersister = ( factory == null ? null : factory.getCollectionPersister( role ) );
}

public boolean wasDereferenced() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.util.Set;

import org.hibernate.LockMode;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.persister.entity.EntityPersister;

/**
Expand All @@ -38,6 +39,10 @@ public interface EntityEntry {

Object[] getLoadedState();

Object getLoadedValue(String propertyName);

void overwriteLoadedStateCollectionValue(String propertyName, PersistentCollection collection);

Object[] getDeletedState();

void setDeletedState(Object[] deletedState);
Expand Down Expand Up @@ -87,8 +92,6 @@ public interface EntityEntry {

boolean isNullifiable(boolean earlyInsert, SessionImplementor session);

Object getLoadedValue(String propertyName);

/**
* Not sure this is the best method name, but the general idea here is to return {@code true} if the entity can
* possibly be dirty. This can only be the case if it is in a modifiable state (not read-only/deleted) and it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,17 @@ public Object initializeLazyProperty(String fieldName, Object entity, SessionImp
session.getPersistenceContext().addCollectionHolder( collection );
}

// update the "state" of the entity's EntityEntry to over-write UNFETCHED_PROPERTY reference
// for the collection to the just loaded collection
final EntityEntry ownerEntry = session.getPersistenceContext().getEntry( entity );
if ( ownerEntry == null ) {
// not good
throw new AssertionFailure(
"Could not locate EntityEntry for the collection owner in the PersistenceContext"
);
}
ownerEntry.overwriteLoadedStateCollectionValue( fieldName, collection );

// EARLY EXIT!!!
return collection;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public void execute() {
Foo foo = s.get( Foo.class, fooId );

// accessing the collection results in an exception
foo.bar.size();
foo.bars.size();

s.flush();
s.getTransaction().commit();
Expand All @@ -88,8 +88,9 @@ protected void cleanup() {
@Id @GeneratedValue
int id;

@OneToMany(orphanRemoval = true, mappedBy = Bar.FOO, targetEntity = Bar.class) @Cascade(CascadeType.ALL)
Set<Bar> bar = new HashSet<>();
@OneToMany(orphanRemoval = true, mappedBy = Bar.FOO, targetEntity = Bar.class)
@Cascade(CascadeType.ALL)
Set<Bar> bars = new HashSet<>();
}

}

0 comments on commit 0f918b4

Please sign in to comment.