Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » engine » [javadoc | source]
    1   /*
    2    * Hibernate, Relational Persistence for Idiomatic Java
    3    *
    4    * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
    5    * indicated by the @author tags or express copyright attribution
    6    * statements applied by the authors.  All third-party contributions are
    7    * distributed under license by Red Hat Middleware LLC.
    8    *
    9    * This copyrighted material is made available to anyone wishing to use, modify,
   10    * copy, or redistribute it subject to the terms and conditions of the GNU
   11    * Lesser General Public License, as published by the Free Software Foundation.
   12    *
   13    * This program is distributed in the hope that it will be useful,
   14    * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   15    * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
   16    * for more details.
   17    *
   18    * You should have received a copy of the GNU Lesser General Public License
   19    * along with this distribution; if not, write to:
   20    * Free Software Foundation, Inc.
   21    * 51 Franklin Street, Fifth Floor
   22    * Boston, MA  02110-1301  USA
   23    *
   24    */
   25   package org.hibernate.engine;
   26   
   27   import org.slf4j.Logger;
   28   import org.slf4j.LoggerFactory;
   29   import org.hibernate.AssertionFailure;
   30   import org.hibernate.EntityMode;
   31   import org.hibernate.HibernateException;
   32   import org.hibernate.collection.PersistentCollection;
   33   import org.hibernate.persister.collection.CollectionPersister;
   34   import org.hibernate.pretty.MessageHelper;
   35   import org.hibernate.type.CollectionType;
   36   
   37   import java.io.Serializable;
   38   
   39   /**
   40    * Implements book-keeping for the collection persistence by reachability algorithm
   41    * @author Gavin King
   42    */
   43   public final class Collections {
   44   
   45   	private Collections() {}
   46   
   47   	private static final Logger log = LoggerFactory.getLogger(Collections.class);
   48   
   49   	/**
   50   	 * record the fact that this collection was dereferenced
   51   	 *
   52   	 * @param coll The collection to be updated by unreachability.
   53   	 * @throws HibernateException
   54   	 */
   55   	public static void processUnreachableCollection(PersistentCollection coll, SessionImplementor session)
   56   	throws HibernateException {
   57   
   58   		if ( coll.getOwner()==null ) {
   59   			processNeverReferencedCollection(coll, session);
   60   		}
   61   		else {
   62   			processDereferencedCollection(coll, session);
   63   		}
   64   
   65   	}
   66   
   67   	private static void processDereferencedCollection(PersistentCollection coll, SessionImplementor session)
   68   	throws HibernateException {
   69   
   70   		final PersistenceContext persistenceContext = session.getPersistenceContext();
   71   		CollectionEntry entry = persistenceContext.getCollectionEntry(coll);
   72   		final CollectionPersister loadedPersister = entry.getLoadedPersister();
   73   
   74   		if ( log.isDebugEnabled() && loadedPersister != null )
   75   			log.debug(
   76   					"Collection dereferenced: " +
   77   					MessageHelper.collectionInfoString(
   78   							loadedPersister,
   79   					        entry.getLoadedKey(),
   80   					        session.getFactory()
   81   						)
   82   				);
   83   
   84   		// do a check
   85   		boolean hasOrphanDelete = loadedPersister != null &&
   86   		                          loadedPersister.hasOrphanDelete();
   87   		if (hasOrphanDelete) {
   88   			Serializable ownerId = loadedPersister.getOwnerEntityPersister()
   89   					.getIdentifier( coll.getOwner(), session.getEntityMode() );
   90   			if ( ownerId == null ) {
   91   				// the owning entity may have been deleted and its identifier unset due to
   92   				// identifier-rollback; in which case, try to look up its identifier from
   93   				// the persistence context
   94   				if ( session.getFactory().getSettings().isIdentifierRollbackEnabled() ) {
   95   					EntityEntry ownerEntry = persistenceContext.getEntry( coll.getOwner() );
   96   					if ( ownerEntry != null ) {
   97   						ownerId = ownerEntry.getId();
   98   					}
   99   				}
  100   				if ( ownerId == null ) {
  101   					throw new AssertionFailure( "Unable to determine collection owner identifier for orphan-delete processing" );
  102   				}
  103   			}
  104   			EntityKey key = new EntityKey(
  105   					ownerId,
  106   			        loadedPersister.getOwnerEntityPersister(),
  107   			        session.getEntityMode()
  108   			);
  109   			Object owner = persistenceContext.getEntity(key);
  110   			if ( owner == null ) {
  111   				throw new AssertionFailure(
  112   						"collection owner not associated with session: " +
  113   						loadedPersister.getRole()
  114   				);
  115   			}
  116   			EntityEntry e = persistenceContext.getEntry(owner);
  117   			//only collections belonging to deleted entities are allowed to be dereferenced in the case of orphan delete
  118   			if ( e != null && e.getStatus() != Status.DELETED && e.getStatus() != Status.GONE ) {
  119   				throw new HibernateException(
  120   						"A collection with cascade=\"all-delete-orphan\" was no longer referenced by the owning entity instance: " +
  121   						loadedPersister.getRole()
  122   				);
  123   			}
  124   		}
  125   
  126   		// do the work
  127   		entry.setCurrentPersister(null);
  128   		entry.setCurrentKey(null);
  129   		prepareCollectionForUpdate( coll, entry, session.getEntityMode(), session.getFactory() );
  130   
  131   	}
  132   
  133   	private static void processNeverReferencedCollection(PersistentCollection coll, SessionImplementor session)
  134   	throws HibernateException {
  135   
  136   		final PersistenceContext persistenceContext = session.getPersistenceContext();
  137   		CollectionEntry entry = persistenceContext.getCollectionEntry(coll);
  138   
  139   		log.debug(
  140   				"Found collection with unloaded owner: " +
  141   				MessageHelper.collectionInfoString(
  142   						entry.getLoadedPersister(),
  143   				        entry.getLoadedKey(),
  144   				        session.getFactory()
  145   				)
  146   		);
  147   
  148   		entry.setCurrentPersister( entry.getLoadedPersister() );
  149   		entry.setCurrentKey( entry.getLoadedKey() );
  150   
  151   		prepareCollectionForUpdate( coll, entry, session.getEntityMode(), session.getFactory() );
  152   
  153   	}
  154   
  155   	/**
  156   	 * Initialize the role of the collection.
  157   	 *
  158   	 * @param collection The collection to be updated by reachibility.
  159   	 * @param type The type of the collection.
  160   	 * @param entity The owner of the collection.
  161   	 * @throws HibernateException
  162   	 */
  163   	public static void processReachableCollection(
  164   			PersistentCollection collection,
  165   	        CollectionType type,
  166   	        Object entity,
  167   	        SessionImplementor session)
  168   	throws HibernateException {
  169   
  170   		collection.setOwner(entity);
  171   
  172   		CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(collection);
  173   
  174   		if ( ce == null ) {
  175   			// refer to comment in StatefulPersistenceContext.addCollection()
  176   			throw new HibernateException(
  177   					"Found two representations of same collection: " +
  178   					type.getRole()
  179   			);
  180   		}
  181   
  182   		// The CollectionEntry.isReached() stuff is just to detect any silly users  
  183   		// who set up circular or shared references between/to collections.
  184   		if ( ce.isReached() ) {
  185   			// We've been here before
  186   			throw new HibernateException(
  187   					"Found shared references to a collection: " +
  188   					type.getRole()
  189   			);
  190   		}
  191   		ce.setReached(true);
  192   
  193   		SessionFactoryImplementor factory = session.getFactory();
  194   		CollectionPersister persister = factory.getCollectionPersister( type.getRole() );
  195   		ce.setCurrentPersister(persister);
  196   		ce.setCurrentKey( type.getKeyOfOwner(entity, session) ); //TODO: better to pass the id in as an argument?
  197   
  198   		if ( log.isDebugEnabled() ) {
  199   			log.debug(
  200   					"Collection found: " +
  201   					MessageHelper.collectionInfoString( persister, ce.getCurrentKey(), factory ) +
  202   					", was: " +
  203   					MessageHelper.collectionInfoString( ce.getLoadedPersister(), ce.getLoadedKey(), factory ) +
  204   					( collection.wasInitialized() ? " (initialized)" : " (uninitialized)" )
  205   			);
  206   		}
  207   
  208   		prepareCollectionForUpdate( collection, ce, session.getEntityMode(), factory );
  209   
  210   	}
  211   
  212   	/**
  213   	 * 1. record the collection role that this collection is referenced by
  214   	 * 2. decide if the collection needs deleting/creating/updating (but
  215   	 *	don't actually schedule the action yet)
  216   	 */
  217   	private static void prepareCollectionForUpdate(
  218   			PersistentCollection collection,
  219   	        CollectionEntry entry,
  220   	        EntityMode entityMode,
  221   	        SessionFactoryImplementor factory)
  222   	throws HibernateException {
  223   
  224   		if ( entry.isProcessed() ) {
  225   			throw new AssertionFailure( "collection was processed twice by flush()" );
  226   		}
  227   		entry.setProcessed(true);
  228   
  229   		final CollectionPersister loadedPersister = entry.getLoadedPersister();
  230   		final CollectionPersister currentPersister = entry.getCurrentPersister();
  231   		if ( loadedPersister != null || currentPersister != null ) {					// it is or was referenced _somewhere_
  232   
  233   			boolean ownerChanged = loadedPersister != currentPersister ||				// if either its role changed,
  234   			                       !currentPersister
  235   					                       .getKeyType().isEqual(                       // or its key changed
  236   													entry.getLoadedKey(),
  237   			                                        entry.getCurrentKey(),
  238   			                                        entityMode, factory
  239   			                       );
  240   
  241   			if (ownerChanged) {
  242   
  243   				// do a check
  244   				final boolean orphanDeleteAndRoleChanged = loadedPersister != null &&
  245   				                                           currentPersister != null &&
  246   				                                           loadedPersister.hasOrphanDelete();
  247   
  248   				if (orphanDeleteAndRoleChanged) {
  249   					throw new HibernateException(
  250   							"Don't change the reference to a collection with cascade=\"all-delete-orphan\": " +
  251   							loadedPersister.getRole()
  252   						);
  253   				}
  254   
  255   				// do the work
  256   				if ( currentPersister != null ) {
  257   					entry.setDorecreate(true);											// we will need to create new entries
  258   				}
  259   
  260   				if ( loadedPersister != null ) {
  261   					entry.setDoremove(true);											// we will need to remove ye olde entries
  262   					if ( entry.isDorecreate() ) {
  263   						log.trace( "Forcing collection initialization" );
  264   						collection.forceInitialization();								// force initialize!
  265   					}
  266   				}
  267   
  268   			}
  269   			else if ( collection.isDirty() ) {											// else if it's elements changed
  270   				entry.setDoupdate(true);
  271   			}
  272   
  273   		}
  274   
  275   	}
  276   
  277   }

Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » engine » [javadoc | source]