001: // $Id: DefaultSaveOrUpdateEventListener.java 10949 2006-12-07 21:53:41Z steve.ebersole@jboss.com $
002: package org.hibernate.event.def;
003:
004: import java.io.Serializable;
005:
006: import org.apache.commons.logging.Log;
007: import org.apache.commons.logging.LogFactory;
008:
009: import org.hibernate.AssertionFailure;
010: import org.hibernate.EntityMode;
011: import org.hibernate.HibernateException;
012: import org.hibernate.LockMode;
013: import org.hibernate.PersistentObjectException;
014: import org.hibernate.TransientObjectException;
015: import org.hibernate.classic.Lifecycle;
016: import org.hibernate.engine.Cascade;
017: import org.hibernate.engine.CascadingAction;
018: import org.hibernate.engine.EntityEntry;
019: import org.hibernate.engine.EntityKey;
020: import org.hibernate.engine.SessionFactoryImplementor;
021: import org.hibernate.engine.SessionImplementor;
022: import org.hibernate.engine.Status;
023: import org.hibernate.event.EventSource;
024: import org.hibernate.event.SaveOrUpdateEvent;
025: import org.hibernate.event.SaveOrUpdateEventListener;
026: import org.hibernate.persister.entity.EntityPersister;
027: import org.hibernate.pretty.MessageHelper;
028: import org.hibernate.proxy.HibernateProxy;
029:
030: /**
031: * Defines the default listener used by Hibernate for handling save-update
032: * events.
033: *
034: * @author Steve Ebersole
035: * @author Gavin King
036: */
037: public class DefaultSaveOrUpdateEventListener extends
038: AbstractSaveEventListener implements SaveOrUpdateEventListener {
039:
040: private static final Log log = LogFactory
041: .getLog(DefaultSaveOrUpdateEventListener.class);
042:
043: /**
044: * Handle the given update event.
045: *
046: * @param event The update event to be handled.
047: */
048: public void onSaveOrUpdate(SaveOrUpdateEvent event) {
049: final SessionImplementor source = event.getSession();
050: final Object object = event.getObject();
051: final Serializable requestedId = event.getRequestedId();
052:
053: if (requestedId != null) {
054: //assign the requested id to the proxy, *before*
055: //reassociating the proxy
056: if (object instanceof HibernateProxy) {
057: ((HibernateProxy) object).getHibernateLazyInitializer()
058: .setIdentifier(requestedId);
059: }
060: }
061:
062: if (reassociateIfUninitializedProxy(object, source)) {
063: log.trace("reassociated uninitialized proxy");
064: // an uninitialized proxy, noop, don't even need to
065: // return an id, since it is never a save()
066: } else {
067: //initialize properties of the event:
068: final Object entity = source.getPersistenceContext()
069: .unproxyAndReassociate(object);
070: event.setEntity(entity);
071: event.setEntry(source.getPersistenceContext().getEntry(
072: entity));
073: //return the id in the event object
074: event.setResultId(performSaveOrUpdate(event));
075: }
076:
077: }
078:
079: protected boolean reassociateIfUninitializedProxy(Object object,
080: SessionImplementor source) {
081: return source.getPersistenceContext()
082: .reassociateIfUninitializedProxy(object);
083: }
084:
085: protected Serializable performSaveOrUpdate(SaveOrUpdateEvent event) {
086: int entityState = getEntityState(event.getEntity(), event
087: .getEntityName(), event.getEntry(), event.getSession());
088:
089: switch (entityState) {
090: case DETACHED:
091: entityIsDetached(event);
092: return null;
093: case PERSISTENT:
094: return entityIsPersistent(event);
095: default: //TRANSIENT or DELETED
096: return entityIsTransient(event);
097: }
098: }
099:
100: protected Serializable entityIsPersistent(SaveOrUpdateEvent event)
101: throws HibernateException {
102: log.trace("ignoring persistent instance");
103:
104: EntityEntry entityEntry = event.getEntry();
105: if (entityEntry == null) {
106: throw new AssertionFailure(
107: "entity was transient or detached");
108: } else {
109:
110: if (entityEntry.getStatus() == Status.DELETED) {
111: throw new AssertionFailure("entity was deleted");
112: }
113:
114: final SessionFactoryImplementor factory = event
115: .getSession().getFactory();
116:
117: Serializable requestedId = event.getRequestedId();
118:
119: Serializable savedId;
120: if (requestedId == null) {
121: savedId = entityEntry.getId();
122: } else {
123:
124: final boolean isEqual = !entityEntry.getPersister()
125: .getIdentifierType().isEqual(requestedId,
126: entityEntry.getId(),
127: event.getSession().getEntityMode(),
128: factory);
129:
130: if (isEqual) {
131: throw new PersistentObjectException(
132: "object passed to save() was already persistent: "
133: + MessageHelper.infoString(
134: entityEntry.getPersister(),
135: requestedId, factory));
136: }
137:
138: savedId = requestedId;
139:
140: }
141:
142: if (log.isTraceEnabled()) {
143: log.trace("object already associated with session: "
144: + MessageHelper.infoString(entityEntry
145: .getPersister(), savedId, factory));
146: }
147:
148: return savedId;
149:
150: }
151: }
152:
153: /**
154: * The given save-update event named a transient entity.
155: * <p/>
156: * Here, we will perform the save processing.
157: *
158: * @param event The save event to be handled.
159: *
160: * @return The entity's identifier after saving.
161: */
162: protected Serializable entityIsTransient(SaveOrUpdateEvent event) {
163:
164: log.trace("saving transient instance");
165:
166: final EventSource source = event.getSession();
167:
168: EntityEntry entityEntry = event.getEntry();
169: if (entityEntry != null) {
170: if (entityEntry.getStatus() == Status.DELETED) {
171: source.forceFlush(entityEntry);
172: } else {
173: throw new AssertionFailure("entity was persistent");
174: }
175: }
176:
177: Serializable id = saveWithGeneratedOrRequestedId(event);
178:
179: source.getPersistenceContext().reassociateProxy(
180: event.getObject(), id);
181:
182: return id;
183: }
184:
185: /**
186: * Save the transient instance, assigning the right identifier
187: *
188: * @param event The initiating event.
189: *
190: * @return The entity's identifier value after saving.
191: */
192: protected Serializable saveWithGeneratedOrRequestedId(
193: SaveOrUpdateEvent event) {
194: return saveWithGeneratedId(event.getEntity(), event
195: .getEntityName(), null, event.getSession(), true);
196: }
197:
198: /**
199: * The given save-update event named a detached entity.
200: * <p/>
201: * Here, we will perform the update processing.
202: *
203: * @param event The update event to be handled.
204: */
205: protected void entityIsDetached(SaveOrUpdateEvent event) {
206:
207: log.trace("updating detached instance");
208:
209: if (event.getSession().getPersistenceContext().isEntryFor(
210: event.getEntity())) {
211: //TODO: assertion only, could be optimized away
212: throw new AssertionFailure("entity was persistent");
213: }
214:
215: Object entity = event.getEntity();
216:
217: EntityPersister persister = event.getSession()
218: .getEntityPersister(event.getEntityName(), entity);
219:
220: event.setRequestedId(getUpdateId(entity, persister, event
221: .getRequestedId(), event.getSession().getEntityMode()));
222:
223: performUpdate(event, entity, persister);
224:
225: }
226:
227: /**
228: * Determine the id to use for updating.
229: *
230: * @param entity The entity.
231: * @param persister The entity persister
232: * @param requestedId The requested identifier
233: * @param entityMode The entity mode.
234: *
235: * @return The id.
236: *
237: * @throws TransientObjectException If the entity is considered transient.
238: */
239: protected Serializable getUpdateId(Object entity,
240: EntityPersister persister, Serializable requestedId,
241: EntityMode entityMode) {
242: // use the id assigned to the instance
243: Serializable id = persister.getIdentifier(entity, entityMode);
244: if (id == null) {
245: // assume this is a newly instantiated transient object
246: // which should be saved rather than updated
247: throw new TransientObjectException(
248: "The given object has a null identifier: "
249: + persister.getEntityName());
250: } else {
251: return id;
252: }
253:
254: }
255:
256: protected void performUpdate(SaveOrUpdateEvent event,
257: Object entity, EntityPersister persister)
258: throws HibernateException {
259:
260: if (!persister.isMutable()) {
261: log
262: .trace("immutable instance passed to doUpdate(), locking");
263: reassociate(event, entity, event.getRequestedId(),
264: persister);
265: } else {
266:
267: if (log.isTraceEnabled()) {
268: log.trace("updating "
269: + MessageHelper.infoString(persister, event
270: .getRequestedId(), event.getSession()
271: .getFactory()));
272: }
273:
274: final EventSource source = event.getSession();
275:
276: EntityKey key = new EntityKey(event.getRequestedId(),
277: persister, source.getEntityMode());
278:
279: source.getPersistenceContext().checkUniqueness(key, entity);
280:
281: if (invokeUpdateLifecycle(entity, persister, source)) {
282: reassociate(event, event.getObject(), event
283: .getRequestedId(), persister);
284: return;
285: }
286:
287: // this is a transient object with existing persistent state not loaded by the session
288:
289: new OnUpdateVisitor(source, event.getRequestedId(), entity)
290: .process(entity, persister);
291:
292: //TODO: put this stuff back in to read snapshot from
293: // the second-level cache (needs some extra work)
294: /*Object[] cachedState = null;
295:
296: if ( persister.hasCache() ) {
297: CacheEntry entry = (CacheEntry) persister.getCache()
298: .get( event.getRequestedId(), source.getTimestamp() );
299: cachedState = entry==null ?
300: null :
301: entry.getState(); //TODO: half-assemble this stuff
302: }*/
303:
304: source.getPersistenceContext()
305: .addEntity(
306: entity,
307: Status.MANAGED,
308: null, //cachedState,
309: key,
310: persister.getVersion(entity, source
311: .getEntityMode()), LockMode.NONE,
312: true, persister, false, true //assume true, since we don't really know, and it doesn't matter
313: );
314:
315: persister.afterReassociate(entity, source);
316:
317: if (log.isTraceEnabled()) {
318: log
319: .trace("updating "
320: + MessageHelper.infoString(persister,
321: event.getRequestedId(), source
322: .getFactory()));
323: }
324:
325: cascadeOnUpdate(event, persister, entity);
326:
327: }
328: }
329:
330: protected boolean invokeUpdateLifecycle(Object entity,
331: EntityPersister persister, EventSource source) {
332: if (persister.implements Lifecycle(source.getEntityMode())) {
333: log.debug("calling onUpdate()");
334: if (((Lifecycle) entity).onUpdate(source)) {
335: log.debug("update vetoed by onUpdate()");
336: return true;
337: }
338: }
339: return false;
340: }
341:
342: /**
343: * Handles the calls needed to perform cascades as part of an update request
344: * for the given entity.
345: *
346: * @param event The event currently being processed.
347: * @param persister The defined persister for the entity being updated.
348: * @param entity The entity being updated.
349: */
350: private void cascadeOnUpdate(SaveOrUpdateEvent event,
351: EntityPersister persister, Object entity) {
352: EventSource source = event.getSession();
353: source.getPersistenceContext().incrementCascadeLevel();
354: try {
355: new Cascade(CascadingAction.SAVE_UPDATE,
356: Cascade.AFTER_UPDATE, source).cascade(persister,
357: entity);
358: } finally {
359: source.getPersistenceContext().decrementCascadeLevel();
360: }
361: }
362:
363: protected CascadingAction getCascadeAction() {
364: return CascadingAction.SAVE_UPDATE;
365: }
366: }
|