/**
   * Prepares the save call using a newly generated id.
   *
   * @param entity The entity to be saved
   * @param entityName The entity-name for the entity to be saved
   * @param anything Generally cascade-specific information.
   * @param source The session which is the source of this save event.
   * @param requiresImmediateIdAccess does the event context require access to the identifier
   *     immediately after execution of this method (if not, post-insert style id generators may be
   *     postponed if we are outside a transaction).
   * @return The id used to save the entity; may be null depending on the type of id generator used
   *     and the requiresImmediateIdAccess value
   */
  protected Serializable saveWithGeneratedId(
      Object entity,
      String entityName,
      Object anything,
      EventSource source,
      boolean requiresImmediateIdAccess) {
    EntityPersister persister = source.getEntityPersister(entityName, entity);
    Serializable generatedId = persister.getIdentifierGenerator().generate(source, entity);
    if (generatedId == null) {
      throw new IdentifierGenerationException("null id generated for:" + entity.getClass());
    } else if (generatedId == IdentifierGeneratorHelper.SHORT_CIRCUIT_INDICATOR) {
      return source.getIdentifier(entity);
    } else if (generatedId == IdentifierGeneratorHelper.POST_INSERT_INDICATOR) {
      return performSave(
          entity, null, persister, true, anything, source, requiresImmediateIdAccess);
    } else {
      // TODO: define toString()s for generators
      if (LOG.isDebugEnabled()) {
        LOG.debugf(
            "Generated identifier: %s, using strategy: %s",
            persister.getIdentifierType().toLoggableString(generatedId, source.getFactory()),
            persister.getIdentifierGenerator().getClass().getName());
      }

      return performSave(entity, generatedId, persister, false, anything, source, true);
    }
  }
  /** make sure user didn't mangle the id */
  public void checkId(
      Object object,
      EntityPersister persister,
      Serializable id,
      EntityMode entityMode,
      SessionImplementor session)
      throws HibernateException {

    if (id != null && id instanceof DelayedPostInsertIdentifier) {
      // this is a situation where the entity id is assigned by a post-insert generator
      // and was saved outside the transaction forcing it to be delayed
      return;
    }

    if (persister.canExtractIdOutOfEntity()) {

      Serializable oid = persister.getIdentifier(object, session);
      if (id == null) {
        throw new AssertionFailure(
            "null id in "
                + persister.getEntityName()
                + " entry (don't flush the Session after an exception occurs)");
      }
      if (!persister.getIdentifierType().isEqual(id, oid, entityMode, session.getFactory())) {
        throw new HibernateException(
            "identifier of an instance of "
                + persister.getEntityName()
                + " was altered from "
                + id
                + " to "
                + oid);
      }
    }
  }
 private CacheKey buildCacheKey(Serializable identifier, EntityPersister p) {
   return new CacheKey(
       identifier,
       p.getIdentifierType(),
       p.getRootEntityName(),
       EntityMode.POJO,
       SessionFactoryImpl.this);
 }
 public int compareTo(Object other) {
   EntityAction action = (EntityAction) other;
   // sort first by entity name
   int roleComparison = entityName.compareTo(action.entityName);
   if (roleComparison != 0) {
     return roleComparison;
   } else {
     // then by id
     return persister.getIdentifierType().compare(id, action.id, session.getEntityMode());
   }
 }
  /**
   * Attempts to load the entity from the second-level cache.
   *
   * @return The entity from the second-level cache, or null.
   * @throws HibernateException
   */
  protected Object loadFromSecondLevelCache(
      final LoadEvent event,
      final EntityPersister persister,
      final LoadEventListener.LoadType options)
      throws HibernateException {

    final SessionImplementor source = event.getSession();

    final boolean useCache =
        persister.hasCache()
            && source.getCacheMode().isGetEnabled()
            && event.getLockMode().lessThan(LockMode.READ);

    if (useCache) {

      final SessionFactoryImplementor factory = source.getFactory();

      final CacheKey ck =
          new CacheKey(
              event.getEntityId(),
              persister.getIdentifierType(),
              persister.getRootEntityName(),
              source.getEntityMode(),
              source.getFactory());
      Object ce = persister.getCache().get(ck, source.getTimestamp());

      if (factory.getStatistics().isStatisticsEnabled()) {
        if (ce == null) {
          factory
              .getStatisticsImplementor()
              .secondLevelCacheMiss(persister.getCache().getRegionName());
        } else {
          factory
              .getStatisticsImplementor()
              .secondLevelCacheHit(persister.getCache().getRegionName());
        }
      }

      if (ce != null) {

        CacheEntry entry = (CacheEntry) persister.getCacheEntryStructure().destructure(ce, factory);

        // Entity was found in second-level cache...
        return assembleCacheEntry(entry, event.getEntityId(), persister, event);
      }
    }

    return null;
  }
  @Override
  public void doAfterTransactionCompletion(boolean success, SessionImplementor session)
      throws HibernateException {
    final EntityPersister persister = getPersister();
    if (success && isCachePutEnabled(persister, getSession())) {
      final CacheKey ck =
          getSession()
              .generateCacheKey(
                  getId(), persister.getIdentifierType(), persister.getRootEntityName());
      final boolean put = persister.getCacheAccessStrategy().afterInsert(ck, cacheEntry, version);

      if (put && getSession().getFactory().getStatistics().isStatisticsEnabled()) {
        getSession()
            .getFactory()
            .getStatisticsImplementor()
            .secondLevelCachePut(getPersister().getCacheAccessStrategy().getRegion().getName());
      }
    }
    postCommitInsert();
  }
  /**
   * If the class to be loaded has been configured with a cache, then lock given id in that cache
   * and then perform the load.
   *
   * @return The loaded entity
   * @throws HibernateException
   */
  protected Object lockAndLoad(
      final LoadEvent event,
      final EntityPersister persister,
      final EntityKey keyToLoad,
      final LoadEventListener.LoadType options,
      final SessionImplementor source)
      throws HibernateException {

    CacheConcurrencyStrategy.SoftLock lock = null;
    final CacheKey ck;
    if (persister.hasCache()) {
      ck =
          new CacheKey(
              event.getEntityId(),
              persister.getIdentifierType(),
              persister.getRootEntityName(),
              source.getEntityMode(),
              source.getFactory());
      lock = persister.getCache().lock(ck, null);
    } else {
      ck = null;
    }

    Object entity;
    try {
      entity = load(event, persister, keyToLoad, options);
    } finally {
      if (persister.hasCache()) {
        persister.getCache().release(ck, lock);
      }
    }

    Object proxy =
        event.getSession().getPersistenceContext().proxyFor(persister, keyToLoad, entity);

    return proxy;
  }
  @Override
  public void execute() throws HibernateException {
    nullifyTransientReferencesIfNotAlready();

    final EntityPersister persister = getPersister();
    final SessionImplementor session = getSession();
    final Object instance = getInstance();
    final Serializable id = getId();

    final boolean veto = preInsert();

    // Don't need to lock the cache here, since if someone
    // else inserted the same pk first, the insert would fail

    if (!veto) {

      persister.insert(id, getState(), instance, session);

      final EntityEntry entry = session.getPersistenceContext().getEntry(instance);
      if (entry == null) {
        throw new AssertionFailure("possible non-threadsafe access to session");
      }

      entry.postInsert(getState());

      if (persister.hasInsertGeneratedProperties()) {
        persister.processInsertGeneratedProperties(id, instance, getState(), session);
        if (persister.isVersionPropertyGenerated()) {
          version = Versioning.getVersion(getState(), persister);
        }
        entry.postUpdate(instance, getState(), version);
      }

      getSession().getPersistenceContext().registerInsertedKey(getPersister(), getId());
    }

    final SessionFactoryImplementor factory = getSession().getFactory();

    if (isCachePutEnabled(persister, session)) {
      final CacheEntry ce = persister.buildCacheEntry(instance, getState(), version, session);
      cacheEntry = persister.getCacheEntryStructure().structure(ce);
      final CacheKey ck =
          session.generateCacheKey(
              id, persister.getIdentifierType(), persister.getRootEntityName());
      final boolean put = persister.getCacheAccessStrategy().insert(ck, cacheEntry, version);

      if (put && factory.getStatistics().isStatisticsEnabled()) {
        factory
            .getStatisticsImplementor()
            .secondLevelCachePut(getPersister().getCacheAccessStrategy().getRegion().getName());
      }
    }

    handleNaturalIdPostSaveNotifications(id);

    postInsert();

    if (factory.getStatistics().isStatisticsEnabled() && !veto) {
      factory.getStatisticsImplementor().insertEntity(getPersister().getEntityName());
    }

    markExecuted();
  }
  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());
    }
  }
  /**
   * Perform the second step of 2-phase load. Fully initialize the entity instance.
   *
   * <p>After processing a JDBC result set, we "resolve" all the associations between the entities
   * which were instantiated and had their state "hydrated" into an array
   */
  public static void initializeEntity(
      final Object entity,
      final boolean readOnly,
      final SessionImplementor session,
      final PreLoadEvent preLoadEvent,
      final PostLoadEvent postLoadEvent)
      throws HibernateException {

    // TODO: Should this be an InitializeEntityEventListener??? (watch out for performance!)

    final PersistenceContext persistenceContext = session.getPersistenceContext();
    EntityEntry entityEntry = persistenceContext.getEntry(entity);
    if (entityEntry == null) {
      throw new AssertionFailure("possible non-threadsafe access to the session");
    }
    EntityPersister persister = entityEntry.getPersister();
    Serializable id = entityEntry.getId();
    Object[] hydratedState = entityEntry.getLoadedState();

    if (log.isDebugEnabled())
      log.debug(
          "resolving associations for "
              + MessageHelper.infoString(persister, id, session.getFactory()));

    Type[] types = persister.getPropertyTypes();
    for (int i = 0; i < hydratedState.length; i++) {
      final Object value = hydratedState[i];
      if (value != LazyPropertyInitializer.UNFETCHED_PROPERTY
          && value != BackrefPropertyAccessor.UNKNOWN) {
        hydratedState[i] = types[i].resolve(value, session, entity);
      }
    }

    // Must occur after resolving identifiers!
    if (session.isEventSource()) {
      preLoadEvent.setEntity(entity).setState(hydratedState).setId(id).setPersister(persister);
      PreLoadEventListener[] listeners = session.getListeners().getPreLoadEventListeners();
      for (int i = 0; i < listeners.length; i++) {
        listeners[i].onPreLoad(preLoadEvent);
      }
    }

    persister.setPropertyValues(entity, hydratedState, session.getEntityMode());

    final SessionFactoryImplementor factory = session.getFactory();
    if (persister.hasCache() && session.getCacheMode().isPutEnabled()) {

      if (log.isDebugEnabled())
        log.debug(
            "adding entity to second-level cache: "
                + MessageHelper.infoString(persister, id, session.getFactory()));

      Object version = Versioning.getVersion(hydratedState, persister);
      CacheEntry entry =
          new CacheEntry(
              hydratedState,
              persister,
              entityEntry.isLoadedWithLazyPropertiesUnfetched(),
              version,
              session,
              entity);
      CacheKey cacheKey =
          new CacheKey(
              id,
              persister.getIdentifierType(),
              persister.getRootEntityName(),
              session.getEntityMode(),
              session.getFactory());
      boolean put =
          persister
              .getCache()
              .put(
                  cacheKey,
                  persister.getCacheEntryStructure().structure(entry),
                  session.getTimestamp(),
                  version,
                  persister.isVersioned() ? persister.getVersionType().getComparator() : null,
                  useMinimalPuts(
                      session,
                      entityEntry)); // we could use persister.hasLazyProperties() instead of true

      if (put && factory.getStatistics().isStatisticsEnabled()) {
        factory
            .getStatisticsImplementor()
            .secondLevelCachePut(persister.getCache().getRegionName());
      }
    }

    if (readOnly || !persister.isMutable()) {
      // no need to take a snapshot - this is a
      // performance optimization, but not really
      // important, except for entities with huge
      // mutable property values
      persistenceContext.setEntryStatus(entityEntry, Status.READ_ONLY);
    } else {
      // take a snapshot
      TypeFactory.deepCopy(
          hydratedState,
          persister.getPropertyTypes(),
          persister.getPropertyUpdateability(),
          hydratedState, // after setting values to object, entityMode
          session);
      persistenceContext.setEntryStatus(entityEntry, Status.MANAGED);
    }

    persister.afterInitialize(entity, entityEntry.isLoadedWithLazyPropertiesUnfetched(), session);

    if (session.isEventSource()) {
      postLoadEvent.setEntity(entity).setId(id).setPersister(persister);
      PostLoadEventListener[] listeners = session.getListeners().getPostLoadEventListeners();
      for (int i = 0; i < listeners.length; i++) {
        listeners[i].onPostLoad(postLoadEvent);
      }
    }

    if (log.isDebugEnabled())
      log.debug(
          "done materializing entity "
              + MessageHelper.infoString(persister, id, session.getFactory()));

    if (factory.getStatistics().isStatisticsEnabled()) {
      factory.getStatisticsImplementor().loadEntity(persister.getEntityName());
    }
  }