Package org.hibernate.engine

Source Code of org.hibernate.engine.ForeignKeys

//$Id: ForeignKeys.java 10133 2006-07-24 10:35:25Z max.andersen@jboss.com $
package org.hibernate.engine;

import java.io.Serializable;

import org.hibernate.HibernateException;
import org.hibernate.TransientObjectException;
import org.hibernate.intercept.LazyPropertyInitializer;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import org.hibernate.type.AbstractComponentType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;

/**
* Algorithms related to foreign key constraint transparency
*
* @author Gavin King
*/
public final class ForeignKeys {
 
  private ForeignKeys() {}
 
  public static class Nullifier {
 
    private final boolean isDelete;
    private final boolean isEarlyInsert;
    private final SessionImplementor session;
    private final Object self;
   
    public Nullifier(Object self, boolean isDelete, boolean isEarlyInsert, SessionImplementor session) {
      this.isDelete = isDelete;
      this.isEarlyInsert = isEarlyInsert;
      this.session = session;
      this.self = self;
    }
   
    /**
     * Nullify all references to entities that have not yet
     * been inserted in the database, where the foreign key
     * points toward that entity
     */
    public void nullifyTransientReferences(final Object[] values, final Type[] types)
    throws HibernateException {
      for ( int i = 0; i < types.length; i++ ) {
        values[i] = nullifyTransientReferences( values[i], types[i] );
      }
    }
 
    /**
     * Return null if the argument is an "unsaved" entity (ie.
     * one with no existing database row), or the input argument
     * otherwise. This is how Hibernate avoids foreign key constraint
     * violations.
     */
    private Object nullifyTransientReferences(final Object value, final Type type)
    throws HibernateException {
      if ( value == null ) {
        return null;
      }
      else if ( type.isEntityType() ) {
        EntityType entityType = (EntityType) type;
        if ( entityType.isOneToOne() ) {
          return value;
        }
        else {
          String entityName = entityType.getAssociatedEntityName();
          return isNullifiable(entityName, value) ? null : value;
        }
      }
      else if ( type.isAnyType() ) {
        return isNullifiable(null, value) ? null : value;
      }
      else if ( type.isComponentType() ) {
        AbstractComponentType actype = (AbstractComponentType) type;
        Object[] subvalues = actype.getPropertyValues(value, session);
        Type[] subtypes = actype.getSubtypes();
        boolean substitute = false;
        for ( int i = 0; i < subvalues.length; i++ ) {
          Object replacement = nullifyTransientReferences( subvalues[i], subtypes[i] );
          if ( replacement != subvalues[i] ) {
            substitute = true;
            subvalues[i] = replacement;
          }
        }
        if (substitute) actype.setPropertyValues( value, subvalues, session.getEntityMode() );
        return value;
      }
      else {
        return value;
      }
    }
 
    /**
     * Determine if the object already exists in the database,
     * using a "best guess"
     */
    private boolean isNullifiable(final String entityName, Object object)
    throws HibernateException {
     
      if (object==LazyPropertyInitializer.UNFETCHED_PROPERTY) return false; //this is kinda the best we can do...
     
      if ( object instanceof HibernateProxy ) {
        // if its an uninitialized proxy it can't be transient
        LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer();
        if ( li.getImplementation(session) == null ) {
          return false;
          // ie. we never have to null out a reference to
          // an uninitialized proxy
        }
        else {
          //unwrap it
          object = li.getImplementation();
        }
      }
 
      // if it was a reference to self, don't need to nullify
      // unless we are using native id generation, in which
      // case we definitely need to nullify
      if ( object == self ) {
        return isEarlyInsert || (
          isDelete &&
          session.getFactory()
            .getDialect()
            .hasSelfReferentialForeignKeyBug()
        );
      }
 
      // See if the entity is already bound to this session, if not look at the
      // entity identifier and assume that the entity is persistent if the
      // id is not "unsaved" (that is, we rely on foreign keys to keep
      // database integrity)
 
      EntityEntry entityEntry = session.getPersistenceContext().getEntry(object);
      if ( entityEntry==null ) {
        return isTransient(entityName, object, null, session);
      }
      else {
        return entityEntry.isNullifiable(isEarlyInsert, session);
      }
 
    }
   
  }
 
  /**
   * Is this instance persistent or detached?
   * If <tt>assumed</tt> is non-null, don't hit the database to make the
   * determination, instead assume that value; the client code must be
   * prepared to "recover" in the case that this assumed result is incorrect.
   */
  public static boolean isNotTransient(String entityName, Object entity, Boolean assumed, SessionImplementor session)
  throws HibernateException {
    if (entity instanceof HibernateProxy) return true;
    if ( session.getPersistenceContext().isEntryFor(entity) ) return true;
    return !isTransient(entityName, entity, assumed, session);
  }
 
  /**
   * Is this instance, which we know is not persistent, actually transient?
   * If <tt>assumed</tt> is non-null, don't hit the database to make the
   * determination, instead assume that value; the client code must be
   * prepared to "recover" in the case that this assumed result is incorrect.
   */
  public static boolean isTransient(String entityName, Object entity, Boolean assumed, SessionImplementor session)
  throws HibernateException {
   
    if (entity==LazyPropertyInitializer.UNFETCHED_PROPERTY) {
      // an unfetched association can only point to
      // an entity that already exists in the db
      return false;
    }
   
    // let the interceptor inspect the instance to decide
    Boolean isUnsaved = session.getInterceptor().isTransient(entity);
    if (isUnsaved!=null) return isUnsaved.booleanValue();
   
    // let the persister inspect the instance to decide
    EntityPersister persister = session.getEntityPersister(entityName, entity);
    isUnsaved = persister.isTransient(entity, session);
    if (isUnsaved!=null) return isUnsaved.booleanValue();

    // we use the assumed value, if there is one, to avoid hitting
    // the database
    if (assumed!=null) return assumed.booleanValue();
   
    // hit the database, after checking the session cache for a snapshot
    Object[] snapshot = session.getPersistenceContext()
            .getDatabaseSnapshot( persister.getIdentifier( entity, session.getEntityMode() ), persister );
    return snapshot==null;

  }

  /**
   * Return the identifier of the persistent or transient object, or throw
   * an exception if the instance is "unsaved"
   *
   * Used by OneToOneType and ManyToOneType to determine what id value should
   * be used for an object that may or may not be associated with the session.
   * This does a "best guess" using any/all info available to use (not just the
   * EntityEntry).
   */
  public static Serializable getEntityIdentifierIfNotUnsaved(
      final String entityName,
      final Object object,
      final SessionImplementor session)
  throws HibernateException {
    if ( object == null ) {
      return null;
    }
    else {
      Serializable id = session.getContextEntityIdentifier( object );
      if ( id == null ) {
        // context-entity-identifier returns null explicitly if the entity
        // is not associated with the persistence context; so make some
        // deeper checks...
        if ( isTransient(entityName, object, Boolean.FALSE, session) ) {
          throw new TransientObjectException(
              "object references an unsaved transient instance - save the transient instance before flushing: " +
              (entityName == null ? session.guessEntityName( object ) : entityName)
          );
        }
        id = session.getEntityPersister( entityName, object ).getIdentifier( object, session.getEntityMode() );
      }
      return id;
    }
  }

}
TOP

Related Classes of org.hibernate.engine.ForeignKeys

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.