/**
   * Based on configured options, will either return a pre-existing proxy, generate a new proxy, or
   * perform an actual load.
   *
   * @return The result of the proxy/load operation.
   * @throws HibernateException
   */
  protected Object proxyOrLoad(
      final LoadEvent event,
      final EntityPersister persister,
      final EntityKey keyToLoad,
      final LoadEventListener.LoadType options)
      throws HibernateException {

    if (log.isTraceEnabled()) {
      log.trace(
          "loading entity: "
              + MessageHelper.infoString(
                  persister, event.getEntityId(), event.getSession().getFactory()));
    }

    if (!persister.hasProxy()) {
      // this class has no proxies (so do a shortcut)
      return load(event, persister, keyToLoad, options);
    } else {
      final PersistenceContext persistenceContext = event.getSession().getPersistenceContext();

      // look for a proxy
      Object proxy = persistenceContext.getProxy(keyToLoad);
      if (proxy != null) {
        return returnNarrowedProxy(event, persister, keyToLoad, options, persistenceContext, proxy);
      } else {
        if (options.isAllowProxyCreation()) {
          return createProxyIfNecessary(event, persister, keyToLoad, options, persistenceContext);
        } else {
          // return a newly loaded object
          return load(event, persister, keyToLoad, options);
        }
      }
    }
  }
  @SuppressWarnings("unchecked")
  @Override
  /**
   * Handle the given delete event. This is the cascaded form.
   *
   * @param event The delete event.
   * @param transientEntities The cache of entities already deleted
   * @throws HibernateException
   */
  public void onDelete(DeleteEvent event, Set transientEntities) throws HibernateException {
    final EventSource source = event.getSession();

    final PersistenceContext persistenceContext = source.getPersistenceContext();
    Object entity = persistenceContext.unproxyAndReassociate(event.getObject());
    EntityEntry entityEntry = persistenceContext.getEntry(entity);

    final EntityPersister persister = source.getEntityPersister(event.getEntityName(), entity);
    final Object version;

    if (persister.isVersioned()) {
      version = persister.getVersion(entity, source.getEntityMode());
      // Make sure version has not changed on deleted entities
      if ((entity instanceof TimelineEntity) && !((TimelineEntity) entity).isNew()) {
        if (!persister.getVersionType().isEqual(version, entityEntry.getVersion())) {
          throw new StaleObjectStateException(persister.getEntityName(), entityEntry.getId());
        }
      }
    }
    super.onDelete(event, transientEntities);
  }
 /**
  * Given that there is no pre-existing proxy. Check if the entity is already loaded. If it is,
  * return the entity, otherwise create and return a proxy.
  */
 private Object createProxyIfNecessary(
     final LoadEvent event,
     final EntityPersister persister,
     final EntityKey keyToLoad,
     final LoadEventListener.LoadType options,
     final PersistenceContext persistenceContext) {
   Object existing = persistenceContext.getEntity(keyToLoad);
   if (existing != null) {
     // return existing object or initialized proxy (unless deleted)
     log.trace("entity found in session cache");
     if (options.isCheckDeleted()) {
       EntityEntry entry = persistenceContext.getEntry(existing);
       throwObjectDeletedIfNecessary(event, entry);
     }
     return existing;
   } else {
     log.trace("creating new proxy for entity");
     // return new uninitialized proxy
     Object proxy = persister.createProxy(event.getEntityId(), event.getSession());
     persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey(keyToLoad);
     persistenceContext.addProxy(keyToLoad, proxy);
     return proxy;
   }
 }
 /** Given that there is a pre-existing proxy. Initialize it if necessary; narrow if necessary. */
 private Object returnNarrowedProxy(
     final LoadEvent event,
     final EntityPersister persister,
     final EntityKey keyToLoad,
     final LoadEventListener.LoadType options,
     final PersistenceContext persistenceContext,
     final Object proxy) {
   log.trace("entity proxy found in session cache");
   LazyInitializer li = ((HibernateProxy) proxy).getHibernateLazyInitializer();
   if (li.isUnwrap()) {
     return li.getImplementation();
   }
   // return existing or narrowed proxy
   Object impl =
       options.isAllowProxyCreation() ? null : load(event, persister, keyToLoad, options);
   return persistenceContext.narrowProxy(proxy, persister, keyToLoad, impl);
 }
  private Object assembleCacheEntry(
      final CacheEntry entry,
      final Serializable id,
      final EntityPersister persister,
      final LoadEvent event)
      throws HibernateException {

    final Object optionalObject = event.getInstanceToLoad();
    final EventSource session = event.getSession();
    final SessionFactoryImplementor factory = session.getFactory();

    if (log.isTraceEnabled()) {
      log.trace(
          "assembling entity from second-level cache: "
              + MessageHelper.infoString(persister, id, factory));
    }

    EntityPersister subclassPersister = factory.getEntityPersister(entry.getSubclass());
    Object result =
        optionalObject == null ? session.instantiate(subclassPersister, id) : optionalObject;

    // make it circular-reference safe
    TwoPhaseLoad.addUninitializedCachedEntity(
        new EntityKey(id, subclassPersister, session.getEntityMode()),
        result,
        subclassPersister,
        LockMode.NONE,
        entry.areLazyPropertiesUnfetched(),
        entry.getVersion(),
        session);

    Type[] types = subclassPersister.getPropertyTypes();
    Object[] values =
        entry.assemble(
            result,
            id,
            subclassPersister,
            session.getInterceptor(),
            session); // intializes result by side-effect
    TypeFactory.deepCopy(
        values, types, subclassPersister.getPropertyUpdateability(), values, session);

    Object version = Versioning.getVersion(values, subclassPersister);
    if (log.isTraceEnabled()) log.trace("Cached Version: " + version);

    final PersistenceContext persistenceContext = session.getPersistenceContext();
    persistenceContext.addEntry(
        result,
        Status.MANAGED,
        values,
        null,
        id,
        version,
        LockMode.NONE,
        true,
        subclassPersister,
        false,
        entry.areLazyPropertiesUnfetched());
    subclassPersister.afterInitialize(result, entry.areLazyPropertiesUnfetched(), session);
    persistenceContext.initializeNonLazyCollections();
    // upgrade the lock if necessary:
    // lock(result, lockMode);

    // PostLoad is needed for EJB3
    // TODO: reuse the PostLoadEvent...
    PostLoadEvent postLoadEvent =
        new PostLoadEvent(session).setEntity(result).setId(id).setPersister(persister);
    PostLoadEventListener[] listeners = session.getListeners().getPostLoadEventListeners();
    for (int i = 0; i < listeners.length; i++) {
      listeners[i].onPostLoad(postLoadEvent);
    }

    return result;
  }
  public void execute() throws HibernateException {
    Serializable id = getId();
    EntityPersister persister = getPersister();
    SessionImplementor session = getSession();
    Object instance = getInstance();

    boolean veto = preDelete();

    Object version = this.version;
    if (persister.isVersionPropertyGenerated()) {
      // we need to grab the version value from the entity, otherwise
      // we have issues with generated-version entities that may have
      // multiple actions queued during the same flush
      version = persister.getVersion(instance, session.getEntityMode());
    }

    final CacheKey ck;
    if (persister.hasCache()) {
      ck =
          new CacheKey(
              id,
              persister.getIdentifierType(),
              persister.getRootEntityName(),
              session.getEntityMode(),
              session.getFactory());
      lock = persister.getCacheAccessStrategy().lockItem(ck, version);
    } else {
      ck = null;
    }

    if (!isCascadeDeleteEnabled && !veto) {
      persister.delete(id, version, instance, session);
    }

    // postDelete:
    // After actually deleting a row, record the fact that the instance no longer
    // exists on the database (needed for identity-column key generation), and
    // remove it from the session cache
    final PersistenceContext persistenceContext = session.getPersistenceContext();
    EntityEntry entry = persistenceContext.removeEntry(instance);
    if (entry == null) {
      throw new AssertionFailure("possible nonthreadsafe access to session");
    }
    entry.postDelete();

    persistenceContext.removeEntity(entry.getEntityKey());
    persistenceContext.removeProxy(entry.getEntityKey());

    if (persister.hasCache()) {
      persister.getCacheAccessStrategy().remove(ck);
    }

    postDelete();

    if (getSession().getFactory().getStatistics().isStatisticsEnabled() && !veto) {
      getSession()
          .getFactory()
          .getStatisticsImplementor()
          .deleteEntity(getPersister().getEntityName());
    }
  }