Package org.hibernate.engine

Source Code of org.hibernate.engine.Collections

/*
* 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.engine;

import java.io.Serializable;

import org.hibernate.AssertionFailure;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.collection.PersistentCollection;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.type.CollectionType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Implements book-keeping for the collection persistence by reachability algorithm
* @author Gavin King
*/
public final class Collections {

  private Collections() {}

  private static final Logger log = LoggerFactory.getLogger(Collections.class);

  /**
   * record the fact that this collection was dereferenced
   *
   * @param coll The collection to be updated by unreachability.
   * @throws HibernateException
   */
  public static void processUnreachableCollection(PersistentCollection coll, SessionImplementor session)
  throws HibernateException {

    if ( coll.getOwner()==null ) {
      processNeverReferencedCollection(coll, session);
    }
    else {
      processDereferencedCollection(coll, session);
    }

  }

  private static void processDereferencedCollection(PersistentCollection coll, SessionImplementor session)
  throws HibernateException {

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

    if ( log.isDebugEnabled() && loadedPersister != null )
      log.debug(
          "Collection dereferenced: " +
          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.getEntityMode() );
      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 = new EntityKey(
          ownerId,
              loadedPersister.getOwnerEntityPersister(),
              session.getEntityMode()
      );
      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.getEntityMode(), session.getFactory() );

  }

  private static void processNeverReferencedCollection(PersistentCollection coll, SessionImplementor session)
  throws HibernateException {

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

    log.debug(
        "Found collection with unloaded owner: " +
        MessageHelper.collectionInfoString(
            entry.getLoadedPersister(),
                entry.getLoadedKey(),
                session.getFactory()
        )
    );

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

    prepareCollectionForUpdate( coll, entry, session.getEntityMode(), session.getFactory() );

  }

  /**
   * Initialize the role of the collection.
   *
   * @param collection The collection to be updated by reachibility.
   * @param type The type of the collection.
   * @param entity The owner of the collection.
   * @throws HibernateException
   */
  public static void processReachableCollection(
      PersistentCollection collection,
          CollectionType type,
          Object entity,
          SessionImplementor session)
  throws HibernateException {

    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() ) {
      log.debug(
          "Collection found: " +
          MessageHelper.collectionInfoString( persister, ce.getCurrentKey(), factory ) +
          ", was: " +
          MessageHelper.collectionInfoString( ce.getLoadedPersister(), ce.getLoadedKey(), factory ) +
          ( collection.wasInitialized() ? " (initialized)" : " (uninitialized)" )
      );
    }

    prepareCollectionForUpdate( collection, ce, session.getEntityMode(), factory );

  }

  /**
   * 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)
   */
  private static void prepareCollectionForUpdate(
      PersistentCollection collection,
          CollectionEntry entry,
          EntityMode entityMode,
          SessionFactoryImplementor factory)
  throws HibernateException {

    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(),
                                              entityMode, 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();                // force initialize!
          }
        }

      }
      else if ( collection.isDirty() ) {                      // else if it's elements changed
        entry.setDoupdate(true);
      }

    }

  }

}
TOP

Related Classes of org.hibernate.engine.Collections

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.