@SuppressWarnings("unchecked") public void onSaveOrUpdate(SaveOrUpdateEvent event) throws HibernateException { Object entity = event.getObject(); boolean newEntity = !event.getSession().contains(entity); if (newEntity) { if (beforeInsertCaller != null) { beforeInsertCaller.call(entity); if (event.getSession().contains(entity)) { EntityEntry entry = event.getEntry(); if (entry != null) { Object[] state = entry.getLoadedState(); synchronizePersisterState(entity, entry.getPersister(), state); } } } if (shouldTimestamp) { long time = System.currentTimeMillis(); if (dateCreatedProperty != null && newEntity) { Object now = DefaultGroovyMethods.newInstance(dateCreatedProperty.getType(), new Object[] {time}); dateCreatedProperty.setProperty(entity, now); } if (lastUpdatedProperty != null) { Object now = DefaultGroovyMethods.newInstance(lastUpdatedProperty.getType(), new Object[] {time}); lastUpdatedProperty.setProperty(entity, now); } } } }
@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); }
/** Convience method to retreive an entities next version value */ private Object getNextVersion(FlushEntityEvent event) throws HibernateException { EntityEntry entry = event.getEntityEntry(); EntityPersister persister = entry.getPersister(); if (persister.isVersioned()) { Object[] values = event.getPropertyValues(); if (entry.isBeingReplicated()) { return Versioning.getVersion(values, persister); } else { int[] dirtyProperties = event.getDirtyProperties(); final boolean isVersionIncrementRequired = isVersionIncrementRequired(event, entry, persister, dirtyProperties); final Object nextVersion = isVersionIncrementRequired ? Versioning.increment( entry.getVersion(), persister.getVersionType(), event.getSession()) : entry.getVersion(); // use the current version Versioning.setVersion(values, nextVersion, persister); return nextVersion; } } else { return null; } }
private void checkNaturalId( EntityPersister persister, EntityEntry entry, Object[] current, Object[] loaded, EntityMode entityMode, SessionImplementor session) { if (persister.hasNaturalIdentifier() && entry.getStatus() != Status.READ_ONLY) { Object[] snapshot = null; Type[] types = persister.getPropertyTypes(); int[] props = persister.getNaturalIdentifierProperties(); boolean[] updateable = persister.getPropertyUpdateability(); for (int i = 0; i < props.length; i++) { int prop = props[i]; if (!updateable[prop]) { Object loadedVal; if (loaded == null) { if (snapshot == null) { snapshot = session.getPersistenceContext().getNaturalIdSnapshot(entry.getId(), persister); } loadedVal = snapshot[i]; } else { loadedVal = loaded[prop]; } if (!types[prop].isEqual(current[prop], loadedVal, entityMode)) { throw new HibernateException( "immutable natural identifier of an instance of " + persister.getEntityName() + " was altered"); } } } } }
private Object[] getValues( Object entity, EntityEntry entry, EntityMode entityMode, boolean mightBeDirty, SessionImplementor session) { final Object[] loadedState = entry.getLoadedState(); final Status status = entry.getStatus(); final EntityPersister persister = entry.getPersister(); final Object[] values; if (status == Status.DELETED) { // grab its state saved at deletion values = entry.getDeletedState(); } else if (!mightBeDirty && loadedState != null) { values = loadedState; } else { checkId(entity, persister, entry.getId(), entityMode, session); // grab its current state values = persister.getPropertyValues(entity, entityMode); checkNaturalId(persister, entry, values, loadedState, entityMode, session); } return values; }
/** * Flushes a single entity's state to the database, by scheduling an update action, if necessary */ public void onFlushEntity(FlushEntityEvent event) throws HibernateException { final Object entity = event.getEntity(); final EntityEntry entry = event.getEntityEntry(); final EventSource session = event.getSession(); final EntityPersister persister = entry.getPersister(); final Status status = entry.getStatus(); final EntityMode entityMode = session.getEntityMode(); final Type[] types = persister.getPropertyTypes(); final boolean mightBeDirty = entry.requiresDirtyCheck(entity); final Object[] values = getValues(entity, entry, entityMode, mightBeDirty, session); event.setPropertyValues(values); // TODO: avoid this for non-new instances where mightBeDirty==false boolean substitute = wrapCollections(session, persister, types, values); if (isUpdateNecessary(event, mightBeDirty)) { substitute = scheduleUpdate(event) || substitute; } if (status != Status.DELETED) { // now update the object .. has to be outside the main if block above (because of collections) if (substitute) persister.setPropertyValues(entity, values, entityMode); // Search for collections by reachability, updating their role. // We don't want to touch collections reachable from a deleted object if (persister.hasCollections()) { new FlushVisitor(session, entity).processEntityPropertyValues(values, types); } } }
private void throwObjectDeletedIfNecessary(LoadEvent event, EntityEntry oldEntry) { Status status = oldEntry.getStatus(); if (status == Status.DELETED || status == Status.GONE) { throw new ObjectDeletedException( "The object with that id was deleted", event.getEntityId(), event.getEntityClassName()); } }
protected boolean invokeInterceptor( SessionImplementor session, Object entity, EntityEntry entry, final Object[] values, EntityPersister persister) { return session .getInterceptor() .onFlushDirty( entity, entry.getId(), values, entry.getLoadedState(), persister.getPropertyNames(), persister.getPropertyTypes()); }
private boolean isVersionIncrementRequired( FlushEntityEvent event, EntityEntry entry, EntityPersister persister, int[] dirtyProperties) { final boolean isVersionIncrementRequired = entry.getStatus() != Status.DELETED && (dirtyProperties == null || Versioning.isVersionIncrementRequired( dirtyProperties, event.hasDirtyCollection(), persister.getPropertyVersionability())); return isVersionIncrementRequired; }
/** * The given save-update event named a transient entity. * * <p>Here, we will perform the save processing. * * @param event The save event to be handled. * @return The entity's identifier after saving. */ protected Serializable entityIsTransient(SaveOrUpdateEvent event) { LOG.trace("Saving transient instance"); final EventSource source = event.getSession(); EntityEntry entityEntry = event.getEntry(); if (entityEntry != null) { if (entityEntry.getStatus() == Status.DELETED) { source.forceFlush(entityEntry); } else { throw new AssertionFailure("entity was persistent"); } } Serializable id = saveWithGeneratedOrRequestedId(event); source.getPersistenceContext().reassociateProxy(event.getObject(), id); return id; }
@Override protected boolean invokeInterceptor( SessionImplementor session, Object entity, EntityEntry entry, Object[] values, EntityPersister persister) { boolean isDirty = false; if (entry.getStatus() != Status.DELETED) { if (callbackHandler.preUpdate(entity)) { isDirty = copyState(entity, persister.getPropertyTypes(), values, session.getFactory()); } } return super.invokeInterceptor(session, entity, entry, values, persister) || isDirty; }
protected boolean handleInterception(FlushEntityEvent event) { SessionImplementor session = event.getSession(); EntityEntry entry = event.getEntityEntry(); EntityPersister persister = entry.getPersister(); Object entity = event.getEntity(); // give the Interceptor a chance to modify property values final Object[] values = event.getPropertyValues(); final boolean intercepted = invokeInterceptor(session, entity, entry, values, persister); // now we might need to recalculate the dirtyProperties array if (intercepted && event.isDirtyCheckPossible() && !event.isDirtyCheckHandledByInterceptor()) { int[] dirtyProperties; if (event.hasDatabaseSnapshot()) { dirtyProperties = persister.findModified(event.getDatabaseSnapshot(), values, entity, session); } else { dirtyProperties = persister.findDirty(values, entry.getLoadedState(), entity, session); } event.setDirtyProperties(dirtyProperties); } return intercepted; }
protected Serializable entityIsPersistent(SaveOrUpdateEvent event) throws HibernateException { LOG.trace("Ignoring persistent instance"); EntityEntry entityEntry = event.getEntry(); if (entityEntry == null) { throw new AssertionFailure("entity was transient or detached"); } else { if (entityEntry.getStatus() == Status.DELETED) { throw new AssertionFailure("entity was deleted"); } final SessionFactoryImplementor factory = event.getSession().getFactory(); Serializable requestedId = event.getRequestedId(); Serializable savedId; if (requestedId == null) { savedId = entityEntry.getId(); } else { final boolean isEqual = !entityEntry .getPersister() .getIdentifierType() .isEqual( requestedId, entityEntry.getId(), event.getSession().getEntityMode(), factory); if (isEqual) { throw new PersistentObjectException( "object passed to save() was already persistent: " + MessageHelper.infoString(entityEntry.getPersister(), requestedId, factory)); } savedId = requestedId; } if (LOG.isTraceEnabled()) LOG.trace( "Object already associated with session: " + MessageHelper.infoString(entityEntry.getPersister(), savedId, factory)); return savedId; } }
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 a dirty check, and attach the results to the event */ protected void dirtyCheck(FlushEntityEvent event) throws HibernateException { final Object entity = event.getEntity(); final Object[] values = event.getPropertyValues(); final SessionImplementor session = event.getSession(); final EntityEntry entry = event.getEntityEntry(); final EntityPersister persister = entry.getPersister(); final Serializable id = entry.getId(); final Object[] loadedState = entry.getLoadedState(); int[] dirtyProperties = session .getInterceptor() .findDirty( entity, id, values, loadedState, persister.getPropertyNames(), persister.getPropertyTypes()); event.setDatabaseSnapshot(null); final boolean interceptorHandledDirtyCheck; boolean cannotDirtyCheck; if (dirtyProperties == null) { // Interceptor returned null, so do the dirtycheck ourself, if possible interceptorHandledDirtyCheck = false; cannotDirtyCheck = loadedState == null; // object loaded by update() if (!cannotDirtyCheck) { // dirty check against the usual snapshot of the entity dirtyProperties = persister.findDirty(values, loadedState, entity, session); } else if (entry.getStatus() == Status.DELETED && !event.getEntityEntry().isModifiableEntity()) { // A non-modifiable (e.g., read-only or immutable) entity needs to be have // references to transient entities set to null before being deleted. No other // fields should be updated. if (values != entry.getDeletedState()) { throw new IllegalStateException( "Entity has status Status.DELETED but values != entry.getDeletedState"); } // Even if loadedState == null, we can dirty-check by comparing currentState and // entry.getDeletedState() because the only fields to be updated are those that // refer to transient entities that are being set to null. // - currentState contains the entity's current property values. // - entry.getDeletedState() contains the entity's current property values with // references to transient entities set to null. // - dirtyProperties will only contain properties that refer to transient entities final Object[] currentState = persister.getPropertyValues(event.getEntity(), event.getSession().getEntityMode()); dirtyProperties = persister.findDirty(entry.getDeletedState(), currentState, entity, session); cannotDirtyCheck = false; } else { // dirty check against the database snapshot, if possible/necessary final Object[] databaseSnapshot = getDatabaseSnapshot(session, persister, id); if (databaseSnapshot != null) { dirtyProperties = persister.findModified(databaseSnapshot, values, entity, session); cannotDirtyCheck = false; event.setDatabaseSnapshot(databaseSnapshot); } } } else { // the Interceptor handled the dirty checking cannotDirtyCheck = false; interceptorHandledDirtyCheck = true; } logDirtyProperties(id, dirtyProperties, persister); event.setDirtyProperties(dirtyProperties); event.setDirtyCheckHandledByInterceptor(interceptorHandledDirtyCheck); event.setDirtyCheckPossible(!cannotDirtyCheck); }
private boolean scheduleUpdate(final FlushEntityEvent event) { final EntityEntry entry = event.getEntityEntry(); final EventSource session = event.getSession(); final Object entity = event.getEntity(); final Status status = entry.getStatus(); final EntityMode entityMode = session.getEntityMode(); final EntityPersister persister = entry.getPersister(); final Object[] values = event.getPropertyValues(); if (LOG.isTraceEnabled()) { if (status == Status.DELETED) { if (!persister.isMutable()) LOG.trace( "Updating immutable, deleted entity: " + MessageHelper.infoString(persister, entry.getId(), session.getFactory())); else if (!entry.isModifiableEntity()) LOG.trace( "Updating non-modifiable, deleted entity: " + MessageHelper.infoString(persister, entry.getId(), session.getFactory())); else LOG.trace( "Updating deleted entity: " + MessageHelper.infoString(persister, entry.getId(), session.getFactory())); } else LOG.trace( "Updating entity: " + MessageHelper.infoString(persister, entry.getId(), session.getFactory())); } final boolean intercepted = !entry.isBeingReplicated() && handleInterception(event); // increment the version number (if necessary) final Object nextVersion = getNextVersion(event); // if it was dirtied by a collection only int[] dirtyProperties = event.getDirtyProperties(); if (event.isDirtyCheckPossible() && dirtyProperties == null) { if (!intercepted && !event.hasDirtyCollection()) { throw new AssertionFailure("dirty, but no dirty properties"); } dirtyProperties = ArrayHelper.EMPTY_INT_ARRAY; } // check nullability but do not doAfterTransactionCompletion command execute // we'll use scheduled updates for that. new Nullability(session).checkNullability(values, persister, true); // schedule the update // note that we intentionally do _not_ pass in currentPersistentState! session .getActionQueue() .addAction( new EntityUpdateAction( entry.getId(), values, dirtyProperties, event.hasDirtyCollection(), (status == Status.DELETED && !entry.isModifiableEntity() ? persister.getPropertyValues(entity, entityMode) : entry.getLoadedState()), entry.getVersion(), nextVersion, entity, entry.getRowId(), persister, session)); return intercepted; }