org.hibernate.engine.spi.CollectionEntry Maven / Gradle / Ivy
/*
* 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 .
*/
package org.hibernate.engine.spi;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Collection;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.collection.internal.AbstractPersistentCollection;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.pretty.MessageHelper;
/**
* We need an entry to tell us all about the current state
* of a collection with respect to its persistent state
*
* @author Gavin King
*/
public final class CollectionEntry implements Serializable {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( CollectionEntry.class );
//ATTRIBUTES MAINTAINED BETWEEN FLUSH CYCLES
// session-start/post-flush persistent state
private Serializable snapshot;
// allow the CollectionSnapshot to be serialized
private String role;
// "loaded" means the reference that is consistent
// with the current database state
private transient CollectionPersister loadedPersister;
private Serializable loadedKey;
// ATTRIBUTES USED ONLY DURING FLUSH CYCLE
// during flush, we navigate the object graph to
// collections and decide what to do with them
private transient boolean reached;
private transient boolean processed;
private transient boolean doupdate;
private transient boolean doremove;
private transient boolean dorecreate;
// if we instantiate a collection during the flush() process,
// we must ignore it for the rest of the flush()
private transient boolean ignore;
// "current" means the reference that was found during flush()
private transient CollectionPersister currentPersister;
private transient Serializable currentKey;
/**
* For newly wrapped collections, or dereferenced collection wrappers
*/
public CollectionEntry(CollectionPersister persister, PersistentCollection collection) {
// new collections that get found + wrapped
// during flush shouldn't be ignored
ignore = false;
collection.clearDirty(); //a newly wrapped collection is NOT dirty (or we get unnecessary version updates)
snapshot = persister.isMutable() ?
collection.getSnapshot( persister ) :
null;
collection.setSnapshot( loadedKey, role, snapshot );
}
/**
* For collections just loaded from the database
*/
public CollectionEntry(
final PersistentCollection collection,
final CollectionPersister loadedPersister,
final Serializable loadedKey,
final boolean ignore ) {
this.ignore = ignore;
//collection.clearDirty()
this.loadedKey = loadedKey;
setLoadedPersister( loadedPersister );
collection.setSnapshot( loadedKey, role, null );
//postInitialize() will be called after initialization
}
/**
* For uninitialized detached collections
*/
public CollectionEntry(CollectionPersister loadedPersister, Serializable loadedKey) {
// detached collection wrappers that get found + reattached
// during flush shouldn't be ignored
ignore = false;
//collection.clearDirty()
this.loadedKey = loadedKey;
setLoadedPersister( loadedPersister );
}
/**
* For initialized detached collections
*/
public CollectionEntry(PersistentCollection collection, SessionFactoryImplementor factory) throws MappingException {
// detached collections that get found + reattached
// during flush shouldn't be ignored
ignore = false;
loadedKey = collection.getKey();
setLoadedPersister( factory.getMetamodel().collectionPersister( collection.getRole() ) );
snapshot = collection.getStoredSnapshot();
}
/**
* Used from custom serialization.
*
* @see #serialize
* @see #deserialize
*/
private CollectionEntry(
String role,
Serializable snapshot,
Serializable loadedKey,
SessionFactoryImplementor factory) {
this.role = role;
this.snapshot = snapshot;
this.loadedKey = loadedKey;
if ( role != null ) {
afterDeserialize( factory );
}
}
/**
* Determine if the collection is "really" dirty, by checking dirtiness
* of the collection elements, if necessary
*/
private void dirty(PersistentCollection collection) throws HibernateException {
final CollectionPersister loadedPersister = getLoadedPersister();
boolean forceDirty = collection.wasInitialized() &&
!collection.isDirty() && //optimization
loadedPersister != null &&
loadedPersister.isMutable() && //optimization
( collection.isDirectlyAccessible() || loadedPersister.getElementType().isMutable() ) && //optimization
!collection.equalsSnapshot( loadedPersister );
if ( forceDirty ) {
collection.dirty();
}
}
public void preFlush(PersistentCollection collection) throws HibernateException {
if ( loadedKey == null && collection.getKey() != null ) {
loadedKey = collection.getKey();
}
final CollectionPersister loadedPersister = getLoadedPersister();
boolean nonMutableChange = collection.isDirty()
&& loadedPersister != null
&& !loadedPersister.isMutable();
if ( nonMutableChange ) {
throw new HibernateException(
"changed an immutable collection instance: " +
MessageHelper.collectionInfoString( loadedPersister.getRole(), getLoadedKey() )
);
}
dirty( collection );
if ( LOG.isDebugEnabled() && collection.isDirty() && loadedPersister != null ) {
LOG.debugf(
"Collection dirty: %s",
MessageHelper.collectionInfoString( loadedPersister.getRole(), getLoadedKey() )
);
}
setReached( false );
setProcessed( false );
setDoupdate( false );
setDoremove( false );
setDorecreate( false );
}
public void postInitialize(PersistentCollection collection) throws HibernateException {
final CollectionPersister loadedPersister = getLoadedPersister();
snapshot = loadedPersister.isMutable()
? collection.getSnapshot( loadedPersister )
: null;
collection.setSnapshot(loadedKey, role, snapshot);
if ( loadedPersister.getBatchSize() > 1 ) {
( (AbstractPersistentCollection) collection ).getSession()
.getPersistenceContextInternal()
.getBatchFetchQueue()
.removeBatchLoadableCollection( this );
}
}
/**
* Called after a successful flush
*/
public void postFlush(PersistentCollection collection) throws HibernateException {
if ( isIgnore() ) {
ignore = false;
}
else if ( !isProcessed() ) {
throw new HibernateException( LOG.collectionNotProcessedByFlush( collection.getRole() ) );
}
collection.setSnapshot( loadedKey, role, snapshot );
}
/**
* Called after execution of an action
*/
public void afterAction(PersistentCollection collection) {
loadedKey = getCurrentKey();
setLoadedPersister( getCurrentPersister() );
boolean resnapshot = collection.wasInitialized() &&
( isDoremove() || isDorecreate() || isDoupdate() );
if ( resnapshot ) {
snapshot = loadedPersister == null || !loadedPersister.isMutable() ?
null :
collection.getSnapshot( loadedPersister ); //re-snapshot
}
collection.postAction();
}
public Serializable getKey() {
return getLoadedKey();
}
public String getRole() {
return role;
}
public Serializable getSnapshot() {
return snapshot;
}
private boolean fromMerge;
/**
* Reset the stored snapshot for both the persistent collection and this collection entry.
* Used during the merge of detached collections.
*
* @param collection the persistentcollection to be updated
* @param storedSnapshot the new stored snapshot
*/
public void resetStoredSnapshot(PersistentCollection collection, Serializable storedSnapshot) {
LOG.debugf("Reset storedSnapshot to %s for %s", storedSnapshot, this);
if ( fromMerge ) {
return; // EARLY EXIT!
}
snapshot = storedSnapshot;
collection.setSnapshot( loadedKey, role, snapshot );
fromMerge = true;
}
private void setLoadedPersister(CollectionPersister persister) {
loadedPersister = persister;
setRole( persister == null ? null : persister.getRole() );
}
void afterDeserialize(SessionFactoryImplementor factory) {
loadedPersister = ( factory == null ? null : factory.getMetamodel().collectionPersister( role ) );
}
public boolean wasDereferenced() {
return getLoadedKey() == null;
}
public boolean isReached() {
return reached;
}
public void setReached(boolean reached) {
this.reached = reached;
}
public boolean isProcessed() {
return processed;
}
public void setProcessed(boolean processed) {
this.processed = processed;
}
public boolean isDoupdate() {
return doupdate;
}
public void setDoupdate(boolean doupdate) {
this.doupdate = doupdate;
}
public boolean isDoremove() {
return doremove;
}
public void setDoremove(boolean doremove) {
this.doremove = doremove;
}
public boolean isDorecreate() {
return dorecreate;
}
public void setDorecreate(boolean dorecreate) {
this.dorecreate = dorecreate;
}
public boolean isIgnore() {
return ignore;
}
public CollectionPersister getCurrentPersister() {
return currentPersister;
}
public void setCurrentPersister(CollectionPersister currentPersister) {
this.currentPersister = currentPersister;
}
/**
* This is only available late during the flush
* cycle
*/
public Serializable getCurrentKey() {
return currentKey;
}
public void setCurrentKey(Serializable currentKey) {
this.currentKey = currentKey;
}
/**
* This is only available late during the flush cycle
*/
public CollectionPersister getLoadedPersister() {
return loadedPersister;
}
public Serializable getLoadedKey() {
return loadedKey;
}
public void setRole(String role) {
this.role = role;
}
@Override
public String toString() {
String result = "CollectionEntry" +
MessageHelper.collectionInfoString( loadedPersister.getRole(), loadedKey );
if ( currentPersister != null ) {
result += "->" +
MessageHelper.collectionInfoString( currentPersister.getRole(), currentKey );
}
return result;
}
/**
* Get the collection orphans (entities which were removed from the collection)
*/
public Collection getOrphans(String entityName, PersistentCollection collection) throws HibernateException {
if ( snapshot == null ) {
throw new AssertionFailure( "no collection snapshot for orphan delete" );
}
return collection.getOrphans( snapshot, entityName );
}
public boolean isSnapshotEmpty(PersistentCollection collection) {
//TODO: does this really need to be here?
// does the collection already have
// it's own up-to-date snapshot?
final CollectionPersister loadedPersister = getLoadedPersister();
return collection.wasInitialized() &&
( loadedPersister ==null || loadedPersister.isMutable() ) &&
collection.isSnapshotEmpty( getSnapshot() );
}
/**
* Custom serialization routine used during serialization of a
* Session/PersistenceContext for increased performance.
*
* @param oos The stream to which we should write the serial data.
* @throws java.io.IOException
*/
public void serialize(ObjectOutputStream oos) throws IOException {
oos.writeObject( role );
oos.writeObject( snapshot );
oos.writeObject( loadedKey );
}
/**
* Custom deserialization routine used during deserialization of a
* Session/PersistenceContext for increased performance.
*
* @param ois The stream from which to read the entry.
* @param session The session being deserialized.
*
* @return The deserialized CollectionEntry
*
* @throws IOException
* @throws ClassNotFoundException
*/
public static CollectionEntry deserialize(
ObjectInputStream ois,
SessionImplementor session) throws IOException, ClassNotFoundException {
return new CollectionEntry(
(String) ois.readObject(),
(Serializable) ois.readObject(),
(Serializable) ois.readObject(),
(session == null ? null : session.getFactory())
);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy