KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hibernate > event > def > AbstractSaveEventListener


1 //$Id: AbstractSaveEventListener.java,v 1.22 2005/07/20 07:25:14 oneovthafew Exp $
2
package org.hibernate.event.def;
3
4 import java.io.Serializable JavaDoc;
5 import java.util.Map JavaDoc;
6
7 import net.sf.cglib.transform.impl.InterceptFieldEnabled;
8
9 import org.apache.commons.logging.Log;
10 import org.apache.commons.logging.LogFactory;
11 import org.hibernate.HibernateException;
12 import org.hibernate.LockMode;
13 import org.hibernate.NonUniqueObjectException;
14 import org.hibernate.action.EntityIdentityInsertAction;
15 import org.hibernate.action.EntityInsertAction;
16 import org.hibernate.classic.Lifecycle;
17 import org.hibernate.classic.Validatable;
18 import org.hibernate.engine.Cascade;
19 import org.hibernate.engine.CascadingAction;
20 import org.hibernate.engine.EntityEntry;
21 import org.hibernate.engine.EntityKey;
22 import org.hibernate.engine.ForeignKeys;
23 import org.hibernate.engine.Nullability;
24 import org.hibernate.engine.SessionImplementor;
25 import org.hibernate.engine.Status;
26 import org.hibernate.engine.Versioning;
27 import org.hibernate.event.EventSource;
28 import org.hibernate.id.IdentifierGenerationException;
29 import org.hibernate.id.IdentifierGeneratorFactory;
30 import org.hibernate.intercept.FieldInterceptor;
31 import org.hibernate.persister.entity.EntityPersister;
32 import org.hibernate.pretty.MessageHelper;
33 import org.hibernate.type.Type;
34 import org.hibernate.type.TypeFactory;
35
36 /**
37  * A convenience bas class for listeners responding to save events.
38  *
39  * @author Steve Ebersole.
40  */

41 public abstract class AbstractSaveEventListener extends AbstractReassociateEventListener {
42
43     protected static final int PERSISTENT = 0;
44     protected static final int TRANSIENT = 1;
45     protected static final int DETACHED = 2;
46     protected static final int DELETED = 3;
47
48     private static final Log log = LogFactory.getLog(AbstractSaveEventListener.class);
49
50     /**
51      * Prepares the save call using the given requested id.
52      * @param entity The entity to be saved.
53      * @param requestedId The id to which to associate the entity.
54      * @param source The session which is the source of this save event.
55      * @return The id used to save the entity.
56      * @throws HibernateException
57      */

58     protected Serializable JavaDoc saveWithRequestedId(
59             Object JavaDoc entity,
60             Serializable JavaDoc requestedId,
61             String JavaDoc entityName,
62             Object JavaDoc anything,
63             EventSource source)
64     throws HibernateException {
65         return performSave(
66                 entity,
67                 requestedId,
68                 source.getEntityPersister(entityName, entity),
69                 false,
70                 anything,
71                 source
72         );
73     }
74
75     /**
76      * Prepares the save call using a newly generated id.
77      * @param entity The entity to be saved
78      * @param source The session which is the source of this save event.
79      * @return The id used to save the entity
80      * @throws HibernateException
81      */

82     protected Serializable JavaDoc saveWithGeneratedId(
83             Object JavaDoc entity,
84             String JavaDoc entityName,
85             Object JavaDoc anything,
86             EventSource source)
87     throws HibernateException {
88         
89         EntityPersister persister = source.getEntityPersister(entityName, entity);
90
91         Serializable JavaDoc generatedId = persister.getIdentifierGenerator()
92             .generate( source, entity );
93         
94         if ( generatedId == null ) {
95             throw new IdentifierGenerationException( "null id generated for:" + entity.getClass() );
96         }
97         else if ( generatedId == IdentifierGeneratorFactory.SHORT_CIRCUIT_INDICATOR ) {
98             return source.getIdentifier( entity );
99         }
100         else if ( generatedId == IdentifierGeneratorFactory.POST_INSERT_INDICATOR ) {
101             return performSave(entity, null, persister, true, anything, source);
102         }
103         else {
104             
105             if ( log.isDebugEnabled() ) {
106                 log.debug(
107                         "generated identifier: " +
108                         persister.getIdentifierType().toLoggableString(generatedId, source.getFactory()) +
109                         ", using strategy: " +
110                         persister.getIdentifierGenerator().getClass().getName() //TODO: define toString()s for generators
111
);
112             }
113             
114             return performSave(entity, generatedId, persister, false, anything, source);
115         }
116     }
117
118     /**
119      * Ppepares the save call by checking the session caches for a pre-existing
120      * entity and performing any lifecycle callbacks.
121      * @param entity The entity to be saved.
122      * @param id The id by which to save the entity.
123      * @param persister The entity's persister instance.
124      * @param useIdentityColumn Is an identity column in use?
125      * @param source The session from which the event originated.
126      * @return The id used to save the entity.
127      * @throws HibernateException
128      */

129     protected Serializable JavaDoc performSave(
130             Object JavaDoc entity,
131             Serializable JavaDoc id,
132             EntityPersister persister,
133             boolean useIdentityColumn,
134             Object JavaDoc anything,
135             EventSource source)
136     throws HibernateException {
137
138         if ( log.isTraceEnabled() ) {
139             log.trace( "saving " + MessageHelper.infoString(persister, id, source.getFactory()) );
140         }
141         
142         EntityKey key;
143         if ( !useIdentityColumn ) {
144             key = new EntityKey( id, persister, source.getEntityMode() );
145             Object JavaDoc old = source.getPersistenceContext().getEntity(key);
146             if (old != null) {
147                 if ( source.getPersistenceContext().getEntry(old).getStatus() == Status.DELETED ) {
148                     source.forceFlush( source.getPersistenceContext().getEntry(old) );
149                 }
150                 else {
151                     throw new NonUniqueObjectException( id, persister.getEntityName() );
152                 }
153             }
154             persister.setIdentifier(entity, id, source.getEntityMode());
155         }
156         else {
157             key = null;
158         }
159         
160         if ( invokeSaveLifecycle(entity, persister, source) ) {
161             return id; //EARLY EXIT
162
}
163
164         return performSaveOrReplicate(
165                 entity,
166                 key,
167                 persister,
168                 useIdentityColumn,
169                 anything,
170                 source
171             );
172     }
173     
174     protected boolean invokeSaveLifecycle(Object JavaDoc entity, EntityPersister persister, EventSource source) {
175         // Sub-insertions should occur before containing insertion so
176
// Try to do the callback now
177
if ( persister.implementsLifecycle( source.getEntityMode() ) ) {
178             log.debug( "calling onSave()" );
179             if ( ( (Lifecycle) entity ).onSave(source) ) {
180                 log.debug( "insertion vetoed by onSave()" );
181                 return true;
182             }
183         }
184         return false;
185     }
186
187     protected void validate(Object JavaDoc entity, EntityPersister persister, EventSource source) {
188         if ( persister.implementsValidatable( source.getEntityMode() ) ) {
189             ( ( Validatable ) entity ).validate();
190         }
191     }
192
193     /**
194      * Performs all the actual work needed to save an entity (well to get the save moved to
195      * the execution queue).
196      * @param entity The entity to be saved
197      * @param key The id to be used for saving the entity (or null, in the case of identity columns)
198      * @param persister The entity's persister instance.
199      * @param useIdentityColumn Should an identity column be used for id generation?
200      * @param source The session which is the source of the current event.
201      * @return The id used to save the entity.
202      * @throws HibernateException
203      */

204     protected Serializable JavaDoc performSaveOrReplicate(
205             Object JavaDoc entity,
206             EntityKey key,
207             EntityPersister persister,
208             boolean useIdentityColumn,
209             Object JavaDoc anything,
210             EventSource source)
211     throws HibernateException {
212         
213         validate( entity, persister, source );
214
215         Serializable JavaDoc id = key==null ? null : key.getIdentifier();
216
217         if (useIdentityColumn) {
218             log.trace("executing insertions");
219             source.getActionQueue().executeInserts();
220         }
221
222         // Put a placeholder in entries, so we don't recurse back and try to save() the
223
// same object again. QUESTION: should this be done before onSave() is called?
224
// likewise, should it be done before onUpdate()?
225
source.getPersistenceContext().addEntry(
226                 entity,
227                 Status.SAVING,
228                 null,
229                 null,
230                 id,
231                 null,
232                 LockMode.WRITE,
233                 useIdentityColumn,
234                 persister,
235                 false,
236                 false
237             );
238
239         cascadeBeforeSave(source, persister, entity, anything);
240
241         Object JavaDoc[] values = persister.getPropertyValuesToInsert(entity, getMergeMap(anything), source);
242         Type[] types = persister.getPropertyTypes();
243
244         boolean substitute = substituteValuesIfNecessary(entity, id, values, persister, source);
245
246         if ( persister.hasCollections() ) {
247             substitute = substitute || visitCollectionsBeforeSave(id, values, types, source);
248         }
249
250         if (substitute) persister.setPropertyValues( entity, values, source.getEntityMode() );
251
252         TypeFactory.deepCopy(
253                 values,
254                 types,
255                 persister.getPropertyUpdateability(),
256                 values,
257                 source
258             );
259         
260         new ForeignKeys.Nullifier(entity, false, useIdentityColumn, source)
261                 .nullifyTransientReferences(values, types);
262         new Nullability(source).checkNullability( values, persister, false );
263         
264         if (useIdentityColumn) {
265             EntityIdentityInsertAction insert = new EntityIdentityInsertAction(values, entity, persister, source);
266             source.getActionQueue().execute(insert);
267             id = insert.getGeneratedId();
268             persister.setIdentifier( entity, id, source.getEntityMode() );
269             key = new EntityKey( id, persister, source.getEntityMode() );
270             source.getPersistenceContext().checkUniqueness(key, entity);
271             //source.getBatcher().executeBatch(); //found another way to ensure that all batched joined inserts have been executed
272
}
273
274         Object JavaDoc version = Versioning.getVersion(values, persister);
275         source.getPersistenceContext().addEntity(
276                 entity,
277                 Status.MANAGED,
278                 values,
279                 key,
280                 version,
281                 LockMode.WRITE,
282                 useIdentityColumn,
283                 persister,
284                 isVersionIncrementDisabled(),
285                 false
286             );
287         //source.getPersistenceContext().removeNonExist( new EntityKey( id, persister, source.getEntityMode() ) );
288

289         if ( !useIdentityColumn ) {
290             source.getActionQueue().addAction(
291                     new EntityInsertAction(id, values, entity, version, persister, source)
292                 );
293         }
294
295         cascadeAfterSave(source, persister, entity, anything);
296
297         if ( entity instanceof InterceptFieldEnabled ) {
298             FieldInterceptor fieldInterceptor = FieldInterceptor.initFieldInterceptor(
299                     entity,
300                     persister.getEntityName(),
301                     source,
302                     null
303                 );
304             fieldInterceptor.dirty();
305         }
306
307         return id;
308     }
309     
310     protected Map JavaDoc getMergeMap(Object JavaDoc anything) {
311         return null;
312     }
313     
314     /**
315      * After the save, will te version number be incremented
316      * if the instance is modified?
317      */

318     protected boolean isVersionIncrementDisabled() {
319         return false;
320     }
321     
322     protected boolean visitCollectionsBeforeSave(Serializable JavaDoc id, Object JavaDoc[] values, Type[] types, EventSource source) {
323         WrapVisitor visitor = new WrapVisitor(source);
324         // substitutes into values by side-effect
325
visitor.processEntityPropertyValues(values, types);
326         return visitor.isSubstitutionRequired();
327     }
328     
329     /**
330      * Perform any property value substitution that is necessary
331      * (interceptor callback, version initialization...)
332      */

333     protected boolean substituteValuesIfNecessary(
334             Object JavaDoc entity,
335             Serializable JavaDoc id,
336             Object JavaDoc[] values,
337             EntityPersister persister,
338             SessionImplementor source
339     ) {
340         boolean substitute = source.getInterceptor().onSave(
341                 entity,
342                 id,
343                 values,
344                 persister.getPropertyNames(),
345                 persister.getPropertyTypes()
346             );
347
348         //keep the existing version number in the case of replicate!
349
if ( persister.isVersioned() ) {
350             substitute = Versioning.seedVersion(
351                     values,
352                     persister.getVersionProperty(),
353                     persister.getVersionType()
354             ) || substitute;
355         }
356         return substitute;
357     }
358
359     /**
360      * Handles the calls needed to perform pre-save cascades for the given entity.
361      * @param source The session from whcih the save event originated.
362      * @param persister The entity's persister instance.
363      * @param entity The entity to be saved.
364      * @throws HibernateException
365      */

366     protected void cascadeBeforeSave(
367             EventSource source,
368             EntityPersister persister,
369             Object JavaDoc entity,
370             Object JavaDoc anything)
371     throws HibernateException {
372
373         // cascade-save to many-to-one BEFORE the parent is saved
374
source.getPersistenceContext().incrementCascadeLevel();
375         try {
376             new Cascade( getCascadeAction(), Cascade.BEFORE_INSERT_AFTER_DELETE, source )
377                 .cascade(persister, entity, anything);
378         }
379         finally {
380             source.getPersistenceContext().decrementCascadeLevel();
381         }
382     }
383
384     /**
385      * Handles to calls needed to perform post-save cascades.
386      * @param source The session from which the event originated.
387      * @param persister The entity's persister instance.
388      * @param entity The entity beng saved.
389      * @throws HibernateException
390      */

391     protected void cascadeAfterSave(
392             EventSource source,
393             EntityPersister persister,
394             Object JavaDoc entity,
395             Object JavaDoc anything)
396     throws HibernateException {
397
398         // cascade-save to collections AFTER the collection owner was saved
399
source.getPersistenceContext().incrementCascadeLevel();
400         try {
401             new Cascade( getCascadeAction(), Cascade.AFTER_INSERT_BEFORE_DELETE, source )
402                 .cascade(persister, entity, anything);
403         }
404         finally {
405             source.getPersistenceContext().decrementCascadeLevel();
406         }
407     }
408     
409     protected abstract CascadingAction getCascadeAction();
410     
411     /**
412      * Determine whether the entity is persistent, detached, or transient
413      */

414     protected int getEntityState(
415             Object JavaDoc entity,
416             String JavaDoc entityName,
417             EntityEntry entry, //pass this as an argument only to avoid double looking
418
SessionImplementor source
419     ) {
420                 
421         if ( entry!=null ) { // the object is persistent
422

423             //the entity is associated with the session, so check its status
424
if ( entry.getStatus() != Status.DELETED ) {
425                 // do nothing for persistent instances
426
if ( log.isTraceEnabled() ) log.trace( "persistent instance of: " + getLoggableName(entityName, entity) );
427                 return PERSISTENT;
428             }
429             else {
430                 //ie. e.status==DELETED
431
if ( log.isTraceEnabled() ) log.trace( "deleted instance of: " + getLoggableName(entityName, entity) );
432                 return DELETED;
433             }
434             
435         }
436         else { // the object is transient or detached
437

438             //the entity is not associated with the session, so
439
//try interceptor and unsaved-value
440

441             if ( ForeignKeys.isTransient( entityName, entity, getAssumedUnsaved(), source ) ) {
442                 if ( log.isTraceEnabled() ) log.trace( "transient instance of: " + getLoggableName(entityName, entity) );
443                 return TRANSIENT;
444             }
445             else {
446                 if ( log.isTraceEnabled() ) log.trace( "detached instance of: " + getLoggableName(entityName, entity) );
447                 return DETACHED;
448             }
449
450         }
451     }
452     
453     private String JavaDoc getLoggableName(String JavaDoc entityName, Object JavaDoc entity) {
454         return entityName==null ? entity.getClass().getName() : entityName;
455     }
456     
457     protected Boolean JavaDoc getAssumedUnsaved() {
458         return null;
459     }
460
461 }
462
Popular Tags