private Object[] createDeletedState( EntityPersister persister, Object[] currentState, EventSource session) { Type[] propTypes = persister.getPropertyTypes(); final Object[] deletedState = new Object[propTypes.length]; // TypeFactory.deepCopy( currentState, propTypes, persister.getPropertyUpdateability(), // deletedState, session ); boolean[] copyability = new boolean[propTypes.length]; java.util.Arrays.fill(copyability, true); TypeHelper.deepCopy(currentState, propTypes, copyability, deletedState, session); return deletedState; }
@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()); } }
/** * Performs all the actual work needed to save an entity (well to get the save moved to the * execution queue). * * @param entity The entity to be saved * @param key The id to be used for saving the entity (or null, in the case of identity columns) * @param persister The entity's persister instance. * @param useIdentityColumn Should an identity column be used for id generation? * @param anything Generally cascade-specific information. * @param source The session which is the source of the current event. * @param requiresImmediateIdAccess Is access to the identifier required immediately after the * completion of the save? persist(), for example, does not require this... * @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 performSaveOrReplicate( Object entity, EntityKey key, EntityPersister persister, boolean useIdentityColumn, Object anything, EventSource source, boolean requiresImmediateIdAccess) { Serializable id = key == null ? null : key.getIdentifier(); boolean inTxn = source.isTransactionInProgress(); boolean shouldDelayIdentityInserts = !inTxn && !requiresImmediateIdAccess; // Put a placeholder in entries, so we don't recurse back and try to save() the // same object again. QUESTION: should this be done before onSave() is called? // likewise, should it be done before onUpdate()? EntityEntry original = source .getPersistenceContext() .addEntry( entity, Status.SAVING, null, null, id, null, LockMode.WRITE, useIdentityColumn, persister, false, false); cascadeBeforeSave(source, persister, entity, anything); Object[] values = persister.getPropertyValuesToInsert(entity, getMergeMap(anything), source); Type[] types = persister.getPropertyTypes(); boolean substitute = substituteValuesIfNecessary(entity, id, values, persister, source); if (persister.hasCollections()) { substitute = substitute || visitCollectionsBeforeSave(entity, id, values, types, source); } if (substitute) { persister.setPropertyValues(entity, values); } TypeHelper.deepCopy(values, types, persister.getPropertyUpdateability(), values, source); AbstractEntityInsertAction insert = addInsertAction( values, id, entity, persister, useIdentityColumn, source, shouldDelayIdentityInserts); // postpone initializing id in case the insert has non-nullable transient dependencies // that are not resolved until cascadeAfterSave() is executed cascadeAfterSave(source, persister, entity, anything); if (useIdentityColumn && insert.isEarlyInsert()) { if (!EntityIdentityInsertAction.class.isInstance(insert)) { throw new IllegalStateException( "Insert should be using an identity column, but action is of unexpected type: " + insert.getClass().getName()); } id = ((EntityIdentityInsertAction) insert).getGeneratedId(); insert.handleNaturalIdPostSaveNotifications(id); } EntityEntry newEntry = source.getPersistenceContext().getEntry(entity); if (newEntry != original) { EntityEntryExtraState extraState = newEntry.getExtraState(EntityEntryExtraState.class); if (extraState == null) { newEntry.addExtraState(original.getExtraState(EntityEntryExtraState.class)); } } return id; }