/** * 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; }