org.hibernate.collection.AbstractPersistentCollection Maven / Gradle / Ivy
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.collection;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.LazyInitializationException;
import org.hibernate.engine.CollectionEntry;
import org.hibernate.engine.EntityEntry;
import org.hibernate.engine.ForeignKeys;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.engine.Status;
import org.hibernate.engine.TypedValue;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.type.Type;
import org.hibernate.util.CollectionHelper;
import org.hibernate.util.EmptyIterator;
import org.hibernate.util.IdentitySet;
import org.hibernate.util.MarkerObject;
/**
* Base class implementing {@link PersistentCollection}
*
* @author Gavin King
*/
public abstract class AbstractPersistentCollection implements Serializable, PersistentCollection {
private transient SessionImplementor session;
private boolean initialized;
private transient List operationQueue;
private transient boolean directlyAccessible;
private transient boolean initializing;
private Object owner;
private int cachedSize = -1;
private String role;
private Serializable key;
// collections detect changes made via their public interface and mark
// themselves as dirty as a performance optimization
private boolean dirty;
private Serializable storedSnapshot;
public final String getRole() {
return role;
}
public final Serializable getKey() {
return key;
}
public final boolean isUnreferenced() {
return role==null;
}
public final boolean isDirty() {
return dirty;
}
public final void clearDirty() {
dirty = false;
}
public final void dirty() {
dirty = true;
}
public final Serializable getStoredSnapshot() {
return storedSnapshot;
}
//Careful: these methods do not initialize the collection.
/**
* Is the initialized collection empty?
*/
public abstract boolean empty();
/**
* Called by any read-only method of the collection interface
*/
protected final void read() {
initialize(false);
}
/**
* Called by the size() method
*/
protected boolean readSize() {
if (!initialized) {
if ( cachedSize!=-1 && !hasQueuedOperations() ) {
return true;
}
else {
throwLazyInitializationExceptionIfNotConnected();
CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this);
CollectionPersister persister = entry.getLoadedPersister();
if ( persister.isExtraLazy() ) {
if ( hasQueuedOperations() ) {
session.flush();
}
cachedSize = persister.getSize( entry.getLoadedKey(), session );
return true;
}
}
}
read();
return false;
}
protected Boolean readIndexExistence(Object index) {
if (!initialized) {
throwLazyInitializationExceptionIfNotConnected();
CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this);
CollectionPersister persister = entry.getLoadedPersister();
if ( persister.isExtraLazy() ) {
if ( hasQueuedOperations() ) {
session.flush();
}
return new Boolean( persister.indexExists( entry.getLoadedKey(), index, session ) );
}
}
read();
return null;
}
protected Boolean readElementExistence(Object element) {
if (!initialized) {
throwLazyInitializationExceptionIfNotConnected();
CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this);
CollectionPersister persister = entry.getLoadedPersister();
if ( persister.isExtraLazy() ) {
if ( hasQueuedOperations() ) {
session.flush();
}
return new Boolean( persister.elementExists( entry.getLoadedKey(), element, session ) );
}
}
read();
return null;
}
protected static final Object UNKNOWN = new MarkerObject("UNKNOWN");
protected Object readElementByIndex(Object index) {
if (!initialized) {
throwLazyInitializationExceptionIfNotConnected();
CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this);
CollectionPersister persister = entry.getLoadedPersister();
if ( persister.isExtraLazy() ) {
if ( hasQueuedOperations() ) {
session.flush();
}
return persister.getElementByIndex( entry.getLoadedKey(), index, session, owner );
}
}
read();
return UNKNOWN;
}
protected int getCachedSize() {
return cachedSize;
}
/**
* Is the collection currently connected to an open session?
*/
private final boolean isConnectedToSession() {
return session!=null &&
session.isOpen() &&
session.getPersistenceContext().containsCollection(this);
}
/**
* Called by any writer method of the collection interface
*/
protected final void write() {
initialize(true);
dirty();
}
/**
* Is this collection in a state that would allow us to
* "queue" operations?
*/
protected boolean isOperationQueueEnabled() {
return !initialized &&
isConnectedToSession() &&
isInverseCollection();
}
/**
* Is this collection in a state that would allow us to
* "queue" puts? This is a special case, because of orphan
* delete.
*/
protected boolean isPutQueueEnabled() {
return !initialized &&
isConnectedToSession() &&
isInverseOneToManyOrNoOrphanDelete();
}
/**
* Is this collection in a state that would allow us to
* "queue" clear? This is a special case, because of orphan
* delete.
*/
protected boolean isClearQueueEnabled() {
return !initialized &&
isConnectedToSession() &&
isInverseCollectionNoOrphanDelete();
}
/**
* Is this the "inverse" end of a bidirectional association?
*/
private boolean isInverseCollection() {
CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this);
return ce != null && ce.getLoadedPersister().isInverse();
}
/**
* Is this the "inverse" end of a bidirectional association with
* no orphan delete enabled?
*/
private boolean isInverseCollectionNoOrphanDelete() {
CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this);
return ce != null &&
ce.getLoadedPersister().isInverse() &&
!ce.getLoadedPersister().hasOrphanDelete();
}
/**
* Is this the "inverse" end of a bidirectional one-to-many, or
* of a collection with no orphan delete?
*/
private boolean isInverseOneToManyOrNoOrphanDelete() {
CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this);
return ce != null && ce.getLoadedPersister().isInverse() && (
ce.getLoadedPersister().isOneToMany() ||
!ce.getLoadedPersister().hasOrphanDelete()
);
}
/**
* Queue an addition
*/
protected final void queueOperation(Object element) {
if (operationQueue==null) operationQueue = new ArrayList(10);
operationQueue.add(element);
dirty = true; //needed so that we remove this collection from the second-level cache
}
/**
* After reading all existing elements from the database,
* add the queued elements to the underlying collection.
*/
protected final void performQueuedOperations() {
for ( int i=0; i