KickJava   Java API By Example, From Geeks To Geeks.

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


1 //$Id: DefaultMergeEventListener.java,v 1.13 2005/07/19 18:17:12 oneovthafew Exp $
2
package org.hibernate.event.def;
3
4 import java.io.Serializable JavaDoc;
5 import java.util.Map JavaDoc;
6
7 import org.apache.commons.logging.Log;
8 import org.apache.commons.logging.LogFactory;
9 import org.hibernate.AssertionFailure;
10 import org.hibernate.HibernateException;
11 import org.hibernate.ObjectDeletedException;
12 import org.hibernate.StaleObjectStateException;
13 import org.hibernate.WrongClassException;
14 import org.hibernate.engine.Cascade;
15 import org.hibernate.engine.CascadingAction;
16 import org.hibernate.event.EventSource;
17 import org.hibernate.event.MergeEvent;
18 import org.hibernate.event.MergeEventListener;
19 import org.hibernate.engine.SessionImplementor;
20 import org.hibernate.persister.entity.EntityPersister;
21 import org.hibernate.proxy.HibernateProxy;
22 import org.hibernate.proxy.LazyInitializer;
23 import org.hibernate.type.ForeignKeyDirection;
24 import org.hibernate.type.TypeFactory;
25 import org.hibernate.util.IdentityMap;
26
27 /**
28  * Defines the default copy event listener used by hibernate for copying entities
29  * in response to generated copy events.
30  *
31  * @author Gavin King
32  */

33 public class DefaultMergeEventListener extends AbstractSaveEventListener
34     implements MergeEventListener {
35
36     private static final Log log = LogFactory.getLog(DefaultMergeEventListener.class);
37     
38     private final boolean isSaveOrUpdateCopyListener;
39     
40     public DefaultMergeEventListener() {
41         this(false);
42     }
43     
44     protected Map JavaDoc getMergeMap(Object JavaDoc anything) {
45         return IdentityMap.invert( (Map JavaDoc) anything );
46     }
47
48     public DefaultMergeEventListener(boolean isSaveOrUpdateCopyListener) {
49         this.isSaveOrUpdateCopyListener = isSaveOrUpdateCopyListener;
50     }
51
52     /**
53      * Handle the given merge event.
54      *
55      * @param event The merge event to be handled.
56      * @return The result (i.e., the copied entity).
57      * @throws HibernateException
58      */

59     public Object JavaDoc onMerge(MergeEvent event) throws HibernateException {
60         return onMerge( event, IdentityMap.instantiate(10) );
61     }
62
63     /**
64      * Handle the given merge event.
65      *
66      * @param event The merge event to be handled.
67      * @return The result (i.e., the copied entity).
68      * @throws HibernateException
69      */

70     public Object JavaDoc onMerge(MergeEvent event, Map JavaDoc copyCache) throws HibernateException {
71
72         final EventSource source = event.getSession();
73         final Object JavaDoc original = event.getOriginal();
74
75         final Object JavaDoc entity;
76         if ( original == null ) {
77             //EARLY EXIT!
78
return null;
79         }
80         else if ( original instanceof HibernateProxy ) {
81             LazyInitializer li = ( (HibernateProxy) original ).getHibernateLazyInitializer();
82             if ( li.isUninitialized() ) {
83                 //EARLY EXIT!
84
log.trace("ignoring uninitialized proxy");
85                 return source.load( li.getEntityName(), li.getIdentifier() );
86             }
87             else {
88                 entity = li.getImplementation();
89             }
90         }
91         else {
92             entity = original;
93         }
94         
95         if ( copyCache.containsKey(entity) ) {
96             log.trace("already merged");
97             return entity; //EARLY EXIT!
98
}
99
100         event.setEntity(entity);
101
102         int entityState = getEntityState(
103                 entity,
104                 event.getEntityName(),
105                 source.getPersistenceContext().getEntry(entity),
106                 source
107             );
108         
109         switch (entityState) {
110             case DETACHED:
111                 return entityIsDetached(event, copyCache);
112             case TRANSIENT:
113                 return entityIsTransient(event, copyCache);
114             case PERSISTENT:
115                 return entityIsPersistent(event, copyCache);
116             default: //DELETED
117
throw new ObjectDeletedException( "deleted instance passed to merge", null, event.getEntityName() );
118         }
119         
120     }
121     
122     protected Object JavaDoc entityIsPersistent(MergeEvent event, Map JavaDoc copyCache) {
123         log.trace("ignoring persistent instance");
124         
125         //TODO: check that entry.getIdentifier().equals(requestedId)
126

127         final Object JavaDoc entity = event.getEntity();
128         final EventSource source = event.getSession();
129         final EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity );
130         
131         copyCache.put(entity, entity); //before cascade!
132

133         cascadeOnMerge(source, persister, entity, copyCache);
134         copyValues(persister, entity, entity, source, copyCache);
135         
136         return entity;
137     }
138     
139     protected Object JavaDoc entityIsTransient(MergeEvent event, Map JavaDoc copyCache) {
140         
141         log.trace("merging transient instance");
142         
143         final Object JavaDoc entity = event.getEntity();
144         final EventSource source = event.getSession();
145
146         final EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity );
147         final String JavaDoc entityName = persister.getEntityName();
148         
149         final Serializable JavaDoc id = persister.hasIdentifierProperty() ?
150                 persister.getIdentifier( entity, source.getEntityMode() ) :
151                 null;
152         
153         final Object JavaDoc copy = persister.instantiate( id, source.getEntityMode() ); //TODO: should this be Session.instantiate(Persister, ...)?
154
copyCache.put(entity, copy); //before cascade!
155

156         // cascade first, so that all unsaved objects get their
157
// copy created before we actually copy
158
//cascadeOnMerge(event, persister, entity, copyCache, Cascades.CASCADE_BEFORE_MERGE);
159
super.cascadeBeforeSave(source, persister, entity, copyCache);
160         copyValues(persister, entity, copy, source, copyCache, ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT);
161         
162         //this bit is only *really* absolutely necessary for handling
163
//requestedId, but is also good if we merge multiple object
164
//graphs, since it helps ensure uniqueness
165
final Serializable JavaDoc requestedId = event.getRequestedId();
166         if (requestedId==null) {
167             saveWithGeneratedId(copy, entityName, copyCache, source);
168         }
169         else {
170             saveWithRequestedId(copy, requestedId, entityName, copyCache, source);
171         }
172         
173         // cascade first, so that all unsaved objects get their
174
// copy created before we actually copy
175
super.cascadeAfterSave(source, persister, entity, copyCache);
176         copyValues(persister, entity, copy, source, copyCache, ForeignKeyDirection.FOREIGN_KEY_TO_PARENT);
177         
178         return copy;
179
180     }
181
182     protected Object JavaDoc entityIsDetached(MergeEvent event, Map JavaDoc copyCache) {
183         
184         log.trace("merging detached instance");
185         
186         final Object JavaDoc entity = event.getEntity();
187         final EventSource source = event.getSession();
188
189         final EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity );
190         final String JavaDoc entityName = persister.getEntityName();
191             
192         Serializable JavaDoc id = event.getRequestedId();
193         if ( id == null ) {
194             id = persister.getIdentifier( entity, source.getEntityMode() );
195         }
196         else {
197             //TODO: check that entity id = requestedId
198
}
199
200         final Object JavaDoc result = source.get(entityName, id);
201         if ( result == null ) {
202             //TODO: we should throw an exception if we really *know* for sure
203
// that this is a detached instance, rather than just assuming
204
//throw new StaleObjectStateException(entityName, id);
205

206             // we got here because we assumed that an instance
207
// with an assigned id was detached, when it was
208
// really persistent
209
return entityIsTransient(event, copyCache);
210         }
211         else {
212             copyCache.put(entity, result); //before cascade!
213

214             final Object JavaDoc target = source.getPersistenceContext().unproxy(result);
215             if ( target == entity ) {
216                 throw new AssertionFailure("entity was not detached");
217             }
218             else if ( !source.getEntityName(target).equals(entityName) ) {
219                 throw new WrongClassException(
220                     "class of the given object did not match class of persistent copy",
221                     event.getRequestedId(),
222                     entityName
223                 );
224             }
225             else if (
226                 persister.isVersioned() &&
227                 !persister.getVersionType().isSame(
228                         persister.getVersion( target, source.getEntityMode() ),
229                         persister.getVersion( entity, source.getEntityMode() ),
230                         source.getEntityMode()
231                 )
232             ) {
233                 throw new StaleObjectStateException( entityName, event.getRequestedId() );
234             }
235     
236             // cascade first, so that all unsaved objects get their
237
// copy created before we actually copy
238
cascadeOnMerge(source, persister, entity, copyCache);
239             copyValues(persister, entity, target, source, copyCache);
240             
241             return result;
242         }
243
244     }
245     
246     protected void copyValues(
247         final EntityPersister persister,
248         final Object JavaDoc entity,
249         final Object JavaDoc target,
250         final SessionImplementor source,
251         final Map JavaDoc copyCache
252     ) {
253         
254         final Object JavaDoc[] copiedValues = TypeFactory.replace(
255                 persister.getPropertyValues( entity, source.getEntityMode() ),
256                 persister.getPropertyValues( target, source.getEntityMode() ),
257                 persister.getPropertyTypes(),
258                 source,
259                 target,
260                 copyCache
261             );
262
263         persister.setPropertyValues( target, copiedValues, source.getEntityMode() );
264     }
265
266     protected void copyValues(
267             final EntityPersister persister,
268             final Object JavaDoc entity,
269             final Object JavaDoc target,
270             final SessionImplementor source,
271             final Map JavaDoc copyCache,
272             final ForeignKeyDirection foreignKeyDirection
273         ) {
274             
275             final Object JavaDoc[] copiedValues = TypeFactory.replace(
276                     persister.getPropertyValues( entity, source.getEntityMode() ),
277                     persister.getPropertyValues( target, source.getEntityMode() ),
278                     persister.getPropertyTypes(),
279                     source,
280                     target,
281                     copyCache,
282                     foreignKeyDirection
283                 );
284
285             persister.setPropertyValues( target, copiedValues, source.getEntityMode() );
286         }
287
288     /**
289      * Perform any cascades needed as part of this copy event.
290      *
291      * @param event The merge event being processed.
292      * @param persister The persister of the entity being copied.
293      * @param entity The entity being copied.
294      * @param copyCache A cache of already copied instance.
295      * @param cascadeTo The cascade point
296      */

297     protected void cascadeOnMerge(
298         final EventSource source,
299         final EntityPersister persister,
300         final Object JavaDoc entity,
301         final Map JavaDoc copyCache
302     ) {
303         source.getPersistenceContext().incrementCascadeLevel();
304         try {
305             new Cascade( getCascadeAction(), Cascade.BEFORE_MERGE, source )
306                     .cascade(persister, entity, copyCache);
307         }
308         finally {
309             source.getPersistenceContext().decrementCascadeLevel();
310         }
311     }
312
313
314     protected CascadingAction getCascadeAction() {
315         return isSaveOrUpdateCopyListener ?
316                 CascadingAction.SAVE_UPDATE_COPY :
317                 CascadingAction.MERGE;
318     }
319
320     protected Boolean JavaDoc getAssumedUnsaved() {
321         return Boolean.FALSE;
322     }
323     
324     /**
325      * Cascade behavior is redefined by this subclass, disable superclass behavior
326      */

327     protected void cascadeAfterSave(EventSource source, EntityPersister persister, Object JavaDoc entity, Object JavaDoc anything)
328     throws HibernateException {
329     }
330
331     /**
332      * Cascade behavior is redefined by this subclass, disable superclass behavior
333      */

334     protected void cascadeBeforeSave(EventSource source, EntityPersister persister, Object JavaDoc entity, Object JavaDoc anything)
335     throws HibernateException {
336     }
337
338 }
339
Popular Tags