@Override
  public void doAfterTransactionCompletion(boolean success, SessionImplementor session)
      throws CacheException {
    final EntityPersister persister = getPersister();
    if (persister.hasCache()) {
      final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy();
      final Object ck =
          cache.generateCacheKey(
              getId(), persister, session.getFactory(), session.getTenantIdentifier());

      if (success && cacheEntry != null /*!persister.isCacheInvalidationRequired()*/) {
        final boolean put = cacheAfterUpdate(cache, ck);

        if (put && getSession().getFactory().getStatistics().isStatisticsEnabled()) {
          getSession()
              .getFactory()
              .getStatisticsImplementor()
              .secondLevelCachePut(cache.getRegion().getName());
        }
      } else {
        cache.unlockItem(session, ck, lock);
      }
    }
    postCommitUpdate(success);
  }
 public void evictEntityRegion(String entityName) {
   EntityPersister p = getEntityPersister(entityName);
   if (p.hasCache()) {
     if (log.isDebugEnabled()) {
       log.debug("evicting second-level cache: " + p.getEntityName());
     }
     p.getCacheAccessStrategy().evictAll();
   }
 }
 public void evictEntity(String entityName, Serializable identifier) {
   EntityPersister p = getEntityPersister(entityName);
   if (p.hasCache()) {
     if (log.isDebugEnabled()) {
       log.debug(
           "evicting second-level cache: "
               + MessageHelper.infoString(p, identifier, SessionFactoryImpl.this));
     }
     p.getCacheAccessStrategy().evict(buildCacheKey(identifier, p));
   }
 }
  /**
   * Closes the session factory, releasing all held resources.
   *
   * <ol>
   *   <li>cleans up used cache regions and "stops" the cache provider.
   *   <li>close the JDBC connection
   *   <li>remove the JNDI binding
   * </ol>
   *
   * Note: Be aware that the sessionfactory instance still can be a "heavy" object memory wise after
   * close() has been called. Thus it is important to not keep referencing the instance to let the
   * garbage collector release the memory.
   */
  public void close() throws HibernateException {

    if (isClosed) {
      log.trace("already closed");
      return;
    }

    log.info("closing");

    isClosed = true;

    Iterator iter = entityPersisters.values().iterator();
    while (iter.hasNext()) {
      EntityPersister p = (EntityPersister) iter.next();
      if (p.hasCache()) {
        p.getCacheAccessStrategy().getRegion().destroy();
      }
    }

    iter = collectionPersisters.values().iterator();
    while (iter.hasNext()) {
      CollectionPersister p = (CollectionPersister) iter.next();
      if (p.hasCache()) {
        p.getCacheAccessStrategy().getRegion().destroy();
      }
    }

    if (settings.isQueryCacheEnabled()) {
      queryCache.destroy();

      iter = queryCaches.values().iterator();
      while (iter.hasNext()) {
        QueryCache cache = (QueryCache) iter.next();
        cache.destroy();
      }
      updateTimestampsCache.destroy();
    }

    settings.getRegionFactory().stop();

    if (settings.isAutoDropSchema()) {
      schemaExport.drop(false, true);
    }

    try {
      settings.getConnectionProvider().close();
    } finally {
      SessionFactoryObjectFactory.removeInstance(uuid, name, properties);
    }

    observer.sessionFactoryClosed(this);
    eventListeners.destroyListeners();
  }
  /**
   * 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;
  }
  /**
   * 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 execute() throws HibernateException {
    final Serializable id = getId();
    final EntityPersister persister = getPersister();
    final SessionImplementor session = getSession();
    final Object instance = getInstance();

    final boolean veto = preUpdate();

    final SessionFactoryImplementor factory = session.getFactory();
    Object previousVersion = this.previousVersion;
    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
      previousVersion = persister.getVersion(instance);
    }

    final Object ck;
    if (persister.hasCache()) {
      final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy();
      ck = cache.generateCacheKey(id, persister, factory, session.getTenantIdentifier());
      lock = cache.lockItem(session, ck, previousVersion);
    } else {
      ck = null;
    }

    if (!veto) {
      persister.update(
          id,
          state,
          dirtyFields,
          hasDirtyCollection,
          previousState,
          previousVersion,
          instance,
          rowId,
          session);
    }

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

    if (entry.getStatus() == Status.MANAGED || persister.isVersionPropertyGenerated()) {
      // get the updated snapshot of the entity state by cloning current state;
      // it is safe to copy in place, since by this time no-one else (should have)
      // has a reference  to the array
      TypeHelper.deepCopy(
          state, persister.getPropertyTypes(), persister.getPropertyCheckability(), state, session);
      if (persister.hasUpdateGeneratedProperties()) {
        // this entity defines proeprty generation, so process those generated
        // values...
        persister.processUpdateGeneratedProperties(id, instance, state, session);
        if (persister.isVersionPropertyGenerated()) {
          nextVersion = Versioning.getVersion(state, persister);
        }
      }
      // have the entity entry doAfterTransactionCompletion post-update processing, passing it the
      // update state and the new version (if one).
      entry.postUpdate(instance, state, nextVersion);
    }

    if (persister.hasCache()) {
      if (persister.isCacheInvalidationRequired() || entry.getStatus() != Status.MANAGED) {
        persister.getCacheAccessStrategy().remove(session, ck);
      } else {
        // TODO: inefficient if that cache is just going to ignore the updated state!
        final CacheEntry ce = persister.buildCacheEntry(instance, state, nextVersion, getSession());
        cacheEntry = persister.getCacheEntryStructure().structure(ce);

        final boolean put = cacheUpdate(persister, previousVersion, ck);
        if (put && factory.getStatistics().isStatisticsEnabled()) {
          factory
              .getStatisticsImplementor()
              .secondLevelCachePut(getPersister().getCacheAccessStrategy().getRegion().getName());
        }
      }
    }

    session
        .getPersistenceContext()
        .getNaturalIdHelper()
        .manageSharedNaturalIdCrossReference(
            persister, id, state, previousNaturalIdValues, CachedNaturalIdValueSource.UPDATE);

    postUpdate();

    if (factory.getStatistics().isStatisticsEnabled() && !veto) {
      factory.getStatisticsImplementor().updateEntity(getPersister().getEntityName());
    }
  }
 private boolean isCachePutEnabled(EntityPersister persister, SessionImplementor session) {
   return persister.hasCache()
       && !persister.isCacheInvalidationRequired()
       && session.getCacheMode().isPutEnabled();
 }
  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());
    }
  }
 public boolean containsEntity(String entityName, Serializable identifier) {
   EntityPersister p = getEntityPersister(entityName);
   return p.hasCache()
       && p.getCacheAccessStrategy().getRegion().contains(buildCacheKey(identifier, p));
 }
 public boolean hasAfterTransactionCompletion() {
   return persister.hasCache() || hasPostCommitEventListeners();
 }