/**
   * process any unreferenced collections and then inspect all known collections, scheduling
   * creates/removes/updates
   */
  private void flushCollections(EventSource session) throws HibernateException {

    if (LOG.isTraceEnabled()) {
      LOG.trace("Processing unreferenced collections");
    }

    List list = IdentityMap.entries(session.getPersistenceContext().getCollectionEntries());
    int size = list.size();
    for (int i = 0; i < size; i++) {
      Map.Entry me = (Map.Entry) list.get(i);
      CollectionEntry ce = (CollectionEntry) me.getValue();
      if (!ce.isReached() && !ce.isIgnore()) {
        Collections.processUnreachableCollection((PersistentCollection) me.getKey(), session);
      }
    }

    // Schedule updates to collections:

    if (LOG.isTraceEnabled()) {
      LOG.trace("Scheduling collection removes/(re)creates/updates");
    }

    list = IdentityMap.entries(session.getPersistenceContext().getCollectionEntries());
    size = list.size();
    ActionQueue actionQueue = session.getActionQueue();
    for (int i = 0; i < size; i++) {
      Map.Entry me = (Map.Entry) list.get(i);
      PersistentCollection coll = (PersistentCollection) me.getKey();
      CollectionEntry ce = (CollectionEntry) 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));
      }
    }

    actionQueue.sortCollectionActions();
  }
  /**
   * 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);
      }
    }
  }
  /**
   * 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();
    persistenceContext
        .getBatchFetchQueue()
        .clearSubselects(); // the database has changed now, so the subselect results need to be
                            // invalidated

    Iterator iter = persistenceContext.getCollectionEntries().entrySet().iterator();
    while (iter.hasNext()) {
      Map.Entry me = (Map.Entry) iter.next();
      CollectionEntry collectionEntry = (CollectionEntry) me.getValue();
      PersistentCollection 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);
      }
    }

    session.getInterceptor().postFlush(new LazyIterator(persistenceContext.getEntitiesByKey()));
  }
Пример #4
0
  /**
   * 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);
  }
Пример #5
0
  private static void processNeverReferencedCollection(
      PersistentCollection coll, SessionImplementor session) throws HibernateException {

    final PersistenceContext persistenceContext = session.getPersistenceContext();
    CollectionEntry entry = persistenceContext.getCollectionEntry(coll);

    if (LOG.isDebugEnabled()) {
      LOG.debugf(
          "Found collection with unloaded owner: %s",
          MessageHelper.collectionInfoString(
              entry.getLoadedPersister(), entry.getLoadedKey(), session.getFactory()));
    }

    entry.setCurrentPersister(entry.getLoadedPersister());
    entry.setCurrentKey(entry.getLoadedKey());

    prepareCollectionForUpdate(coll, entry, session.getFactory());
  }
Пример #6
0
  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());
  }
Пример #7
0
  /**
   * 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);
      }
    }
  }
  /**
   * 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;
  }