Package org.hibernate.ogm.loader

Source Code of org.hibernate.ogm.loader.OgmLoader

/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* JBoss, Home of Professional Open Source
* Copyright 2010-2011 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* 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,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA  02110-1301, USA.
*/
package org.hibernate.ogm.loader;

import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.infinispan.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.WrongClassException;
import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.collection.PersistentCollection;
import org.hibernate.engine.EntityUniqueKey;
import org.hibernate.engine.PersistenceContext;
import org.hibernate.engine.QueryParameters;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.engine.TwoPhaseLoad;
import org.hibernate.event.EventSource;
import org.hibernate.event.PostLoadEvent;
import org.hibernate.event.PreLoadEvent;
import org.hibernate.loader.CollectionAliases;
import org.hibernate.loader.entity.UniqueEntityLoader;
import org.hibernate.ogm.grid.EntityKey;
import org.hibernate.ogm.jdbc.TupleAsMapResultSet;
import org.hibernate.ogm.metadata.GridMetadataManager;
import org.hibernate.ogm.metadata.GridMetadataManagerHelper;
import org.hibernate.ogm.persister.CollectionPhysicalModel;
import org.hibernate.ogm.persister.EntityKeyBuilder;
import org.hibernate.ogm.persister.OgmCollectionPersister;
import org.hibernate.ogm.persister.OgmEntityPersister;
import org.hibernate.ogm.util.impl.PropertyMetadataProvider;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.persister.entity.UniqueKeyLoadable;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.type.AssociationType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.util.ArrayHelper;
import org.hibernate.util.StringHelper;

/**
* Load an entity from the Grid
*
* @author Emmanuel Bernard
*/
public class OgmLoader implements UniqueEntityLoader {
  private static final Logger log = LoggerFactory.getLogger( OgmLoader.class );

  private final OgmEntityPersister[] entityPersisters;
  private final OgmCollectionPersister[] collectionPersisters;
  private final GridMetadataManager gridManager;
  private final SessionFactoryImplementor factory;
  private LockMode[] defaultLockModes;
  private final CollectionAliases[] collectionAliases;

  /**
   * Load a collection
   */
  public OgmLoader(OgmCollectionPersister[] collectionPersisters) {
    if ( collectionPersisters == null || collectionPersisters.length == 0) {
      throw new AssertionFailure( "CollectionPersister[] must not be null or empty" );
    }
    this.entityPersisters = new OgmEntityPersister[] {};
    this.collectionPersisters = collectionPersisters;
    this.factory = collectionPersisters[0].getFactory();
    this.gridManager = GridMetadataManagerHelper.getGridMetadataManager( this.factory );

    //NONE, because its the requested lock mode, not the actual!
    final int fromSize = 1;
    this.defaultLockModes = ArrayHelper.fillArray( LockMode.NONE, fromSize );
    this.collectionAliases = new CollectionAliases[collectionPersisters.length];
    for (int i = 0 ; i < collectionPersisters.length ; i++) {
       collectionAliases[i] = new OgmColumnCollectionAliases( collectionPersisters[i] );
    }
  }

  /**
   * Load an entity
   */
  public OgmLoader(OgmEntityPersister[] entityPersisters) {
    if ( entityPersisters == null || entityPersisters.length == 0) {
      throw new AssertionFailure( "EntityPersister[] must not be null or empty" );
    }
    this.entityPersisters = entityPersisters;
    this.collectionPersisters = new OgmCollectionPersister[] {};
    this.factory = entityPersisters[0].getFactory();
    this.gridManager = GridMetadataManagerHelper.getGridMetadataManager( this.factory );

    //NONE, because its the requested lock mode, not the actual!
    final int fromSize = 1;
    this.defaultLockModes = ArrayHelper.fillArray( LockMode.NONE, fromSize );
    this.collectionAliases = new CollectionAliases[0];
  }

  /** returns the collection column names */
  public CollectionAliases[] getCollectionAliases() {
    return collectionAliases;
  }

  private SessionFactoryImplementor getFactory() {
    return factory;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Object load(Serializable id, Object optionalObject, SessionImplementor session) throws HibernateException {
    return load( id, optionalObject, session, LockOptions.NONE );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Object load(Serializable id, Object optionalObject, SessionImplementor session, LockOptions lockOptions) {
    final OgmEntityPersister currentPersister = entityPersisters[0];
    if ( log.isDebugEnabled() ) {
      log.debug(
          "loading entity: " +
          MessageHelper.infoString( currentPersister, id, currentPersister.getIdentifierType(), session.getFactory() )
        );
    }
    QueryParameters qp = new QueryParameters();
    qp.setPositionalParameterTypes( new Type[] { currentPersister.getIdentifierType() } );
    qp.setPositionalParameterValues( new Object[] { id } );
    qp.setOptionalObject( optionalObject );
    qp.setOptionalEntityName( currentPersister.getEntityName() );
    qp.setOptionalId( id );
    qp.setLockOptions( lockOptions );

    Object result = doQueryAndInitializeNonLazyCollections(
        session,
        qp,
        false
      );
    return result;
  }

  /**
   * Called by subclasses that initialize collections
   */
  public final void loadCollection(
          final SessionImplementor session,
          final Serializable id,
          final Type type) throws HibernateException {

    if ( log.isDebugEnabled() ) {
      log.debug(
          "loading collection: "+
          MessageHelper.collectionInfoString( getCollectionPersisters()[0], id, getFactory() )
        );
    }

    Serializable[] ids = new Serializable[]{id};
    QueryParameters qp = new QueryParameters( new Type[]{type}, ids, ids );
    doQueryAndInitializeNonLazyCollections(
        session,
        qp,
        true
      );

    log.debug("done loading collection");

  }

  OgmEntityPersister[] getEntityPersisters() {
    return entityPersisters;
  }

  /**
   * Load the entity activating the persistence context execution boundaries
   */
  private Object doQueryAndInitializeNonLazyCollections(
      SessionImplementor session,
      QueryParameters qp,
      boolean returnProxies) {


    //TODO handles the read only
    final PersistenceContext persistenceContext = session.getPersistenceContext();
    boolean defaultReadOnlyOrig = persistenceContext.isDefaultReadOnly();
    persistenceContext.beforeLoad();
    Object result;
    try {
      try {
        result = doQuery(
            session,
            qp,
            returnProxies
        );
      }
      finally {
        persistenceContext.afterLoad();
      }
      persistenceContext.initializeNonLazyCollections();
    }
    finally {
      // Restore the original default
      persistenceContext.setDefaultReadOnly( defaultReadOnlyOrig );
    }

    log.debug("done entity load");
    return result;
  }

  /**
   * Execute the physical query and initialize the various entities and collections
   */
  private Object doQuery(
      SessionImplementor session,
      QueryParameters qp,
      boolean returnProxies) {
    //TODO support lock timeout

    int entitySpan = entityPersisters.length;
    final List<Object> hydratedObjects = entitySpan == 0 ? null : new ArrayList<Object>( entitySpan * 10 );
    //TODO yuk! Is there a cleaner way to access the id?
    final Serializable id = qp.getOptionalId() != null ? qp.getOptionalId() : ( Serializable ) qp.getCollectionKeys()[0];
    TupleAsMapResultSet resultset = getResultSet( id, session );

    //Todo implement lockmode
    //final LockMode[] lockModesArray = getLockModes( queryParameters.getLockOptions() );
    //FIXME should we use subselects as it's closer to this process??

    //TODO is resultset a good marker, or should it be an ad-hoc marker??
    //It likely all depends on what resultset ends up being
    handleEmptyCollections( qp.getCollectionKeys(), resultset, session );

    final org.hibernate.engine.EntityKey[] keys = new org.hibernate.engine.EntityKey[entitySpan];

    //for each element in resultset
    //TODO should we collect List<Object> as result? Not necessary today
    Object result = null;
    try {
      while ( resultset.next() ) {
        result = getRowFromResultSet(
            resultset,
            session,
            qp,
            //lockmodeArray,
            qp.getOptionalId(),
            hydratedObjects,
            keys,
            returnProxies);
      }

      //TODO collect subselect result key
    }
    catch ( SQLException e ) {
      //never happens this is not a regular ResultSet
    }

    //end of for each element in resultset

    initializeEntitiesAndCollections( hydratedObjects, resultset, session, qp.isReadOnly( session ) );
    //TODO create subselects
    return result;
  }

  /**
   * If this is a collection initializer, we need to tell the session that a collection
   * is being initialized, to account for the possibility of the collection having
   * no elements (hence no rows in the result set).
   */
  private void handleEmptyCollections(
          final Serializable[] keys,
          final ResultSet resultSetId,
          final SessionImplementor session) {

    if ( keys != null ) {
      // this is a collection initializer, so we must create a collection
      // for each of the passed-in keys, to account for the possibility
      // that the collection is empty and has no rows in the result set

      CollectionPersister[] collectionPersisters = getCollectionPersisters();
      for ( int j=0; j<collectionPersisters.length; j++ ) {
        for ( int i = 0; i < keys.length; i++ ) {
          //handle empty collections

          if ( log.isDebugEnabled() ) {
            log.debug(
                "result set contains (possibly empty) collection: " +
                MessageHelper.collectionInfoString( collectionPersisters[j], keys[i], getFactory() )
              );
          }

          session.getPersistenceContext()
              .getLoadContexts()
              .getCollectionLoadContext( resultSetId )
              .getLoadingCollection( collectionPersisters[j], keys[i] );
        }
      }
    }

    // else this is not a collection initializer (and empty collections will
    // be detected by looking for the owner's identifier in the result set)
  }

  private Object getRowFromResultSet(
      ResultSet resultset,
      SessionImplementor session,
      QueryParameters qp,
      Serializable optionalId,
      List<Object> hydratedObjects,
      org.hibernate.engine.EntityKey[] keys,
      boolean returnProxies)
  throws SQLException {
    final OgmEntityPersister[] persisters = getEntityPersisters();
    final int entitySpan = persisters.length;
    extractKeysFromResultSet( session, optionalId, keys );

    registerNonExists( keys, persisters, session);

    //it's a non existing object: cut short
    if (resultset == null) {
      return null;
    }

    final Object[] row = getRow(
        resultset.unwrap( TupleAsMapResultSet.class ).getTuple(),
        persisters,
        keys,
        qp.getOptionalObject(),
        getOptionalObjectKey( qp, session ),
        getLockModes( qp.getLockOptions() ),
        hydratedObjects,
        session
    );

    readCollectionElements( row, resultset, session );

    if ( returnProxies ) {
      // now get an existing proxy for each row element (if there is one)
      for ( int i = 0; i < entitySpan; i++ ) {
        Object entity = row[i];
        Object proxy = session.getPersistenceContext().proxyFor( persisters[i], keys[i], entity );
        if ( entity != proxy ) {
          // force the proxy to resolve itself
          ( ( HibernateProxy ) proxy ).getHibernateLazyInitializer().setImplementation(entity);
          row[i] = proxy;
        }
      }
    }

    //applyPostLoadLocks
    //nothing to do atm it seems, the code in Hibernate Core does not do anything either

    return getResultColumnOrRow( row );
  }

  private void extractKeysFromResultSet(SessionImplementor session, Serializable optionalId, org.hibernate.engine.EntityKey[] keys) {
    //TODO Implement all Loader#extractKeysFromResultSet (ie resolution in case of composite ids with associations)
    //in the mean time the next two lines are the simplified version
    if (keys.length == 0) {
      //do nothing, this is a collection
    }
    else {
      final org.hibernate.engine.EntityKey key = new org.hibernate.engine.EntityKey( optionalId, entityPersisters[0], session.getEntityMode() );
      keys[0] = key;
    }
  }

  private TupleAsMapResultSet getResultSet(Serializable id, SessionImplementor session) {
    //TODO this if won't work when we will support collections inside the entity tuple but that will do for now
    final TupleAsMapResultSet resultset = new TupleAsMapResultSet();
    if ( getEntityPersisters().length > 0 ) {
      final Cache<EntityKey, Map<String, Object>> entityCache = GridMetadataManagerHelper.getEntityCache( gridManager );
      final Map<String,Object> entry = entityCache.get(
          new EntityKeyBuilder()
              .entityPersister( getEntityPersisters()[0] )
              .id( id )
              .getKey()
      );
      if ( entry != null ) {
        resultset.addTuple( entry );
      }
    }
    else {
      //collection persister
      if ( getCollectionPersisters().length != 1 ) {
        throw new AssertionFailure( "Found an unexpected number of collection persisters: " + getCollectionPersisters().length );
      }
      final CollectionPhysicalModel persister = (CollectionPhysicalModel) getCollectionPersisters()[0];
      PropertyMetadataProvider metadataProvider = new PropertyMetadataProvider()
        .gridManager( gridManager )
        .tableName( persister.getTableName() )
        .key( id )
        .keyColumnNames( persister.getKeyColumnNames() )
        .keyGridType( persister.getKeyGridType() )
        .session( session );
      final List<Map<String,Object>> entry = metadataProvider.getCollectionMetadata();
      if ( entry != null ) {
        for ( Map<String,Object> tuple : entry ) {
          resultset.addTuple( tuple );
        }
      }
    }
    return resultset;
  }

  private Object getResultColumnOrRow(Object[] row) {
    //getResultColumnOrRow
    //today we don't use this to apply the result transformer and we don't have operations to do like other loaders
    //TODO investigate further the result transformer application
    return row.length == 1 ? row[0] : row;

  }

  private void readCollectionElements(Object[] row, ResultSet resultSet, SessionImplementor session)
      throws HibernateException, SQLException {
    //TODO: make this handle multiple collection roles!

    final CollectionPersister[] collectionPersisters = getCollectionPersisters();
    if ( collectionPersisters != null ) {

      //we don't load more than one instance per row, shortcircuiting it for the moment
      final int[] collectionOwners = null;

      for ( int i=0; i<collectionPersisters.length; i++ ) {
        final CollectionAliases[] descriptors = getCollectionAliases();
        final boolean hasCollectionOwners = collectionOwners !=null &&
            collectionOwners[i] > -1;
        //true if this is a query and we are loading multiple instances of the same collection role
        //otherwise this is a CollectionInitializer and we are loading up a single collection or batch

        final Object owner = hasCollectionOwners ?
            row[ collectionOwners[i] ] :
            null; //if null, owner will be retrieved from session

        final CollectionPersister collectionPersister = collectionPersisters[i];
        final Serializable key;
        if ( owner == null ) {
          key = null;
        }
        else {
          key = collectionPersister.getCollectionType().getKeyOfOwner( owner, session );
          //TODO: old version did not require hashmap lookup:
          //keys[collectionOwner].getIdentifier()
        }

        readCollectionElement(
            owner,
            key,
            collectionPersister,
            descriptors[i],
            resultSet, //TODO CURRENT must use the same instance across all calls
            session
          );

      }

    }

  }

  /**
   * Read one collection element from the current row of the JDBC result set
   */
  private void readCollectionElement(
          final Object optionalOwner,
          final Serializable optionalKey,
          final CollectionPersister persister,
          final CollectionAliases descriptor,
          final ResultSet rs,
          final SessionImplementor session)
  throws HibernateException, SQLException {

    final PersistenceContext persistenceContext = session.getPersistenceContext();

    //implement persister.readKey using the grid type (later)
    final Serializable collectionRowKey = (Serializable) persister.readKey(
        rs,
        descriptor.getSuffixedKeyAliases(),
        session
      );

    if ( collectionRowKey != null ) {
      // we found a collection element in the result set

      if ( log.isDebugEnabled() ) {
        log.debug(
            "found row of collection: " +
            MessageHelper.collectionInfoString( persister, collectionRowKey, getFactory() )
          );
      }

      Object owner = optionalOwner;
      if ( owner == null ) {
        owner = persistenceContext.getCollectionOwner( collectionRowKey, persister );
        if ( owner == null ) {
          //TODO: This is assertion is disabled because there is a bug that means the
          //    original owner of a transient, uninitialized collection is not known
          //    if the collection is re-referenced by a different object associated
          //    with the current Session
          //throw new AssertionFailure("bug loading unowned collection");
        }
      }

      PersistentCollection rowCollection = persistenceContext.getLoadContexts()
          .getCollectionLoadContext( rs )
          .getLoadingCollection( persister, collectionRowKey );

      if ( rowCollection != null ) {
        rowCollection.readFrom(
            rs,
            persister,
            descriptor,
            owner );
      }

    }
    else if ( optionalKey != null ) {
      // we did not find a collection element in the result set, so we
      // ensure that a collection is created with the owner's identifier,
      // since what we have is an empty collection

      if ( log.isDebugEnabled() ) {
        log.debug(
            "result set contains (possibly empty) collection: " +
            MessageHelper.collectionInfoString( persister, optionalKey, getFactory() )
          );
      }

      persistenceContext.getLoadContexts()
          .getCollectionLoadContext( rs )
          .getLoadingCollection( persister, optionalKey ); // handle empty collection

    }

    // else no collection element, but also no owner

  }

  /**
   * copied from Loader#initializeEntitiesAndCollections
   */
  private void initializeEntitiesAndCollections(
      final List hydratedObjects,
      final ResultSet resultSetId,
      final SessionImplementor session,
      final boolean readOnly)
  throws HibernateException {

    final CollectionPersister[] collectionPersisters = getCollectionPersisters();
    if ( collectionPersisters != null ) {
      for ( int i=0; i<collectionPersisters.length; i++ ) {
        if ( collectionPersisters[i].isArray() ) {
          //for arrays, we should end the collection load before resolving
          //the entities, since the actual array instances are not instantiated
          //during loading
          //TODO: or we could do this polymorphically, and have two
          //      different operations implemented differently for arrays
          endCollectionLoad( resultSetId, session, collectionPersisters[i] );
        }
      }
    }

    //important: reuse the same event instances for performance!
    final PreLoadEvent pre;
    final PostLoadEvent post;
    if ( session.isEventSource() ) {
      pre = new PreLoadEvent( (EventSource) session );
      post = new PostLoadEvent( (EventSource) session );
    }
    else {
      pre = null;
      post = null;
    }

    if ( hydratedObjects!=null ) {
      int hydratedObjectsSize = hydratedObjects.size();
      if ( log.isTraceEnabled() ) {
        log.trace( "total objects hydrated: " + hydratedObjectsSize );
      }
      for ( int i = 0; i < hydratedObjectsSize; i++ ) {
        TwoPhaseLoad.initializeEntity( hydratedObjects.get(i), readOnly, session, pre, post );
      }
    }

    if ( collectionPersisters != null ) {
      for ( int i=0; i<collectionPersisters.length; i++ ) {
        if ( !collectionPersisters[i].isArray() ) {
          //for sets, we should end the collection load after resolving
          //the entities, since we might call hashCode() on the elements
          //TODO: or we could do this polymorphically, and have two
          //      different operations implemented differently for arrays
          endCollectionLoad( resultSetId, session, collectionPersisters[i] );
        }
      }
    }

  }

  /**
   * copied from Loader#endCollectionLoad
   */
  private void endCollectionLoad(
      final ResultSet resultSetId,
      final SessionImplementor session,
      final CollectionPersister collectionPersister) {
    //this is a query and we are loading multiple instances of the same collection role
    session.getPersistenceContext()
        .getLoadContexts()
        .getCollectionLoadContext( resultSetId )
        //.getCollectionLoadContext( ( ResultSet ) resultSetId )
        .endLoadingCollections( collectionPersister );
  }

  /**
   * An (optional) persister for a collection to be initialized; only
   * collection loaders return a non-null value
   *
   * copied from Loader#getCollectionPersisters
   */
  protected CollectionPersister[] getCollectionPersisters() {
    return collectionPersisters;
  }

  /**
   * @param lockOptions a collection of lock modes specified dynamically via the Query interface
   */
  protected LockMode[] getLockModes(LockOptions lockOptions) {
    if ( lockOptions == null ) {
      return defaultLockModes;
    }

    if ( lockOptions.getAliasLockCount() == 0
        && ( lockOptions.getLockMode() == null || LockMode.NONE.equals( lockOptions.getLockMode() ) ) ) {
      return defaultLockModes;
    }
    //FIXME fix the alias issue: could be that the "default" lock mode in LockOptions does that already
    LockMode lockMode = lockOptions.getEffectiveLockMode( "dummyaliasaswedon'thaveany" );
    if ( lockMode == null ) {
      //NONE, because its the requested lock mode, not the actual!
      lockMode = LockMode.NONE;
    }
    return new LockMode[] { lockMode };
  }

  /**
   * Copied from Loader#getOptionalObjectKey
   */
  private static org.hibernate.engine.EntityKey getOptionalObjectKey(QueryParameters queryParameters, SessionImplementor session) {
    final Object optionalObject = queryParameters.getOptionalObject();
    final Serializable optionalId = queryParameters.getOptionalId();
    final String optionalEntityName = queryParameters.getOptionalEntityName();

    if ( optionalObject != null && optionalEntityName != null ) {
      return new org.hibernate.engine.EntityKey(
          optionalId,
          session.getEntityPersister( optionalEntityName, optionalObject ),
          session.getEntityMode()
        );
    }
    else {
      return null;
    }
  }

  /**
   * Resolve any IDs for currently loaded objects, duplications within the
   * <tt>ResultSet</tt>, etc. Instantiate empty objects to be initialized from the
   * <tt>ResultSet</tt>. Return an array of objects (a row of results) and an
   * array of booleans (by side-effect) that determine whether the corresponding
   * object should be initialized.
   *
   * Copied from Loader#getRow
   */
  private Object[] getRow(
      final Map<String, Object> resultset,
          final OgmEntityPersister[] persisters,
          final org.hibernate.engine.EntityKey[] keys,
          final Object optionalObject,
          final org.hibernate.engine.EntityKey optionalObjectKey,
          final LockMode[] lockModes,
          final List hydratedObjects,
          final SessionImplementor session)
  throws HibernateException {
    if ( keys.length > 1 ) throw new NotYetImplementedException( "Loading involving several entities in one result set is not yet supported in OGM" );

    final int cols = persisters.length;

    if ( log.isDebugEnabled() ) {
      log.debug(
          "result row: " +
          StringHelper.toString( keys )
        );
    }

    final Object[] rowResults = new Object[cols];

    for ( int i = 0; i < cols; i++ ) {

      Object object = null;
      org.hibernate.engine.EntityKey key = keys[i];

      if ( keys[i] == null ) {
        //do nothing
      }
      else {
        //If the object is already loaded, return the loaded one
        object = session.getEntityUsingInterceptor( key );
        if ( object != null ) {
          //its already loaded so don't need to hydrate it
          instanceAlreadyLoaded(
              resultset,
              i,
              persisters[i],
              key,
              object,
              lockModes[i],
              session
            );
        }
        else {
          object = instanceNotYetLoaded(
              resultset,
              i,
              persisters[i],
              null, //We don't support rowId descriptors[i].getRowIdAlias(),
              key,
              lockModes[i],
              optionalObjectKey,
              optionalObject,
              hydratedObjects,
              session
            );
        }

      }

      rowResults[i] = object;

    }

    return rowResults;
  }

  /**
   * The entity instance is already in the session cache
   *
   * Copied from Loader#instanceAlreadyLoaded
   */
  private void instanceAlreadyLoaded(
      final Map<String, Object> resultset,
          final int i,
      //TODO create an interface for this usage
          final OgmEntityPersister persister,
          final org.hibernate.engine.EntityKey key,
          final Object object,
          final LockMode lockMode,
          final SessionImplementor session)
  throws HibernateException {
    if ( !persister.isInstance( object, session.getEntityMode() ) ) {
      throw new WrongClassException(
          "loaded object was of wrong class " + object.getClass(),
          key.getIdentifier(),
          persister.getEntityName()
        );
    }

    if ( LockMode.NONE != lockMode && upgradeLocks() ) { //no point doing this if NONE was requested

      final boolean isVersionCheckNeeded = persister.isVersioned() &&
          session.getPersistenceContext().getEntry(object)
              .getLockMode().lessThan( lockMode );
      // we don't need to worry about existing version being uninitialized
      // because this block isn't called by a re-entrant load (re-entrant
      // loads _always_ have lock mode NONE)
      if (isVersionCheckNeeded) {
        //we only check the version when _upgrading_ lock modes
        Object oldVersion = session.getPersistenceContext().getEntry( object ).getVersion();
        persister.checkVersionAndRaiseSOSE( key.getIdentifier(), oldVersion, session, resultset );
        //we need to upgrade the lock mode to the mode requested
        session.getPersistenceContext().getEntry(object)
            .setLockMode(lockMode);
      }
    }
  }

  /**
   * The entity instance is not in the session cache
   *
   * Copied from Loader#instanceNotYetLoaded
   */
  private Object instanceNotYetLoaded(
          final Map<String, Object> resultset,
          final int i,
          final Loadable persister,
          final String rowIdAlias,
          final org.hibernate.engine.EntityKey key,
          final LockMode lockMode,
          final org.hibernate.engine.EntityKey optionalObjectKey,
          final Object optionalObject,
          final List hydratedObjects,
          final SessionImplementor session)
  throws HibernateException {
    final String instanceClass = getInstanceClass(
        resultset,
        i,
        persister,
        key.getIdentifier(),
        session
      );

    final Object object;
    if ( optionalObjectKey != null && key.equals( optionalObjectKey ) ) {
      //its the given optional object
      object = optionalObject;
    }
    else {
      // instantiate a new instance
      object = session.instantiate( instanceClass, key.getIdentifier() );
    }

    //need to hydrate it.

    // grab its state from the ResultSet and keep it in the Session
    // (but don't yet initialize the object itself)
    // note that we acquire LockMode.READ even if it was not requested
    LockMode acquiredLockMode = lockMode == LockMode.NONE ? LockMode.READ : lockMode;
    loadFromResultSet(
        resultset,
        i,
        object,
        instanceClass,
        key,
        rowIdAlias,
        acquiredLockMode,
        persister,
        session
      );

    //materialize associations (and initialize the object) later
    hydratedObjects.add( object );

    return object;
  }

  /**
   * Determine the concrete class of an instance in the <tt>ResultSet</tt>
   *
   * Copied from Loader#getInstanceClass
   */
  private String getInstanceClass(
          final Map<String, Object> resultset,
          final int i,
          final Loadable persister,
          final Serializable id,
          final SessionImplementor session)
  throws HibernateException {
    //We don't have any discriminator so the class is always the one from the persister
    return persister.getEntityName();
  }

  /**
   * Hydrate the state an object from the SQL <tt>ResultSet</tt>, into
   * an array or "hydrated" values (do not resolve associations yet),
   * and pass the hydrates state to the session.
   *
   * Copied from Loader#loadFromResultSet
   */
  private void loadFromResultSet(
          final Map<String, Object> resultset,
          final int i,
          final Object object,
          final String instanceEntityName,
          final org.hibernate.engine.EntityKey key,
          final String rowIdAlias,
          final LockMode lockMode,
          final Loadable rootPersister,
          final SessionImplementor session)
  throws HibernateException {

    final Serializable id = key.getIdentifier();

    // Get the persister for the _subclass_
    final OgmEntityPersister persister = (OgmEntityPersister) getFactory().getEntityPersister( instanceEntityName );

    if ( log.isTraceEnabled() ) {
      log.trace(
          "Initializing object from ResultSet: " +
          MessageHelper.infoString( persister, id, getFactory() )
        );
    }

    //FIXME figure out what that means and what value should be set
    //boolean eagerPropertyFetch = isEagerPropertyFetchEnabled(i);
    boolean eagerPropertyFetch = true;

    // add temp entry so that the next step is circular-reference
    // safe - only needed because some types don't take proper
    // advantage of two-phase-load (esp. components)
    TwoPhaseLoad.addUninitializedEntity(
        key,
        object,
        persister,
        lockMode,
        !eagerPropertyFetch,
        session
      );

    //TODO what to do with that in OGM
//    //This is not very nice (and quite slow):
//    final String[][] cols = persister == rootPersister ?
//        getEntityAliases()[i].getSuffixedPropertyAliases() :
//        getEntityAliases()[i].getSuffixedPropertyAliases(persister);

    final Object[] values = persister.hydrate(
        resultset,
        id,
        object,
        rootPersister,
        //cols,
        eagerPropertyFetch,
        session
      );

    if ( persister.hasRowId() ) {
      throw new HibernateException( "Hibernate OGM does nto support row id");
    }
    final Object rowId = null;

    final AssociationType[] ownerAssociationTypes = getOwnerAssociationTypes();
    if ( ownerAssociationTypes != null && ownerAssociationTypes[i] != null ) {
      String ukName = ownerAssociationTypes[i].getRHSUniqueKeyPropertyName();
      if (ukName!=null) {
        final int index = ( ( UniqueKeyLoadable ) persister ).getPropertyIndex(ukName);
        final Type type = persister.getPropertyTypes()[index];

        // polymorphism not really handled completely correctly,
        // perhaps...well, actually its ok, assuming that the
        // entity name used in the lookup is the same as the
        // the one used here, which it will be

        EntityUniqueKey euk = new EntityUniqueKey(
            rootPersister.getEntityName(), //polymorphism comment above
            ukName,
            type.semiResolve( values[index], session, object ),
            type,
            session.getEntityMode(), session.getFactory()
          );
        session.getPersistenceContext().addEntity( euk, object );
      }
    }

    TwoPhaseLoad.postHydrate(
        persister,
        id,
        values,
        rowId,
        object,
        lockMode,
        !eagerPropertyFetch,
        session
      );

  }

  /**
   * Does this query return objects that might be already cached
   * by the session, whose lock mode may need upgrading
   */
  protected boolean upgradeLocks() {
    return true;
  }

  /**
   * For missing objects associated by one-to-one with another object in the
   * result set, register the fact that the the object is missing with the
   * session.
   *
   * copied form Loader#registerNonExists
   */
  private void registerNonExists(
          final org.hibernate.engine.EntityKey[] keys,
          final Loadable[] persisters,
          final SessionImplementor session) {

    final int[] owners = getOwners();
    if ( owners != null ) {

      EntityType[] ownerAssociationTypes = getOwnerAssociationTypes();
      for ( int i = 0; i < keys.length; i++ ) {

        int owner = owners[i];
        if ( owner > -1 ) {
          org.hibernate.engine.EntityKey ownerKey = keys[owner];
          if ( keys[i] == null && ownerKey != null ) {

            final PersistenceContext persistenceContext = session.getPersistenceContext();

            /*final boolean isPrimaryKey;
            final boolean isSpecialOneToOne;
            if ( ownerAssociationTypes == null || ownerAssociationTypes[i] == null ) {
              isPrimaryKey = true;
              isSpecialOneToOne = false;
            }
            else {
              isPrimaryKey = ownerAssociationTypes[i].getRHSUniqueKeyPropertyName()==null;
              isSpecialOneToOne = ownerAssociationTypes[i].getLHSPropertyName()!=null;
            }*/

            //TODO: can we *always* use the "null property" approach for everything?
            /*if ( isPrimaryKey && !isSpecialOneToOne ) {
              persistenceContext.addNonExistantEntityKey(
                  new EntityKey( ownerKey.getIdentifier(), persisters[i], session.getEntityMode() )
              );
            }
            else if ( isSpecialOneToOne ) {*/
            boolean isOneToOneAssociation = ownerAssociationTypes!=null &&
                ownerAssociationTypes[i]!=null &&
                ownerAssociationTypes[i].isOneToOne();
            if ( isOneToOneAssociation ) {
              persistenceContext.addNullProperty( ownerKey,
                  ownerAssociationTypes[i].getPropertyName() );
            }
            /*}
            else {
              persistenceContext.addNonExistantEntityUniqueKey( new EntityUniqueKey(
                  persisters[i].getEntityName(),
                  ownerAssociationTypes[i].getRHSUniqueKeyPropertyName(),
                  ownerKey.getIdentifier(),
                  persisters[owner].getIdentifierType(),
                  session.getEntityMode()
              ) );
            }*/
          }
        }
      }
    }
  }

  /**
   * An array of indexes of the entity that owns a one-to-one association
   * to the entity at the given index (-1 if there is no "owner").  The
   * indexes contained here are relative to the result of
   * {@link #getEntityPersisters}.
   *
   * @return The owner indicators (see discussion above).
   */
  protected int[] getOwners() {
    return null;
  }

  /**
   * An array of the owner types corresponding to the {@link #getOwners()}
   * returns.  Indices indicating no owner would be null here.
   *
   * @return The types for the owners.
   */
  protected EntityType[] getOwnerAssociationTypes() {
    return null;
  }
}
TOP

Related Classes of org.hibernate.ogm.loader.OgmLoader

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.