Package com.breezejs.hib

Source Code of com.breezejs.hib.HibernateContext

package com.breezejs.hib;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.hibernate.EntityMode;
import org.hibernate.FlushMode;
import org.hibernate.PropertyValueException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.type.ComponentType;
import org.hibernate.type.Type;

import com.breezejs.save.*;

public class HibernateContext extends ContextProvider {

  private Metadata metadataMap;
  private Session session;
  private List<EntityError> entityErrors = new ArrayList<EntityError>();
  private Map<EntityInfo, KeyMapping> entityKeyMapping = new HashMap<EntityInfo, KeyMapping>();

  /**
   * @param session Hibernate session to be used for saving
   * @param metadataMap metadata from MetadataBuilder
   */
  public HibernateContext(Session session, Metadata metadataMap) {
    this.session = session;
    this.metadataMap = metadataMap;
  }
 
  /**
     * Allows subclasses to process entities before they are saved.  This method is called
     * after BeforeSaveEntities(saveMap), and before any session.Save methods are called.
     * The foreign-key associations on the entities have been resolved, relating the entities
     * to each other, and attaching proxies for other many-to-one associations.
   *
   * @param entitiesToPersist List of entities in the order they will be saved
   * @return The same entitiesToPersist.  Overrides of this method may modify the list.
   */
  public List<EntityInfo> beforeSaveEntityGraph(List<EntityInfo> entitiesToPersist) {
    return entitiesToPersist;
  }

  /**
   * Persist the changes to the entities in the saveMap.
   * This implements the abstract method in ContextProvider.
   * Assigns saveWorkState.KeyMappings, which map the temporary keys to their real generated keys.
   * Note that this method sets session.FlushMode = FlushMode.MANUAL, so manual flushes are required.
   * @param saveWorkState
   */
  protected void saveChangesCore(SaveWorkState saveWorkState) {
    Map<Class, List<EntityInfo>> saveMap = saveWorkState.saveMap;
    session.setFlushMode(FlushMode.MANUAL);
    Transaction tx = session.getTransaction();
    boolean hasExistingTransaction = tx.isActive();
    if (!hasExistingTransaction)
      tx.begin();
    try {
      // Relate entities in the saveMap to other entities, so Hibernate can save the FK values.
      RelationshipFixer fixer = getRelationshipFixer(saveMap);     
      List<EntityInfo> saveOrder = fixer.fixupRelationships();
     
      // Allow subclass to process entities before we save them
      saveOrder = beforeSaveEntityGraph(saveOrder);
     
      processSaves(saveOrder);

      session.flush();
      refreshFromSession(saveMap);
      if (!hasExistingTransaction)
        tx.commit();
      fixer.removeRelationships();
    } catch (PropertyValueException pve) {
      // Hibernate can throw this
      if (tx.isActive())
        tx.rollback();
      entityErrors.add(new EntityError("PropertyValueException", pve.getEntityName(), null,
          pve.getPropertyName(), pve.getMessage()));
      saveWorkState.entityErrors = entityErrors;
    } catch (Exception ex) {
      if (tx.isActive())
        tx.rollback();
      throw ex;
    } finally {
      //          if (!hasExistingTransaction) tx.Dispose();
    }

    saveWorkState.keyMappings = updateAutoGeneratedKeys(saveWorkState.entitiesWithAutoGeneratedKeys);
  }
 
  /**
   * Get a new RelationshipFixer using the saveMap and the foreign-key map from the metadata.
   * @param saveMap
   * @return
   */
  protected RelationshipFixer getRelationshipFixer(Map<Class, List<EntityInfo>> saveMap) {
    // Get the map of foreign key relationships
    Map<String, String> fkMap = metadataMap.foreignKeyMap;
    return new RelationshipFixer(saveMap, fkMap, session);
  }

  /**
   * Persist the changes to the entities in the saveOrder.
   * @param saveMap
   */
  protected void processSaves(List<EntityInfo> saveOrder) {

    SessionFactory sf = session.getSessionFactory();
    for (EntityInfo entityInfo : saveOrder) {
          Class entityType = entityInfo.entity.getClass();
          ClassMetadata classMeta = sf.getClassMetadata(entityType);
          addKeyMapping(entityInfo, entityType, classMeta);
          processEntity(entityInfo, classMeta);
    }
  }

  /**
   * Add, update, or delete the entity according to its EntityState.
   * @param entityInfo
   * @param classMeta
   */
  protected void processEntity(EntityInfo entityInfo, ClassMetadata classMeta) {
    Object entity = entityInfo.entity;
    EntityState state = entityInfo.entityState;

    // Restore the old value of the concurrency column so Hibernate will be able to save the entity
    if (classMeta.isVersioned()) {
      restoreOldVersionValue(entityInfo, classMeta);
    }

    if (state == EntityState.Modified) {
      session.update(entity);
    } else if (state == EntityState.Added) {
      session.save(entity);
    } else if (state == EntityState.Deleted) {
      session.delete(entity);
    } else {
          // Ignore EntityState.Unchanged.  Too many problems using session.Lock or session.Merge
          //session.Lock(entity, LockMode.None);
    }
  }

  /**
   * Record the value of the temporary key in EntityKeyMapping
   * @param entityInfo
   * @param type
   * @param meta
   */
  protected void addKeyMapping(EntityInfo entityInfo, Class type, ClassMetadata meta) {
    if (entityInfo.entityState == EntityState.Added) {
      Object entity = entityInfo.entity;
      Object id = getIdentifier(entity, meta);
      KeyMapping km = new KeyMapping(type.getName(), id);
      entityKeyMapping.put(entityInfo, km);
    }
  }

  /**
   * Get the identifier value for the entity.  If the entity does not have an
   * identifier property, or natural identifiers defined, then the entity itself is returned.
   * @param entity
   * @param meta
   * @return
   */
  protected Object getIdentifier(Object entity, ClassMetadata meta) {
    Class type = entity.getClass();
    if (meta == null)
      meta = session.getSessionFactory().getClassMetadata(type);

    Type idType = meta.getIdentifierType();
    if (idType != null) {
      Serializable id = meta.getIdentifier(entity, null);
      if (idType.isComponentType()) {
        ComponentType compType = (ComponentType) idType;
        return compType.getPropertyValues(id, EntityMode.POJO);
      } else {
        return id;
      }
    } else if (meta.hasNaturalIdentifier()) {
      int[] idprops = meta.getNaturalIdentifierProperties();
      Object[] values = meta.getPropertyValues(entity);
      Object[] idvalues = new Object[idprops.length];
      for (int i = 0; i < idprops.length; i++) {
        idvalues[i] = values[idprops[i]];
      }
      return idvalues;
    }
    return entity;
  }

  /**
   * Get the identifier value for the entity as an object[].  This is needed for creating an EntityError.
   * @param entity
   * @param meta
   * @return
   */
  protected Object[] getIdentifierAsArray(Object entity, ClassMetadata meta) {
    Object value = getIdentifier(entity, meta);
    if (value.getClass().isArray()) {
      return (Object[]) value;
    } else {
      return new Object[] { value };
    }
  }

  /**
   * Restore the old value of the concurrency column so Hibernate will save the entity.
   * Otherwise it will complain because Breeze has already changed the value.
   * @param entityInfo
   * @param classMeta
   */
  protected void restoreOldVersionValue(EntityInfo entityInfo, ClassMetadata classMeta) {
    if (entityInfo.originalValuesMap == null || entityInfo.originalValuesMap.size() == 0)
      return;
    int vcol = classMeta.getVersionProperty();
    String vname = classMeta.getPropertyNames()[vcol];
    Object oldVersion = entityInfo.originalValuesMap.get(vname);
    if (oldVersion != null) {
      Object entity = entityInfo.entity;
      //        Class vtype = classMeta.getPropertyTypes()[vcol].getReturnedClass();
      //        if (vtype != oldVersion.getClass()) {
      //          // because JsonConvert makes all integers Int64
      //          oldVersion = vtype.getConstructor(oldVersion.getClass()).newInstance(oldVersion);
      //        }
      classMeta.setPropertyValue(entity, vname, oldVersion);
    }
  }

  /**
   * Update the KeyMappings with their real values.
   * @param entitiesWithAutoGeneratedKeys
   * @return
   */
  protected List<KeyMapping> updateAutoGeneratedKeys(List<EntityInfo> entitiesWithAutoGeneratedKeys) {
    List<KeyMapping> list = new ArrayList<KeyMapping>();
    for (EntityInfo entityInfo : entitiesWithAutoGeneratedKeys) {
      KeyMapping km = entityKeyMapping.get(entityInfo);
      if (km != null && km.getTempValue() != null) {
        Object entity = entityInfo.entity;
        Object id = getIdentifier(entity, null);
        km.setRealValue(id);
        list.add(km);
      }
    }
    return list;
  }

  /**
   * Refresh the entities from the database.  This picks up changes due to triggers, etc.
   * and makes Hibernate update the foreign keys.
   * @param saveMap
   */
  protected void refreshFromSession(Map<Class, List<EntityInfo>> saveMap) {
      for (Entry<Class, List<EntityInfo>> entry : saveMap.entrySet()) {
            for (EntityInfo entityInfo : entry.getValue()) {
                if (entityInfo.entityState == EntityState.Added || entityInfo.entityState == EntityState.Modified)
                    session.refresh(entityInfo.entity);
            }
        }     
   
  }
}
TOP

Related Classes of com.breezejs.hib.HibernateContext

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.