import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
-import java.util.Map;
import javax.naming.NamingException;
import org.hibernate.AssertionFailure;
-import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.LazyInitializationException;
import org.hibernate.Session;
-import org.hibernate.collection.internal.AbstractPersistentCollection.DelayedOperation;
-import org.hibernate.collection.internal.AbstractPersistentCollection.IteratorProxy;
-import org.hibernate.collection.internal.AbstractPersistentCollection.LazyInitializationWork;
-import org.hibernate.collection.internal.AbstractPersistentCollection.ListIteratorProxy;
-import org.hibernate.collection.internal.AbstractPersistentCollection.ValueDelayedOperation;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.internal.ForeignKeys;
import org.hibernate.engine.spi.CollectionEntry;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.Status;
import org.hibernate.engine.spi.TypedValue;
-import org.hibernate.internal.CoreLogging;
-import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.SessionFactoryRegistry;
import org.hibernate.internal.util.MarkerObject;
import org.hibernate.internal.util.collections.EmptyIterator;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.pretty.MessageHelper;
-import org.hibernate.type.CompositeType;
-import org.hibernate.type.IntegerType;
-import org.hibernate.type.LongType;
-import org.hibernate.type.PostgresUUIDType;
-import org.hibernate.type.StringType;
import org.hibernate.type.Type;
-import org.hibernate.type.UUIDBinaryType;
-import org.hibernate.type.UUIDCharType;
import org.jboss.logging.Logger;
import eu.etaxonomy.cdm.api.application.CdmApplicationRemoteConfiguration;
* @author Cherian Mathew
*/
public abstract class AbstractPersistentCollection implements Serializable, PersistentCollection {
-
+ private static final Logger log = Logger.getLogger( AbstractPersistentCollection.class );
+
/**
* <b>IMPORTANT:</b><br>
* This serialVersionUID must be kept in sync with the serialVersionUID which is generated
*/
private static final long serialVersionUID = 7094296207968006972L;
- private static final CoreMessageLogger LOG = CoreLogging.messageLogger( AbstractPersistentCollection.class );
-
private transient SessionImplementor session;
- private boolean isTempSession = false;
private boolean initialized;
private transient List<DelayedOperation> operationQueue;
private transient boolean directlyAccessible;
private Serializable storedSnapshot;
private String sessionFactoryUuid;
- private boolean allowLoadOutsideTransaction;
-
- /**
- * Not called by Hibernate, but used by non-JDK serialization,
- * eg. SOAP libraries.
- */
- public AbstractPersistentCollection() {
- }
-
- protected AbstractPersistentCollection(SessionImplementor session) {
- this.session = session;
- }
+ private boolean specjLazyLoad = false;
@Override
- public final String getRole() {
+ public final String getRole() {
return role;
}
@Override
- public final Serializable getKey() {
+ public final Serializable getKey() {
return key;
}
@Override
- public final boolean isUnreferenced() {
+ public final boolean isUnreferenced() {
return role == null;
}
@Override
- public final boolean isDirty() {
+ public final boolean isDirty() {
return dirty;
}
@Override
- public final void clearDirty() {
+ public final void clearDirty() {
dirty = false;
}
@Override
- public final void dirty() {
+ public final void dirty() {
dirty = true;
}
@Override
- public final Serializable getStoredSnapshot() {
+ public final Serializable getStoredSnapshot() {
return storedSnapshot;
}
//Careful: these methods do not initialize the collection.
+ /**
+ * Is the initialized collection empty?
+ */
@Override
- public abstract boolean empty();
+ public abstract boolean empty();
/**
* Called by any read-only method of the collection interface
return true;
}
else {
- final boolean isExtraLazy = withTemporarySessionIfNeeded(
- new LazyInitializationWork<Boolean>() {
- @Override
- public Boolean doWork() {
- final CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
-
- if ( entry != null ) {
- final CollectionPersister persister = entry.getLoadedPersister();
- if ( persister.isExtraLazy() ) {
- if ( hasQueuedOperations() ) {
- session.flush();
+ // In remoting we are sure that session is null
+ // both when using property paths and switching off conversations
+ if(session == null && remoting) {
+ log.info("--> readSize, of " + getRole() + " with key " + getKey());
+ read();
+ } else {
+ boolean isExtraLazy = withTemporarySessionIfNeeded(
+ new LazyInitializationWork<Boolean>() {
+ @Override
+ public Boolean doWork() {
+ CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
+
+ if ( entry != null ) {
+ CollectionPersister persister = entry.getLoadedPersister();
+ if ( persister.isExtraLazy() ) {
+ if ( hasQueuedOperations() ) {
+ session.flush();
+ }
+ cachedSize = persister.getSize( entry.getLoadedKey(), session );
+ return true;
+ }
+ else {
+ read();
}
- cachedSize = persister.getSize( entry.getLoadedKey(), session );
- return true;
}
- else {
- read();
+ else{
+ throwLazyInitializationExceptionIfNotConnected();
}
+ return false;
}
- else{
- throwLazyInitializationExceptionIfNotConnected();
- }
- return false;
}
- }
- );
- if ( isExtraLazy ) {
- return true;
+ );
+ if ( isExtraLazy ) {
+ return true;
+ }
}
}
}
return false;
}
- /**
- * TBH not sure why this is public
- *
- * @param <T> The java type of the return for this LazyInitializationWork
- */
public static interface LazyInitializationWork<T> {
- /**
- * Do the represented work and return the result.
- *
- * @return The result
- */
public T doWork();
}
private <T> T withTemporarySessionIfNeeded(LazyInitializationWork<T> lazyInitializationWork) {
- SessionImplementor tempSession = null;
+ SessionImplementor originalSession = null;
+ boolean isTempSession = false;
+ boolean isJTA = false;
if ( session == null ) {
- if ( allowLoadOutsideTransaction ) {
- tempSession = openTemporarySessionForLoading();
+ if ( specjLazyLoad ) {
+ session = openTemporarySessionForLoading();
+ isTempSession = true;
}
else {
throwLazyInitializationException( "could not initialize proxy - no Session" );
}
}
else if ( !session.isOpen() ) {
- if ( allowLoadOutsideTransaction ) {
- tempSession = openTemporarySessionForLoading();
+ if ( specjLazyLoad ) {
+ originalSession = session;
+ session = openTemporarySessionForLoading();
+ isTempSession = true;
}
else {
throwLazyInitializationException( "could not initialize proxy - the owning Session was closed" );
}
}
else if ( !session.isConnected() ) {
- if ( allowLoadOutsideTransaction ) {
- tempSession = openTemporarySessionForLoading();
+ if ( specjLazyLoad ) {
+ originalSession = session;
+ session = openTemporarySessionForLoading();
+ isTempSession = true;
}
else {
throwLazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
}
}
-
- SessionImplementor originalSession = null;
- boolean isJTA = false;
-
- if ( tempSession != null ) {
- isTempSession = true;
- originalSession = session;
- session = tempSession;
-
-
+ if ( isTempSession ) {
+ // TODO: On the next major release, add an
+ // 'isJTA' or 'getTransactionFactory' method to Session.
+ /*isJTA = session.getTransactionCoordinator()
+ .getTransactionContext().getTransactionEnvironment()
+ .getTransactionFactory()
+ .compatibleWithJtaSynchronization();*/
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();
+ ( ( Session) session ).beginTransaction();
}
session.getPersistenceContext().addUninitializedDetachedCollection(
return lazyInitializationWork.doWork();
}
finally {
- if ( tempSession != null ) {
+ if ( isTempSession ) {
// make sure the just opened temp session gets closed!
- isTempSession = false;
- session = originalSession;
-
try {
if ( !isJTA ) {
- ( (Session) tempSession ).getTransaction().commit();
+ ( ( Session) session ).getTransaction().commit();
}
- ( (Session) tempSession ).close();
+ ( (Session) session ).close();
}
catch (Exception e) {
- LOG.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
+ log.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
}
+ session = originalSession;
}
}
}
throwLazyInitializationException( "SessionFactory UUID not known to create temporary Session for loading" );
}
- final SessionFactoryImplementor sf = (SessionFactoryImplementor)
+ SessionFactoryImplementor sf = (SessionFactoryImplementor)
SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid );
- final SessionImplementor session = (SessionImplementor) sf.openSession();
- session.getPersistenceContext().setDefaultReadOnly( true );
- session.setFlushMode( FlushMode.MANUAL );
- return session;
+ return (SessionImplementor) sf.openSession();
}
protected Boolean readIndexExistence(final Object index) {
if ( !initialized ) {
- final Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
- new LazyInitializationWork<Boolean>() {
- @Override
- public Boolean doWork() {
- final CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
- final CollectionPersister persister = entry.getLoadedPersister();
- if ( persister.isExtraLazy() ) {
- if ( hasQueuedOperations() ) {
- session.flush();
+ // In remoting we are sure that session is null
+ // both when using property paths and switching off conversations
+ if(session == null && remoting) {
+ log.info("--> readIndexExistence, of " + getRole() + " with key " + getKey());
+ read();
+ } else {
+ Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
+ new LazyInitializationWork<Boolean>() {
+ @Override
+ public Boolean doWork() {
+ CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
+ CollectionPersister persister = entry.getLoadedPersister();
+ if ( persister.isExtraLazy() ) {
+ if ( hasQueuedOperations() ) {
+ session.flush();
+ }
+ return persister.indexExists( entry.getLoadedKey(), index, session );
}
- return persister.indexExists( entry.getLoadedKey(), index, session );
- }
- else {
- read();
+ else {
+ read();
+ }
+ return null;
}
- return null;
}
- }
- );
- if ( extraLazyExistenceCheck != null ) {
- return extraLazyExistenceCheck;
+ );
+ if ( extraLazyExistenceCheck != null ) {
+ return extraLazyExistenceCheck;
+ }
}
}
return null;
protected Boolean readElementExistence(final Object element) {
if ( !initialized ) {
- final Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
- new LazyInitializationWork<Boolean>() {
- @Override
- public Boolean doWork() {
- final CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
- final CollectionPersister persister = entry.getLoadedPersister();
- if ( persister.isExtraLazy() ) {
- if ( hasQueuedOperations() ) {
- session.flush();
+ // In remoting we are sure that session is null
+ // both when using property paths and switching off conversations
+ if(session == null && remoting) {
+ log.info("--> readElementExistence, of " + getRole() + " with key " + getKey());
+ read();
+
+ } else {
+ Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
+ new LazyInitializationWork<Boolean>() {
+ @Override
+ public Boolean doWork() {
+ CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
+ CollectionPersister persister = entry.getLoadedPersister();
+ if ( persister.isExtraLazy() ) {
+ if ( hasQueuedOperations() ) {
+ session.flush();
+ }
+ return persister.elementExists( entry.getLoadedKey(), element, session );
}
- return persister.elementExists( entry.getLoadedKey(), element, session );
- }
- else {
- read();
+ else {
+ read();
+ }
+ return null;
}
- return null;
}
- }
- );
- if ( extraLazyExistenceCheck != null ) {
- return extraLazyExistenceCheck;
+ );
+ if ( extraLazyExistenceCheck != null ) {
+ return extraLazyExistenceCheck;
+ }
}
}
return null;
protected Object readElementByIndex(final Object index) {
if ( !initialized ) {
- class ExtraLazyElementByIndexReader implements LazyInitializationWork {
- private boolean isExtraLazy;
- private Object element;
+ // In remoting we are sure that session is null
+ // both when using property paths and switching off conversations
+ if(session == null && remoting) {
+ log.info("--> readElementByIndex, of " + getRole() + " with key " + getKey());
+ read();
- @Override
- public Object doWork() {
- final CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
- final CollectionPersister persister = entry.getLoadedPersister();
- isExtraLazy = persister.isExtraLazy();
- if ( isExtraLazy ) {
- if ( hasQueuedOperations() ) {
- session.flush();
+ } else {
+ class ExtraLazyElementByIndexReader implements LazyInitializationWork {
+ private boolean isExtraLazy;
+ private Object element;
+
+ @Override
+ public Object doWork() {
+ CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
+ CollectionPersister persister = entry.getLoadedPersister();
+ isExtraLazy = persister.isExtraLazy();
+ if ( isExtraLazy ) {
+ if ( hasQueuedOperations() ) {
+ session.flush();
+ }
+ element = persister.getElementByIndex( entry.getLoadedKey(), index, session, owner );
}
- element = persister.getElementByIndex( entry.getLoadedKey(), index, session, owner );
- }
- else {
- read();
+ else {
+ read();
+ }
+ return null;
}
- return null;
}
- }
- final ExtraLazyElementByIndexReader reader = new ExtraLazyElementByIndexReader();
- //noinspection unchecked
- withTemporarySessionIfNeeded( reader );
- if ( reader.isExtraLazy ) {
- return reader.element;
+ ExtraLazyElementByIndexReader reader = new ExtraLazyElementByIndexReader();
+ //noinspection unchecked
+ withTemporarySessionIfNeeded( reader );
+ if ( reader.isExtraLazy ) {
+ return reader.element;
+ }
}
}
return UNKNOWN;
return cachedSize;
}
- protected boolean isConnectedToSession() {
- return session != null
- && session.isOpen()
- && session.getPersistenceContext().containsCollection( this );
- }
-
- protected boolean isInitialized() {
- return initialized;
+ private boolean isConnectedToSession() {
+ return session != null &&
+ session.isOpen() &&
+ session.getPersistenceContext().containsCollection( this );
}
/**
*/
@SuppressWarnings({"JavaDoc"})
protected boolean isOperationQueueEnabled() {
- return !initialized
- && isConnectedToSession()
- && isInverseCollection();
+ return !initialized &&
+ isConnectedToSession() &&
+ isInverseCollection();
}
/**
*/
@SuppressWarnings({"JavaDoc"})
protected boolean isPutQueueEnabled() {
- return !initialized
- && isConnectedToSession()
- && isInverseOneToManyOrNoOrphanDelete();
+ return !initialized &&
+ isConnectedToSession() &&
+ isInverseOneToManyOrNoOrphanDelete();
}
/**
*/
@SuppressWarnings({"JavaDoc"})
protected boolean isClearQueueEnabled() {
- return !initialized
- && isConnectedToSession()
- && isInverseCollectionNoOrphanDelete();
+ return !initialized &&
+ isConnectedToSession() &&
+ isInverseCollectionNoOrphanDelete();
}
/**
* Is this the "inverse" end of a bidirectional association?
*/
@SuppressWarnings({"JavaDoc"})
- protected boolean isInverseCollection() {
- final CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
+ private boolean isInverseCollection() {
+ CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
return ce != null && ce.getLoadedPersister().isInverse();
}
* no orphan delete enabled?
*/
@SuppressWarnings({"JavaDoc"})
- protected boolean isInverseCollectionNoOrphanDelete() {
- final CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
- return ce != null
- &&
+ private boolean isInverseCollectionNoOrphanDelete() {
+ CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
+ return ce != null &&
ce.getLoadedPersister().isInverse() &&
!ce.getLoadedPersister().hasOrphanDelete();
}
* of a collection with no orphan delete?
*/
@SuppressWarnings({"JavaDoc"})
- protected boolean isInverseOneToManyOrNoOrphanDelete() {
- final CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
- return ce != null
- && ce.getLoadedPersister().isInverse()
- && ( ce.getLoadedPersister().isOneToMany() || !ce.getLoadedPersister().hasOrphanDelete() );
+ private boolean isInverseOneToManyOrNoOrphanDelete() {
+ CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
+ return ce != null && ce.getLoadedPersister().isInverse() && (
+ ce.getLoadedPersister().isOneToMany() ||
+ !ce.getLoadedPersister().hasOrphanDelete()
+ );
}
/**
operationQueue = new ArrayList<DelayedOperation>( 10 );
}
operationQueue.add( operation );
- //needed so that we remove this collection from the second-level cache
- dirty = true;
- }
-
- /**
- * Replace entity instances with copy in {@code copyCache}/.
- *
- * @param copyCache - mapping from entity in the process of being
- * merged to managed copy.
- */
- public final void replaceQueuedOperationValues(CollectionPersister persister, Map copyCache) {
- for ( DelayedOperation operation : operationQueue ) {
- if ( ValueDelayedOperation.class.isInstance( operation ) ) {
- ( (ValueDelayedOperation) operation ).replace( persister, copyCache );
- }
- }
+ dirty = true; //needed so that we remove this collection from the second-level cache
}
/**
}
}
+ /**
+ * After flushing, re-init snapshot state.
+ */
@Override
- public void setSnapshot(Serializable key, String role, Serializable snapshot) {
+ public void setSnapshot(Serializable key, String role, Serializable snapshot) {
this.key = key;
this.role = role;
this.storedSnapshot = snapshot;
}
+ /**
+ * After flushing, clear any "queued" additions, since the
+ * database state is now synchronized with the memory state.
+ */
@Override
- public void postAction() {
+ public void postAction() {
operationQueue = null;
cachedSize = -1;
clearDirty();
}
+ /**
+ * Not called by Hibernate, but used by non-JDK serialization,
+ * eg. SOAP libraries.
+ */
+ public AbstractPersistentCollection() {
+ }
+
+ protected AbstractPersistentCollection(SessionImplementor session) {
+ this.session = session;
+ }
+
+ /**
+ * return the user-visible collection (or array) instance
+ */
@Override
- public Object getValue() {
+ public Object getValue() {
return this;
}
+ /**
+ * Called just before reading any rows from the JDBC result set
+ */
@Override
- public void beginRead() {
+ public void beginRead() {
// override on some subclasses
initializing = true;
}
+ /**
+ * Called after reading all rows from the JDBC result set
+ */
@Override
- public boolean endRead() {
+ public boolean endRead() {
//override on some subclasses
return afterInitialize();
}
@Override
- public boolean afterInitialize() {
+ public boolean afterInitialize() {
setInitialized();
//do this bit after setting initialized to true or it will recurse
if ( operationQueue != null ) {
* @throws LazyInitializationException if we cannot initialize
*/
protected final void initialize(final boolean writing) {
- if ( initialized ) {
- return;
- }
-
- withTemporarySessionIfNeeded(
- new LazyInitializationWork<Object>() {
- @Override
- public Object doWork() {
- session.initializeCollection( AbstractPersistentCollection.this, writing );
- return null;
- }
- }
- );
+ if ( initialized ) {
+ return;
+ }
+
+ // In remoting we are sure that session is null
+ // both when using property paths and switching off conversations
+ if(session == null && remoting) {
+ remoteInitialize();
+ } else {
+ withTemporarySessionIfNeeded(
+ new LazyInitializationWork<Object>() {
+ @Override
+ public Object doWork() {
+ session.initializeCollection( AbstractPersistentCollection.this, writing );
+ return null;
+ }
+ }
+ );
+ }
}
private void throwLazyInitializationExceptionIfNotConnected() {
this.directlyAccessible = directlyAccessible;
}
+ /**
+ * Could the application possibly have a direct reference to
+ * the underlying collection implementation?
+ */
@Override
- public boolean isDirectlyAccessible() {
+ public boolean isDirectlyAccessible() {
return directlyAccessible;
}
+ /**
+ * Disassociate this collection from the given session.
+ *
+ * @return true if this was currently associated with the given session
+ */
@Override
- public final boolean unsetSession(SessionImplementor currentSession) {
- prepareForPossibleLoadingOutsideTransaction();
+ public final boolean unsetSession(SessionImplementor currentSession) {
+ prepareForPossibleSpecialSpecjInitialization();
if ( currentSession == this.session ) {
- if ( !isTempSession ) {
- this.session = null;
- }
+ this.session = null;
return true;
}
else {
- if ( this.session != null ) {
- LOG.logCannotUnsetUnexpectedSessionInCollection( generateUnexpectedSessionStateMessage( currentSession ) );
- }
return false;
}
}
- protected void prepareForPossibleLoadingOutsideTransaction() {
+ protected void prepareForPossibleSpecialSpecjInitialization() {
if ( session != null ) {
- allowLoadOutsideTransaction = session.getFactory().getSessionFactoryOptions().isInitializeLazyStateOutsideTransactionsEnabled();
+ specjLazyLoad = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled();
- if ( allowLoadOutsideTransaction && sessionFactoryUuid == null ) {
- sessionFactoryUuid = session.getFactory().getUuid();
+ if ( specjLazyLoad && sessionFactoryUuid == null ) {
+ try {
+ sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent();
+ }
+ catch (NamingException e) {
+ //not much we can do if this fails...
+ }
}
}
}
+
+ /**
+ * Associate the collection with the given session.
+ *
+ * @return false if the collection was already associated with the session
+ *
+ * @throws HibernateException if the collection was already associated
+ * with another open session
+ */
@Override
- public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
+ public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
if ( session == this.session ) {
return false;
}
else {
- if ( this.session != null ) {
- final String msg = generateUnexpectedSessionStateMessage( session );
- if ( isConnectedToSession() ) {
+ if ( isConnectedToSession() ) {
+ CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
+ if ( ce == null ) {
throw new HibernateException(
- "Illegal attempt to associate a collection with two open sessions. " + msg
+ "Illegal attempt to associate a collection with two open sessions"
);
}
else {
- LOG.logUnexpectedSessionInCollectionNotConnected( msg );
- this.session = session;
- return true;
+ throw new HibernateException(
+ "Illegal attempt to associate a collection with two open sessions: " +
+ MessageHelper.collectionInfoString(
+ ce.getLoadedPersister(), this,
+ ce.getLoadedKey(), session
+ )
+ );
}
}
else {
}
}
- private String generateUnexpectedSessionStateMessage(SessionImplementor session) {
- // NOTE: If this.session != null, this.session may be operating on this collection
- // (e.g., by changing this.role, this.key, or even this.session) in a different thread.
-
- // Grab the current role and key (it can still get changed by this.session...)
- // If this collection is connected to this.session, then this.role and this.key should
- // be consistent with the CollectionEntry in this.session (as long as this.session doesn't
- // change it). Don't access the CollectionEntry in this.session because that could result
- // in multi-threaded access to this.session.
- final String roleCurrent = role;
- final Serializable keyCurrent = key;
-
- final StringBuilder sb = new StringBuilder( "Collection : " );
- if ( roleCurrent != null ) {
- sb.append( MessageHelper.collectionInfoString( roleCurrent, keyCurrent ) );
- }
- else {
- final CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
- if ( ce != null ) {
- sb.append(
- MessageHelper.collectionInfoString(
- ce.getLoadedPersister(),
- this,
- ce.getLoadedKey(),
- session
- )
- );
- }
- else {
- sb.append( "<unknown>" );
- }
- }
- // only include the collection contents if debug logging
- if ( LOG.isDebugEnabled() ) {
- final String collectionContents = wasInitialized() ? toString() : "<uninitialized>";
- sb.append( "\nCollection contents: [" ).append( collectionContents ).append( "]" );
- }
- return sb.toString();
- }
-
+ /**
+ * Do we need to completely recreate this collection when it changes?
+ */
@Override
- public boolean needsRecreate(CollectionPersister persister) {
- // Workaround for situations like HHH-7072. If the collection element is a component that consists entirely
- // of nullable properties, we currently have to forcefully recreate the entire collection. See the use
- // of hasNotNullableColumns in the AbstractCollectionPersister constructor for more info. In order to delete
- // row-by-row, that would require SQL like "WHERE ( COL = ? OR ( COL is null AND ? is null ) )", rather than
- // the current "WHERE COL = ?" (fails for null for most DBs). Note that
- // the param would have to be bound twice. Until we eventually add "parameter bind points" concepts to the
- // AST in ORM 5+, handling this type of condition is either extremely difficult or impossible. Forcing
- // recreation isn't ideal, but not really any other option in ORM 4.
- // Selecting a type used in where part of update statement
- // (must match condidion in org.hibernate.persister.collection.BasicCollectionPersister.doUpdateRows).
- // See HHH-9474
- Type whereType;
- if ( persister.hasIndex() ) {
- whereType = persister.getIndexType();
- }
- else {
- whereType = persister.getElementType();
- }
- if ( whereType instanceof CompositeType ) {
- CompositeType componentIndexType = (CompositeType) whereType;
- return !componentIndexType.hasNotNullProperty();
- }
+ public boolean needsRecreate(CollectionPersister persister) {
return false;
}
+ /**
+ * To be called internally by the session, forcing
+ * immediate initialization.
+ */
@Override
- public final void forceInitialization() throws HibernateException {
+ public final void forceInitialization() throws HibernateException {
if ( !initialized ) {
if ( initializing ) {
throw new AssertionFailure( "force initialize loading collection" );
}
- initialize( false );
+ if ( session == null ) {
+ throw new HibernateException( "collection is not associated with any session" );
+ }
+ if ( !session.isConnected() ) {
+ throw new HibernateException( "disconnected session" );
+ }
+ session.initializeCollection( this, false );
}
}
return session.getPersistenceContext().getSnapshot( this );
}
+ /**
+ * Is this instance initialized?
+ */
@Override
- public final boolean wasInitialized() {
+ public final boolean wasInitialized() {
return initialized;
}
@Override
- public boolean isRowUpdatePossible() {
+ public boolean isRowUpdatePossible() {
return true;
}
+ /**
+ * Does this instance have any "queued" additions?
+ */
@Override
- public final boolean hasQueuedOperations() {
+ public final boolean hasQueuedOperations() {
return operationQueue != null;
}
+ /**
+ * Iterate the "queued" additions
+ */
@Override
- public final Iterator queuedAdditionIterator() {
+ public final Iterator queuedAdditionIterator() {
if ( hasQueuedOperations() ) {
return new Iterator() {
- private int index;
+ int i = 0;
@Override
- public Object next() {
- return operationQueue.get( index++ ).getAddedInstance();
+ public Object next() {
+ return operationQueue.get( i++ ).getAddedInstance();
}
@Override
- public boolean hasNext() {
- return index < operationQueue.size();
+ public boolean hasNext() {
+ return i < operationQueue.size();
}
@Override
- public void remove() {
+ public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
+ /**
+ * Iterate the "queued" additions
+ */
@Override
- @SuppressWarnings({"unchecked"})
+ @SuppressWarnings({"unchecked"})
public final Collection getQueuedOrphans(String entityName) {
if ( hasQueuedOperations() ) {
- final Collection additions = new ArrayList( operationQueue.size() );
- final Collection removals = new ArrayList( operationQueue.size() );
+ Collection additions = new ArrayList( operationQueue.size() );
+ Collection removals = new ArrayList( operationQueue.size() );
for ( DelayedOperation operation : operationQueue ) {
additions.add( operation.getAddedInstance() );
removals.add( operation.getOrphan() );
}
}
+ /**
+ * Called before inserting rows, to ensure that any surrogate keys
+ * are fully generated
+ */
@Override
- public void preInsert(CollectionPersister persister) throws HibernateException {
+ public void preInsert(CollectionPersister persister) throws HibernateException {
}
+ /**
+ * Called after inserting a row, to fetch the natively generated id
+ */
@Override
- public void afterRowInsert(CollectionPersister persister, Object entry, int i) throws HibernateException {
+ public void afterRowInsert(CollectionPersister persister, Object entry, int i) throws HibernateException {
}
+ /**
+ * get all "orphaned" elements
+ */
@Override
- public abstract Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException;
+ public abstract Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException;
/**
- * Get the session currently associated with this collection.
- *
- * @return The session
+ * Get the current session
*/
+ @SuppressWarnings({"JavaDoc"})
public final SessionImplementor getSession() {
return session;
}
}
@Override
- public boolean hasNext() {
+ public boolean hasNext() {
return itr.hasNext();
}
@Override
- public Object next() {
+ public Object next() {
return itr.next();
}
@Override
- public void remove() {
+ public void remove() {
write();
itr.remove();
}
+
}
protected final class ListIteratorProxy implements ListIterator {
}
@Override
- @SuppressWarnings({"unchecked"})
+ @SuppressWarnings({"unchecked"})
public void add(Object o) {
write();
itr.add( o );
}
@Override
- public boolean hasNext() {
+ public boolean hasNext() {
return itr.hasNext();
}
@Override
- public boolean hasPrevious() {
+ public boolean hasPrevious() {
return itr.hasPrevious();
}
@Override
- public Object next() {
+ public Object next() {
return itr.next();
}
@Override
- public int nextIndex() {
+ public int nextIndex() {
return itr.nextIndex();
}
@Override
- public Object previous() {
+ public Object previous() {
return itr.previous();
}
@Override
- public int previousIndex() {
+ public int previousIndex() {
return itr.previousIndex();
}
@Override
- public void remove() {
+ public void remove() {
write();
itr.remove();
}
@Override
- @SuppressWarnings({"unchecked"})
+ @SuppressWarnings({"unchecked"})
public void set(Object o) {
write();
itr.set( o );
}
+
}
protected class SetProxy implements java.util.Set {
}
@Override
- @SuppressWarnings({"unchecked"})
+ @SuppressWarnings({"unchecked"})
public boolean add(Object o) {
write();
return set.add( o );
}
@Override
- @SuppressWarnings({"unchecked"})
+ @SuppressWarnings({"unchecked"})
public boolean addAll(Collection c) {
write();
return set.addAll( c );
}
@Override
- public void clear() {
+ public void clear() {
write();
set.clear();
}
@Override
- public boolean contains(Object o) {
+ public boolean contains(Object o) {
return set.contains( o );
}
@Override
- @SuppressWarnings("unchecked")
- public boolean containsAll(Collection c) {
+ public boolean containsAll(Collection c) {
return set.containsAll( c );
}
@Override
- public boolean isEmpty() {
+ public boolean isEmpty() {
return set.isEmpty();
}
@Override
- public Iterator iterator() {
+ public Iterator iterator() {
return new IteratorProxy( set.iterator() );
}
@Override
- public boolean remove(Object o) {
+ public boolean remove(Object o) {
write();
return set.remove( o );
}
@Override
- @SuppressWarnings("unchecked")
- public boolean removeAll(Collection c) {
+ public boolean removeAll(Collection c) {
write();
return set.removeAll( c );
}
@Override
- @SuppressWarnings("unchecked")
- public boolean retainAll(Collection c) {
+ public boolean retainAll(Collection c) {
write();
return set.retainAll( c );
}
@Override
- public int size() {
+ public int size() {
return set.size();
}
@Override
- public Object[] toArray() {
+ public Object[] toArray() {
return set.toArray();
}
@Override
- @SuppressWarnings({"unchecked"})
+ @SuppressWarnings({"unchecked"})
public Object[] toArray(Object[] array) {
return set.toArray( array );
}
+
}
protected final class ListProxy implements java.util.List {
}
@Override
- @SuppressWarnings("unchecked")
public boolean containsAll(Collection c) {
return list.containsAll( c );
}
}
@Override
- @SuppressWarnings("unchecked")
public boolean removeAll(Collection c) {
write();
return list.removeAll( c );
}
@Override
- @SuppressWarnings("unchecked")
public boolean retainAll(Collection c) {
write();
return list.retainAll( c );
public Object getOrphan();
}
- protected interface ValueDelayedOperation extends DelayedOperation {
- void replace(CollectionPersister collectionPersister, Map copyCache);
- }
-
- protected abstract class AbstractValueDelayedOperation implements ValueDelayedOperation {
- private Object addedValue;
- private Object orphan;
-
- protected AbstractValueDelayedOperation(Object addedValue, Object orphan) {
- this.addedValue = addedValue;
- this.orphan = orphan;
- }
-
- public void replace(CollectionPersister persister, Map copyCache) {
- if ( addedValue != null ) {
- addedValue = getReplacement( persister.getElementType(), addedValue, copyCache );
- }
- }
-
- protected final Object getReplacement(Type type, Object current, Map copyCache) {
- return type.replace( current, null, session, owner, copyCache );
- }
-
- @Override
- public final Object getAddedInstance() {
- return addedValue;
- }
-
- @Override
- public final Object getOrphan() {
- return orphan;
- }
- }
-
/**
* Given a collection of entity instances that used to
* belong to the collection, and a collection of instances
// short-circuit(s)
if ( currentElements.size() == 0 ) {
- // no new elements, the old list contains only Orphans
- return oldElements;
+ return oldElements; // no new elements, the old list contains only Orphans
}
if ( oldElements.size() == 0 ) {
- // no old elements, so no Orphans neither
- return oldElements;
+ return oldElements; // no old elements, so no Orphans neither
}
final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
final Type idType = entityPersister.getIdentifierType();
- final boolean useIdDirect = mayUseIdDirect( idType );
// create the collection holding the Orphans
- final Collection res = new ArrayList();
+ Collection res = new ArrayList();
// collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access
- final java.util.Set currentIds = new HashSet();
- final java.util.Set currentSaving = new IdentitySet();
+ java.util.Set currentIds = new HashSet();
+ java.util.Set currentSaving = new IdentitySet();
for ( Object current : currentElements ) {
if ( current != null && ForeignKeys.isNotTransient( entityName, current, null, session ) ) {
- final EntityEntry ee = session.getPersistenceContext().getEntry( current );
+ EntityEntry ee = session.getPersistenceContext().getEntry( current );
if ( ee != null && ee.getStatus() == Status.SAVING ) {
currentSaving.add( current );
}
else {
- final Serializable currentId = ForeignKeys.getEntityIdentifierIfNotUnsaved(
+ Serializable currentId = ForeignKeys.getEntityIdentifierIfNotUnsaved(
entityName,
current,
session
);
- currentIds.add( useIdDirect ? currentId : new TypedValue( idType, currentId ) );
+ currentIds.add( new TypedValue( idType, currentId, entityPersister.getEntityMode() ) );
}
}
}
// iterate over the *old* list
for ( Object old : oldElements ) {
if ( !currentSaving.contains( old ) ) {
- final Serializable oldId = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, old, session );
- if ( !currentIds.contains( useIdDirect ? oldId : new TypedValue( idType, oldId ) ) ) {
+ Serializable oldId = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, old, session );
+ if ( !currentIds.contains( new TypedValue( idType, oldId, entityPersister.getEntityMode() ) ) ) {
res.add( old );
}
}
return res;
}
- private static boolean mayUseIdDirect(Type idType) {
- return idType == StringType.INSTANCE
- || idType == IntegerType.INSTANCE
- || idType == LongType.INSTANCE
- || idType == UUIDBinaryType.INSTANCE
- || idType == UUIDCharType.INSTANCE
- || idType == PostgresUUIDType.INSTANCE;
- }
-
- /**
- * Removes entity entries that have an equal identifier with the incoming entity instance
- *
- * @param list The list containing the entity instances
- * @param entityInstance The entity instance to match elements.
- * @param entityName The entity name
- * @param session The session
- */
public static void identityRemove(
Collection list,
- Object entityInstance,
+ Object object,
String entityName,
- SessionImplementor session) {
+ SessionImplementor session) throws HibernateException {
- if ( entityInstance != null && ForeignKeys.isNotTransient( entityName, entityInstance, null, session ) ) {
+ if ( object != null && ForeignKeys.isNotTransient( entityName, object, null, session ) ) {
final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
- final Type idType = entityPersister.getIdentifierType();
+ Type idType = entityPersister.getIdentifierType();
- final Serializable idOfCurrent = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, entityInstance, session );
- final Iterator itr = list.iterator();
+ Serializable idOfCurrent = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, object, session );
+ Iterator itr = list.iterator();
while ( itr.hasNext() ) {
- final Serializable idOfOld = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, itr.next(), session );
+ Serializable idOfOld = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, itr.next(), session );
if ( idType.isEqual( idOfCurrent, idOfOld, session.getFactory() ) ) {
itr.remove();
break;
}
@Override
- public Object getIdentifier(Object entry, int i) {
+ public Object getIdentifier(Object entry, int i) {
throw new UnsupportedOperationException();
}
@Override
- public Object getOwner() {
+ public Object getOwner() {
return owner;
}
@Override
- public void setOwner(Object owner) {
+ public void setOwner(Object owner) {
this.owner = owner;
}
configuration = conf;
}
-
private void remoteInitialize() {
if (getOwner() != null && !initialized) {
try {
String role = getRole();
String fieldName = role.substring(role.lastIndexOf(".") + 1);
- LOG.info("--> Remote Lazy Initializing Collection " + getRole() + " , owner : " + getOwner().getClass() + "/" + getKey() + " , field : " + fieldName);
+ log.info("--> Remote Lazy Initializing Collection " + getRole() + " , owner : " + getOwner().getClass() + "/" + getKey() + " , field : " + fieldName);
Object owner = getOwner();
CdmBase cdmBase;
if(owner instanceof CdmBase) {