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 java.io.Serializable;
   28   
   29   import org.hibernate.HibernateException;
   30   import org.hibernate.TransientObjectException;
   31   import org.hibernate.intercept.LazyPropertyInitializer;
   32   import org.hibernate.persister.entity.EntityPersister;
   33   import org.hibernate.proxy.HibernateProxy;
   34   import org.hibernate.proxy.LazyInitializer;
   35   import org.hibernate.type.AbstractComponentType;
   36   import org.hibernate.type.EntityType;
   37   import org.hibernate.type.Type;
   38   
   39   /**
   40    * Algorithms related to foreign key constraint transparency
   41    * 
   42    * @author Gavin King
   43    */
   44   public final class ForeignKeys {
   45   	
   46   	private ForeignKeys() {}
   47   	
   48   	public static class Nullifier {
   49   	
   50   		private final boolean isDelete;
   51   		private final boolean isEarlyInsert;
   52   		private final SessionImplementor session;
   53   		private final Object self;
   54   		
   55   		public Nullifier(Object self, boolean isDelete, boolean isEarlyInsert, SessionImplementor session) {
   56   			this.isDelete = isDelete;
   57   			this.isEarlyInsert = isEarlyInsert;
   58   			this.session = session;
   59   			this.self = self;
   60   		}
   61   		
   62   		/**
   63   		 * Nullify all references to entities that have not yet 
   64   		 * been inserted in the database, where the foreign key
   65   		 * points toward that entity
   66   		 */
   67   		public void nullifyTransientReferences(final Object[] values, final Type[] types) 
   68   		throws HibernateException {
   69   			for ( int i = 0; i < types.length; i++ ) {
   70   				values[i] = nullifyTransientReferences( values[i], types[i] );
   71   			}
   72   		}
   73   	
   74   		/**
   75   		 * Return null if the argument is an "unsaved" entity (ie. 
   76   		 * one with no existing database row), or the input argument 
   77   		 * otherwise. This is how Hibernate avoids foreign key constraint
   78   		 * violations.
   79   		 */
   80   		private Object nullifyTransientReferences(final Object value, final Type type) 
   81   		throws HibernateException {
   82   			if ( value == null ) {
   83   				return null;
   84   			}
   85   			else if ( type.isEntityType() ) {
   86   				EntityType entityType = (EntityType) type;
   87   				if ( entityType.isOneToOne() ) {
   88   					return value;
   89   				}
   90   				else {
   91   					String entityName = entityType.getAssociatedEntityName();
   92   					return isNullifiable(entityName, value) ? null : value;
   93   				}
   94   			}
   95   			else if ( type.isAnyType() ) {
   96   				return isNullifiable(null, value) ? null : value;
   97   			}
   98   			else if ( type.isComponentType() ) {
   99   				AbstractComponentType actype = (AbstractComponentType) type;
  100   				Object[] subvalues = actype.getPropertyValues(value, session);
  101   				Type[] subtypes = actype.getSubtypes();
  102   				boolean substitute = false;
  103   				for ( int i = 0; i < subvalues.length; i++ ) {
  104   					Object replacement = nullifyTransientReferences( subvalues[i], subtypes[i] );
  105   					if ( replacement != subvalues[i] ) {
  106   						substitute = true;
  107   						subvalues[i] = replacement;
  108   					}
  109   				}
  110   				if (substitute) actype.setPropertyValues( value, subvalues, session.getEntityMode() );
  111   				return value;
  112   			}
  113   			else {
  114   				return value;
  115   			}
  116   		}
  117   	
  118   		/**
  119   		 * Determine if the object already exists in the database, 
  120   		 * using a "best guess"
  121   		 */
  122   		private boolean isNullifiable(final String entityName, Object object) 
  123   		throws HibernateException {
  124   			
  125   			if (object==LazyPropertyInitializer.UNFETCHED_PROPERTY) return false; //this is kinda the best we can do...
  126   			
  127   			if ( object instanceof HibernateProxy ) {
  128   				// if its an uninitialized proxy it can't be transient
  129   				LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer();
  130   				if ( li.getImplementation(session) == null ) {
  131   					return false;
  132   					// ie. we never have to null out a reference to
  133   					// an uninitialized proxy
  134   				}
  135   				else {
  136   					//unwrap it
  137   					object = li.getImplementation();
  138   				}
  139   			}
  140   	
  141   			// if it was a reference to self, don't need to nullify
  142   			// unless we are using native id generation, in which
  143   			// case we definitely need to nullify
  144   			if ( object == self ) {
  145   				return isEarlyInsert || (
  146   					isDelete &&
  147   					session.getFactory()
  148   						.getDialect()
  149   						.hasSelfReferentialForeignKeyBug()
  150   				);
  151   			}
  152   	
  153   			// See if the entity is already bound to this session, if not look at the
  154   			// entity identifier and assume that the entity is persistent if the
  155   			// id is not "unsaved" (that is, we rely on foreign keys to keep
  156   			// database integrity)
  157   	
  158   			EntityEntry entityEntry = session.getPersistenceContext().getEntry(object);
  159   			if ( entityEntry==null ) {
  160   				return isTransient(entityName, object, null, session);
  161   			}
  162   			else {
  163   				return entityEntry.isNullifiable(isEarlyInsert, session);
  164   			}
  165   	
  166   		}
  167   		
  168   	}
  169   	
  170   	/**
  171   	 * Is this instance persistent or detached?
  172   	 * If <tt>assumed</tt> is non-null, don't hit the database to make the 
  173   	 * determination, instead assume that value; the client code must be 
  174   	 * prepared to "recover" in the case that this assumed result is incorrect.
  175   	 */
  176   	public static boolean isNotTransient(String entityName, Object entity, Boolean assumed, SessionImplementor session) 
  177   	throws HibernateException {
  178   		if (entity instanceof HibernateProxy) return true;
  179   		if ( session.getPersistenceContext().isEntryFor(entity) ) return true;
  180   		return !isTransient(entityName, entity, assumed, session);
  181   	}
  182   	
  183   	/**
  184   	 * Is this instance, which we know is not persistent, actually transient?
  185   	 * If <tt>assumed</tt> is non-null, don't hit the database to make the 
  186   	 * determination, instead assume that value; the client code must be 
  187   	 * prepared to "recover" in the case that this assumed result is incorrect.
  188   	 */
  189   	public static boolean isTransient(String entityName, Object entity, Boolean assumed, SessionImplementor session) 
  190   	throws HibernateException {
  191   		
  192   		if (entity==LazyPropertyInitializer.UNFETCHED_PROPERTY) {
  193   			// an unfetched association can only point to
  194   			// an entity that already exists in the db
  195   			return false;
  196   		}
  197   		
  198   		// let the interceptor inspect the instance to decide
  199   		Boolean isUnsaved = session.getInterceptor().isTransient(entity);
  200   		if (isUnsaved!=null) return isUnsaved.booleanValue();
  201   		
  202   		// let the persister inspect the instance to decide
  203   		EntityPersister persister = session.getEntityPersister(entityName, entity);
  204   		isUnsaved = persister.isTransient(entity, session);
  205   		if (isUnsaved!=null) return isUnsaved.booleanValue();
  206   
  207   		// we use the assumed value, if there is one, to avoid hitting
  208   		// the database
  209   		if (assumed!=null) return assumed.booleanValue();
  210   		
  211   		// hit the database, after checking the session cache for a snapshot
  212   		Object[] snapshot = session.getPersistenceContext()
  213   		        .getDatabaseSnapshot( persister.getIdentifier( entity, session.getEntityMode() ), persister );
  214   		return snapshot==null;
  215   
  216   	}
  217   
  218   	/**
  219   	 * Return the identifier of the persistent or transient object, or throw
  220   	 * an exception if the instance is "unsaved"
  221   	 *
  222   	 * Used by OneToOneType and ManyToOneType to determine what id value should 
  223   	 * be used for an object that may or may not be associated with the session. 
  224   	 * This does a "best guess" using any/all info available to use (not just the 
  225   	 * EntityEntry).
  226   	 */
  227   	public static Serializable getEntityIdentifierIfNotUnsaved(
  228   			final String entityName, 
  229   			final Object object, 
  230   			final SessionImplementor session) 
  231   	throws HibernateException {
  232   		if ( object == null ) {
  233   			return null;
  234   		}
  235   		else {
  236   			Serializable id = session.getContextEntityIdentifier( object );
  237   			if ( id == null ) {
  238   				// context-entity-identifier returns null explicitly if the entity
  239   				// is not associated with the persistence context; so make some
  240   				// deeper checks...
  241   				if ( isTransient(entityName, object, Boolean.FALSE, session) ) {
  242   					throw new TransientObjectException(
  243   							"object references an unsaved transient instance - save the transient instance before flushing: " +
  244   							(entityName == null ? session.guessEntityName( object ) : entityName)
  245   					);
  246   				}
  247   				id = session.getEntityPersister( entityName, object ).getIdentifier( object, session.getEntityMode() );
  248   			}
  249   			return id;
  250   		}
  251   	}
  252   
  253   }

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