@Override public void execute() throws HibernateException { // this method is called when a new non-null collection is persisted // or when an existing (non-null) collection is moved to a new owner final PersistentCollection collection = getCollection(); preRecreate(); getPersister().recreate( collection, getKey(), getSession() ); getSession().getPersistenceContext().getCollectionEntry( collection ).afterAction( collection ); evict(); postRecreate(); if ( getSession().getFactory().getStatistics().isStatisticsEnabled() ) { getSession().getFactory().getStatisticsImplementor().recreateCollection( getPersister().getRole() ); } }
public void evictCollection(Object value, CollectionType type) { final Object pc; if ( type.hasHolder() ) { pc = getSession().getPersistenceContext().removeCollectionHolder(value); } else if ( value instanceof PersistentCollection ) { pc = value; } else { return; //EARLY EXIT! } PersistentCollection collection = (PersistentCollection) pc; if ( collection.unsetSession( getSession() ) ) evictCollection(collection); }
/** * Removes a persistent collection from its loaded owner. * * Use this constructor when the collection is non-null. * * @param collection The collection to to remove; must be non-null * @param persister The collection's persister * @param id The collection key * @param emptySnapshot Indicates if the snapshot is empty * @param session The session * * @throws AssertionFailure if collection is null. */ public CollectionRemoveAction( final PersistentCollection collection, final CollectionPersister persister, final Serializable id, final boolean emptySnapshot, final SessionImplementor session) { super( persister, collection, id, session ); if ( collection == null ) { throw new AssertionFailure("collection == null"); } this.emptySnapshot = emptySnapshot; // the loaded owner will be set to null after the collection is removed, // so capture its value as the affected owner so it is accessible to // both pre- and post- events this.affectedOwner = session.getPersistenceContext().getLoadedCollectionOwnerOrNull( collection ); }
/** * 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 }
@Override public void execute() throws HibernateException { preRemove(); if ( !emptySnapshot ) { // an existing collection that was either non-empty or uninitialized // is replaced by null or a different collection // (if the collection is uninitialized, hibernate has no way of // knowing if the collection is actually empty without querying the db) getPersister().remove( getKey(), getSession() ); } final PersistentCollection collection = getCollection(); if ( collection != null ) { getSession().getPersistenceContext().getCollectionEntry( collection ).afterAction( collection ); } evict(); postRemove(); if ( getSession().getFactory().getStatistics().isStatisticsEnabled() ) { getSession().getFactory().getStatisticsImplementor().removeCollection( getPersister().getRole() ); } }
private static void processNeverReferencedCollection(PersistentCollection coll, SessionImplementor session) throws HibernateException { final PersistenceContext persistenceContext = session.getPersistenceContext(); final CollectionEntry entry = persistenceContext.getCollectionEntry( coll ); if ( LOG.isDebugEnabled() ) { LOG.debugf( "Found collection with unloaded owner: %s", MessageHelper.collectionInfoString( entry.getLoadedPersister(), coll, entry.getLoadedKey(), session ) ); } entry.setCurrentPersister( entry.getLoadedPersister() ); entry.setCurrentKey( entry.getLoadedKey() ); prepareCollectionForUpdate( coll, entry, session.getFactory() ); }
/** * Add an collection to the cache, with a given collection entry. * * @param coll The collection for which we are adding an entry. * @param entry The entry representing the collection. * @param key The key of the collection's entry. */ private void addCollection(PersistentCollection coll, CollectionEntry entry, Serializable key) { collectionEntries.put( coll, entry ); final CollectionKey collectionKey = new CollectionKey( entry.getLoadedPersister(), key ); final PersistentCollection old = collectionsByKey.put( collectionKey, coll ); if ( old != null ) { if ( old == coll ) { throw new AssertionFailure( "bug adding collection twice" ); } // or should it actually throw an exception? old.unsetSession( session ); collectionEntries.remove( old ); // watch out for a case where old is still referenced // somewhere in the object graph! (which is a user error) } }
private void evictCollection(PersistentCollection collection) { CollectionEntry ce = (CollectionEntry) getSession().getPersistenceContext().getCollectionEntries().remove(collection); if ( LOG.isDebugEnabled() ) { LOG.debugf( "Evicting collection: %s", MessageHelper.collectionInfoString( ce.getLoadedPersister(), collection, ce.getLoadedKey(), getSession() ) ); } if (ce.getLoadedPersister() != null && ce.getLoadedPersister().getBatchSize() > 1) { getSession().getPersistenceContext().getBatchFetchQueue().removeBatchLoadableCollection(ce); } if ( ce.getLoadedPersister() != null && ce.getLoadedKey() != null ) { //TODO: is this 100% correct? getSession().getPersistenceContext().getCollectionsByKey().remove( new CollectionKey( ce.getLoadedPersister(), ce.getLoadedKey() ) ); } }
/** * Generate an info message string relating to a particular managed * collection. Attempts to intelligently handle property-refs issues * where the collection key is not the same as the owner key. * * @param persister The persister for the collection * @param collection The collection itself * @param collectionKey The collection key * @param session The session * @return An info string, in the form [Foo.bars#1] */ public static String collectionInfoString( CollectionPersister persister, PersistentCollection collection, Serializable collectionKey, SessionImplementor session ) { StringBuilder s = new StringBuilder(); s.append( '[' ); if ( persister == null ) { s.append( "<unreferenced>" ); } else { s.append( persister.getRole() ); s.append( '#' ); Type ownerIdentifierType = persister.getOwnerEntityPersister() .getIdentifierType(); Serializable ownerKey; // TODO: Is it redundant to attempt to use the collectionKey, // or is always using the owner id sufficient? if ( collectionKey.getClass().isAssignableFrom( ownerIdentifierType.getReturnedClass() ) ) { ownerKey = collectionKey; } else { ownerKey = session.getPersistenceContext() .getEntry( collection.getOwner() ).getId(); } s.append( ownerIdentifierType.toLoggableString( ownerKey, session.getFactory() ) ); } s.append( ']' ); return s.toString(); }
public PreCollectionRecreateEvent( CollectionPersister collectionPersister, PersistentCollection collection, EventSource source) { super( collectionPersister, collection, source, collection.getOwner(), getOwnerIdOrNull( collection.getOwner(), source ) ); }
/** * Constructs a CollectionUpdateAction * * @param collection The collection to update * @param persister The collection persister * @param id The collection key * @param emptySnapshot Indicates if the snapshot is empty * @param session The session */ public CollectionUpdateAction( final PersistentCollection collection, final CollectionPersister persister, final Serializable id, final boolean emptySnapshot, final SessionImplementor session) { super( persister, collection, id, session ); this.emptySnapshot = emptySnapshot; }
@Override public void processQueuedOps(PersistentCollection collection, Serializable key, SessionImplementor session) throws HibernateException { if ( collection.hasQueuedOperations() ) { doProcessQueuedOps( collection, key, session ); } }
/** * If an CollectionEntry represents a batch loadable collection, add * it to the queue. */ public void addBatchLoadableCollection(PersistentCollection collection, CollectionEntry ce) { final CollectionPersister persister = ce.getLoadedPersister(); LinkedHashMap<CollectionEntry, PersistentCollection> map = batchLoadableCollections.get( persister.getRole() ); if ( map == null ) { map = new LinkedHashMap<CollectionEntry, PersistentCollection>( 16 ); batchLoadableCollections.put( persister.getRole(), map ); } map.put( ce, collection ); }
/** * Has the owner of the collection changed since the collection * was snapshotted and detached? */ protected static boolean isOwnerUnchanged( final PersistentCollection snapshot, final CollectionPersister persister, final Serializable id ) { return isCollectionSnapshotValid(snapshot) && persister.getRole().equals( snapshot.getRole() ) && id.equals( snapshot.getKey() ); }
/** * 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.getCollectionPersister( collection.getRole() ) ); snapshot = collection.getStoredSnapshot(); }
/** * Determine if the collection is "really" dirty, by checking dirtiness * of the collection elements, if necessary */ private void dirty(PersistentCollection collection) throws HibernateException { boolean forceDirty = collection.wasInitialized() && !collection.isDirty() && //optimization getLoadedPersister() != null && getLoadedPersister().isMutable() && //optimization ( collection.isDirectlyAccessible() || getLoadedPersister().getElementType().isMutable() ) && //optimization !collection.equalsSnapshot( getLoadedPersister() ); if ( forceDirty ) { collection.dirty(); } }
public void preFlush(PersistentCollection collection) throws HibernateException { if ( loadedKey == null && collection.getKey() != null ) { loadedKey = collection.getKey(); } 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); if ( LOG.isDebugEnabled() && collection.isDirty() && getLoadedPersister() != null ) { LOG.debugf( "Collection dirty: %s", MessageHelper.collectionInfoString( getLoadedPersister().getRole(), getLoadedKey() ) ); } setDoupdate(false); setDoremove(false); setDorecreate(false); setReached(false); setProcessed(false); }
public void postInitialize(PersistentCollection collection) throws HibernateException { snapshot = getLoadedPersister().isMutable() ? collection.getSnapshot( getLoadedPersister() ) : null; collection.setSnapshot(loadedKey, role, snapshot); if (getLoadedPersister().getBatchSize() > 1) { ((AbstractPersistentCollection) collection).getSession().getPersistenceContext().getBatchFetchQueue().removeBatchLoadableCollection(this); } }
/** * Called after a successful flush */ public void postFlush(PersistentCollection collection) throws HibernateException { if ( isIgnore() ) { ignore = false; } else if ( !isProcessed() ) { throw new AssertionFailure( "collection [" + collection.getRole() + "] was not processed by flush()" ); } 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(); }
/** * 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; }
/** * 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? return collection.wasInitialized() && ( getLoadedPersister()==null || getLoadedPersister().isMutable() ) && collection.isSnapshotEmpty( getSnapshot() ); }
@Override public Object replace( final Object original, final Object target, final SessionImplementor session, final Object owner, final Map copyCache) throws HibernateException { if ( original == null ) { return null; } if ( !Hibernate.isInitialized( original ) ) { return target; } // for a null target, or a target which is the same as the original, we // need to put the merged elements in a new collection Object result = target == null || target == original ? instantiateResult( original ) : target; //for arrays, replaceElements() may return a different reference, since //the array length might not match result = replaceElements( original, result, owner, copyCache, session ); if ( original == target ) { // get the elements back into the target making sure to handle dirty flag boolean wasClean = PersistentCollection.class.isInstance( target ) && !( ( PersistentCollection ) target ).isDirty(); //TODO: this is a little inefficient, don't need to do a whole // deep replaceElements() call replaceElements( result, target, owner, copyCache, session ); if ( wasClean ) { ( ( PersistentCollection ) target ).clearDirty(); } result = target; } return result; }
@Override public Object processCollection(Object collection, CollectionType type) throws HibernateException { if ( collection == null ) { return null; } final SessionImplementor session = getSession(); final CollectionPersister persister = session.getFactory().getCollectionPersister( type.getRole() ); if ( collection instanceof PersistentCollection ) { final PersistentCollection persistentCollection = (PersistentCollection) collection; if ( persistentCollection.setCurrentSession( session ) ) { if ( isOwnerUnchanged( persistentCollection, persister, extractCollectionKeyFromOwner( persister ) ) ) { // a "detached" collection that originally belonged to the same entity if ( persistentCollection.isDirty() ) { throw new HibernateException( "reassociated object has dirty collection" ); } reattachCollection( persistentCollection, type ); } else { // a "detached" collection that belonged to a different entity throw new HibernateException( "reassociated object has dirty collection reference" ); } } else { // a collection loaded in the current session // can not possibly be the collection belonging // to the entity passed to update() throw new HibernateException( "reassociated object has dirty collection reference" ); } } else { // brand new collection //TODO: or an array!! we can't lock objects with arrays now?? throw new HibernateException( "reassociated object has dirty collection reference (or an array)" ); } return null; }
/** * 1. Recreate the collection key -> collection map * 2. rebuild the collection entries * 3. call Interceptor.postFlush() */ protected void postFlush(SessionImplementor session) throws HibernateException { LOG.trace( "Post flush" ); final PersistenceContext persistenceContext = session.getPersistenceContext(); persistenceContext.getCollectionsByKey().clear(); // the database has changed now, so the subselect results need to be invalidated // the batch fetching queues should also be cleared - especially the collection batch fetching one persistenceContext.getBatchFetchQueue().clear(); for ( Map.Entry<PersistentCollection, CollectionEntry> me : IdentityMap.concurrentEntries( persistenceContext.getCollectionEntries() ) ) { CollectionEntry collectionEntry = me.getValue(); PersistentCollection persistentCollection = me.getKey(); collectionEntry.postFlush(persistentCollection); if ( collectionEntry.getLoadedPersister() == null ) { //if the collection is dereferenced, remove from the session cache //iter.remove(); //does not work, since the entrySet is not backed by the set persistenceContext.getCollectionEntries() .remove(persistentCollection); } else { //otherwise recreate the mapping between the collection and its key CollectionKey collectionKey = new CollectionKey( collectionEntry.getLoadedPersister(), collectionEntry.getLoadedKey() ); persistenceContext.getCollectionsByKey().put(collectionKey, persistentCollection); } } }
@Override public void clear() { for ( Object o : proxiesByKey.values() ) { if ( o == null ) { //entry may be GCd continue; } ((HibernateProxy) o).getHibernateLazyInitializer().unsetSession(); } for ( Map.Entry<PersistentCollection, CollectionEntry> aCollectionEntryArray : IdentityMap.concurrentEntries( collectionEntries ) ) { aCollectionEntryArray.getKey().unsetSession( getSession() ); } arrayHolders.clear(); entitiesByKey.clear(); entitiesByUniqueKey.clear(); entityEntryContext.clear(); // entityEntries.clear(); parentsByChild.clear(); entitySnapshotsByKey.clear(); collectionsByKey.clear(); collectionEntries.clear(); if ( unownedCollections != null ) { unownedCollections.clear(); } proxiesByKey.clear(); nullifiableEntityKeys.clear(); if ( batchFetchQueue != null ) { batchFetchQueue.clear(); } // defaultReadOnly is unaffected by clear() hasNonReadOnlyEntities = false; if ( loadContexts != null ) { loadContexts.cleanup(); } naturalIdXrefDelegate.clear(); }
/** * Initialize the flags of the CollectionEntry, including the * dirty check. */ private void prepareCollectionFlushes(PersistenceContext persistenceContext) throws HibernateException { // Initialize dirty flags for arrays + collections with composite elements // and reset reached, doupdate, etc. LOG.debug( "Dirty checking collections" ); for ( Map.Entry<PersistentCollection,CollectionEntry> entry : IdentityMap.concurrentEntries( (Map<PersistentCollection,CollectionEntry>) persistenceContext.getCollectionEntries() )) { entry.getValue().preFlush( entry.getKey() ); } }
@Override public void addUninitializedCollection(CollectionPersister persister, PersistentCollection collection, Serializable id) { final CollectionEntry ce = new CollectionEntry( collection, persister, id, flushing ); addCollection( collection, ce, id ); if ( persister.getBatchSize() > 1 ) { getBatchFetchQueue().addBatchLoadableCollection( collection, ce ); } }
@Override public void addUninitializedDetachedCollection(CollectionPersister persister, PersistentCollection collection) { final CollectionEntry ce = new CollectionEntry( persister, collection.getKey() ); addCollection( collection, ce, collection.getKey() ); if ( persister.getBatchSize() > 1 ) { getBatchFetchQueue().addBatchLoadableCollection( collection, ce ); } }
@Override public void addInitializedDetachedCollection(CollectionPersister collectionPersister, PersistentCollection collection) throws HibernateException { if ( collection.isUnreferenced() ) { //treat it just like a new collection addCollection( collection, collectionPersister ); } else { final CollectionEntry ce = new CollectionEntry( collection, session.getFactory() ); addCollection( collection, ce, collection.getKey() ); } }
protected CollectionAction( final CollectionPersister persister, final PersistentCollection collection, final Serializable key, final SessionImplementor session) { this.persister = persister; this.session = session; this.key = key; this.collectionRole = persister.getRole(); this.collection = collection; }
LoadingCollectionEntry( ResultSet resultSet, CollectionPersister persister, Serializable key, PersistentCollection collection) { this.resultSet = resultSet; this.persister = persister; this.key = key; this.collection = collection; }
/** * Attempt to locate the loading collection given the owner's key. The lookup here * occurs against all result-set contexts... * * @param persister The collection persister * @param ownerKey The owner key * @return The loading collection, or null if not found. */ public PersistentCollection locateLoadingCollection(CollectionPersister persister, Serializable ownerKey) { final LoadingCollectionEntry lce = locateLoadingCollectionEntry( new CollectionKey( persister, ownerKey ) ); if ( lce != null ) { if ( LOG.isTraceEnabled() ) { LOG.tracef( "Returning loading collection: %s", MessageHelper.collectionInfoString( persister, ownerKey, getSession().getFactory() ) ); } return lce.getCollection(); } return null; }
/** * Force initialization of a proxy or persistent collection. * <p/> * Note: This only ensures intialization of a proxy object or collection; * it is not guaranteed that the elements INSIDE the collection will be initialized/materialized. * * @param proxy a persistable object, proxy, persistent collection or <tt>null</tt> * @throws HibernateException if we can't initialize the proxy at this time, eg. the <tt>Session</tt> was closed */ public static void initialize(Object proxy) throws HibernateException { if ( proxy == null ) { return; } if ( proxy instanceof HibernateProxy ) { ( (HibernateProxy) proxy ).getHibernateLazyInitializer().initialize(); } else if ( proxy instanceof PersistentCollection ) { ( (PersistentCollection) proxy ).forceInitialization(); } }
/** * Check if the proxy or persistent collection is initialized. * * @param proxy a persistable object, proxy, persistent collection or <tt>null</tt> * @return true if the argument is already initialized, or is not a proxy or collection */ @SuppressWarnings("SimplifiableIfStatement") public static boolean isInitialized(Object proxy) { if ( proxy instanceof HibernateProxy ) { return !( (HibernateProxy) proxy ).getHibernateLazyInitializer().isUninitialized(); } else if ( proxy instanceof PersistentCollection ) { return ( (PersistentCollection) proxy ).wasInitialized(); } else { return true; } }
@Override public void initializeCollection(PersistentCollection collection, boolean writing) throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); InitializeCollectionEvent event = new InitializeCollectionEvent( collection, this ); for ( InitializeCollectionEventListener listener : listeners( EventType.INIT_COLLECTION ) ) { listener.onInitializeCollection( event ); } delayedAfterCompletion(); }
@Override public Object processCollection(Object collection, CollectionType type) throws HibernateException { if ( collection == CollectionType.UNFETCHED_COLLECTION ) { return null; } final EventSource session = getSession(); final CollectionPersister persister = session.getFactory().getCollectionPersister( type.getRole() ); if ( isUpdate ) { removeCollection( persister, extractCollectionKeyFromOwner( persister ), session ); } if ( collection != null && collection instanceof PersistentCollection ) { final PersistentCollection wrapper = (PersistentCollection) collection; wrapper.setCurrentSession( session ); if ( wrapper.wasInitialized() ) { session.getPersistenceContext().addNewCollection( persister, wrapper ); } else { reattachCollection( wrapper, type ); } } else { // otherwise a null or brand new collection // this will also (inefficiently) handle arrays, which // have no snapshot, so we can't do any better //processArrayOrNewCollection(collection, type); } return null; }
public PersistentCollection instantiate( SessionImplementor session, CollectionPersister persister, Serializable key) throws HibernateException { return new PersistentIdentifierBag(session); }
public PreCollectionRemoveEvent(CollectionPersister collectionPersister, PersistentCollection collection, EventSource source, Object loadedOwner) { super( collectionPersister, collection, source, loadedOwner, getOwnerIdOrNull( loadedOwner, source ) ); }