/** * 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, unset its session reference and remove from the // session cache // iter.remove(); //does not work, since the entrySet is not backed by the set persistentCollection.unsetSession(session); 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); } } }
private static void processDereferencedCollection( PersistentCollection coll, SessionImplementor session) { final PersistenceContext persistenceContext = session.getPersistenceContext(); CollectionEntry entry = persistenceContext.getCollectionEntry(coll); final CollectionPersister loadedPersister = entry.getLoadedPersister(); if (LOG.isDebugEnabled() && loadedPersister != null) { LOG.debugf( "Collection dereferenced: %s", MessageHelper.collectionInfoString( loadedPersister, entry.getLoadedKey(), session.getFactory())); } // do a check boolean hasOrphanDelete = loadedPersister != null && loadedPersister.hasOrphanDelete(); if (hasOrphanDelete) { Serializable ownerId = loadedPersister.getOwnerEntityPersister().getIdentifier(coll.getOwner(), session); if (ownerId == null) { // 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()) { EntityEntry ownerEntry = persistenceContext.getEntry(coll.getOwner()); if (ownerEntry != null) { ownerId = ownerEntry.getId(); } } if (ownerId == null) { throw new AssertionFailure( "Unable to determine collection owner identifier for orphan-delete processing"); } } EntityKey key = session.generateEntityKey(ownerId, loadedPersister.getOwnerEntityPersister()); Object owner = persistenceContext.getEntity(key); if (owner == null) { throw new AssertionFailure( "collection owner not associated with session: " + loadedPersister.getRole()); } EntityEntry e = persistenceContext.getEntry(owner); // only collections belonging to deleted entities are allowed to be dereferenced in the case // of orphan delete if (e != null && e.getStatus() != Status.DELETED && e.getStatus() != Status.GONE) { throw new HibernateException( "A collection with cascade=\"all-delete-orphan\" was no longer referenced by the owning entity instance: " + loadedPersister.getRole()); } } // do the work entry.setCurrentPersister(null); entry.setCurrentKey(null); prepareCollectionForUpdate(coll, entry, session.getFactory()); }
/** * 1. record the collection role that this collection is referenced by 2. decide if the collection * needs deleting/creating/updating (but don't actually schedule the action yet) */ @SuppressWarnings({"JavaDoc"}) private static void prepareCollectionForUpdate( PersistentCollection collection, CollectionEntry entry, SessionFactoryImplementor factory) { if (entry.isProcessed()) { throw new AssertionFailure("collection was processed twice by flush()"); } entry.setProcessed(true); final CollectionPersister loadedPersister = entry.getLoadedPersister(); final CollectionPersister currentPersister = entry.getCurrentPersister(); if (loadedPersister != null || currentPersister != null) { // it is or was referenced _somewhere_ boolean ownerChanged = loadedPersister != currentPersister || // if either its role changed, !currentPersister .getKeyType() .isEqual( // or its key changed entry.getLoadedKey(), entry.getCurrentKey(), factory); if (ownerChanged) { // do a check final boolean orphanDeleteAndRoleChanged = loadedPersister != null && currentPersister != null && loadedPersister.hasOrphanDelete(); if (orphanDeleteAndRoleChanged) { throw new HibernateException( "Don't change the reference to a collection with cascade=\"all-delete-orphan\": " + loadedPersister.getRole()); } // do the work if (currentPersister != null) { entry.setDorecreate(true); // we will need to create new entries } if (loadedPersister != null) { entry.setDoremove(true); // we will need to remove ye olde entries if (entry.isDorecreate()) { LOG.trace("Forcing collection initialization"); collection.forceInitialization(); } } } else if (collection.isDirty()) { // the collection's elements have changed entry.setDoupdate(true); } } }
/** * Initialize the role of the collection. * * @param collection The collection to be updated by reachability. * @param type The type of the collection. * @param entity The owner of the collection. * @param session The session from which this request originates */ public static void processReachableCollection( PersistentCollection collection, CollectionType type, Object entity, SessionImplementor session) { collection.setOwner(entity); CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(collection); if (ce == null) { // refer to comment in StatefulPersistenceContext.addCollection() throw new HibernateException( "Found two representations of same collection: " + type.getRole()); } // 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); SessionFactoryImplementor factory = session.getFactory(); CollectionPersister persister = factory.getCollectionPersister(type.getRole()); ce.setCurrentPersister(persister); ce.setCurrentKey( type.getKeyOfOwner(entity, session)); // TODO: better to pass the id in as an argument? if (LOG.isDebugEnabled()) { if (collection.wasInitialized()) LOG.debugf( "Collection found: %s, was: %s (initialized)", MessageHelper.collectionInfoString(persister, ce.getCurrentKey(), factory), MessageHelper.collectionInfoString( ce.getLoadedPersister(), ce.getLoadedKey(), factory)); else LOG.debugf( "Collection found: %s, was: %s (uninitialized)", MessageHelper.collectionInfoString(persister, ce.getCurrentKey(), factory), MessageHelper.collectionInfoString( ce.getLoadedPersister(), ce.getLoadedKey(), factory)); } prepareCollectionForUpdate(collection, ce, factory); }
/** * record the fact that this collection was dereferenced * * @param coll The collection to be updated by un-reachability. */ @SuppressWarnings({"JavaDoc"}) public static void processUnreachableCollection( PersistentCollection coll, SessionImplementor session) { if (coll.getOwner() == null) { processNeverReferencedCollection(coll, session); } else { processDereferencedCollection(coll, session); } }
/** * 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; } else if (proxy instanceof HibernateProxy) { ((HibernateProxy) proxy).getHibernateLazyInitializer().initialize(); } else if (proxy instanceof PersistentCollection) { ((PersistentCollection) proxy).forceInitialization(); } }
protected final Serializable getKey() { Serializable finalKey = key; if (key instanceof DelayedPostInsertIdentifier) { // need to look it up from the persistence-context finalKey = session.getPersistenceContext().getEntry(collection.getOwner()).getId(); if (finalKey == key) { // we may be screwed here since the collection action is about to execute // and we do not know the final owner key value } } return finalKey; }
/** * process any unreferenced collections and then inspect all known collections, scheduling * creates/removes/updates */ @SuppressWarnings("unchecked") private int flushCollections( final EventSource session, final PersistenceContext persistenceContext) throws HibernateException { LOG.trace("Processing unreferenced collections"); final Map.Entry<PersistentCollection, CollectionEntry>[] entries = IdentityMap.concurrentEntries( (Map<PersistentCollection, CollectionEntry>) persistenceContext.getCollectionEntries()); final int count = entries.length; for (Map.Entry<PersistentCollection, CollectionEntry> me : entries) { CollectionEntry ce = me.getValue(); if (!ce.isReached() && !ce.isIgnore()) { Collections.processUnreachableCollection(me.getKey(), session); } } // Schedule updates to collections: LOG.trace("Scheduling collection removes/(re)creates/updates"); ActionQueue actionQueue = session.getActionQueue(); for (Map.Entry<PersistentCollection, CollectionEntry> me : IdentityMap.concurrentEntries( (Map<PersistentCollection, CollectionEntry>) persistenceContext.getCollectionEntries())) { PersistentCollection coll = me.getKey(); CollectionEntry ce = me.getValue(); if (ce.isDorecreate()) { session.getInterceptor().onCollectionRecreate(coll, ce.getCurrentKey()); actionQueue.addAction( new CollectionRecreateAction( coll, ce.getCurrentPersister(), ce.getCurrentKey(), session)); } if (ce.isDoremove()) { session.getInterceptor().onCollectionRemove(coll, ce.getLoadedKey()); actionQueue.addAction( new CollectionRemoveAction( coll, ce.getLoadedPersister(), ce.getLoadedKey(), ce.isSnapshotEmpty(coll), session)); } if (ce.isDoupdate()) { session.getInterceptor().onCollectionUpdate(coll, ce.getLoadedKey()); actionQueue.addAction( new CollectionUpdateAction( coll, ce.getLoadedPersister(), ce.getLoadedKey(), ce.isSnapshotEmpty(coll), session)); } // todo : I'm not sure the !wasInitialized part should really be part of this check if (!coll.wasInitialized() && coll.hasQueuedOperations()) { actionQueue.addAction( new QueuedOperationCollectionAction( coll, ce.getLoadedPersister(), ce.getLoadedKey(), session)); } } actionQueue.sortCollectionActions(); return count; }