KickJava   Java API By Example, From Geeks To Geeks.

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


1 //$Id: AbstractFlushingEventListener.java,v 1.14 2005/06/17 19:36:08 oneovthafew Exp $
2
package org.hibernate.event.def;
3
4 import java.util.Iterator JavaDoc;
5 import java.util.List JavaDoc;
6 import java.util.Map JavaDoc;
7
8 import org.apache.commons.logging.Log;
9 import org.apache.commons.logging.LogFactory;
10 import org.hibernate.HibernateException;
11 import org.hibernate.action.CollectionRecreateAction;
12 import org.hibernate.action.CollectionRemoveAction;
13 import org.hibernate.action.CollectionUpdateAction;
14 import org.hibernate.collection.PersistentCollection;
15 import org.hibernate.engine.ActionQueue;
16 import org.hibernate.engine.Cascade;
17 import org.hibernate.engine.CascadingAction;
18 import org.hibernate.engine.CollectionEntry;
19 import org.hibernate.engine.CollectionKey;
20 import org.hibernate.engine.Collections;
21 import org.hibernate.engine.EntityEntry;
22 import org.hibernate.engine.PersistenceContext;
23 import org.hibernate.engine.Status;
24 import org.hibernate.event.EventSource;
25 import org.hibernate.event.FlushEntityEvent;
26 import org.hibernate.event.FlushEvent;
27 import org.hibernate.engine.SessionImplementor;
28 import org.hibernate.persister.entity.EntityPersister;
29 import org.hibernate.pretty.Printer;
30 import org.hibernate.util.IdentityMap;
31
32 /**
33  * A convenience base class for listeners whose functionality results in flushing.
34  *
35  * @author Steve Eberole
36  */

37 public abstract class AbstractFlushingEventListener extends AbstractEventListener {
38
39     private static final Log log = LogFactory.getLog(AbstractFlushingEventListener.class);
40     
41     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
42
// Pre-flushing section
43
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
44

45     /**
46      * Coordinates the processing necessary to get things ready for executions
47      * as db calls by preping the session caches and moving the appropriate
48      * entities and collections to their respective execution queues.
49      *
50      * @param event The flush event.
51      * @throws HibernateException Error flushing caches to execution queues.
52      */

53     protected void flushEverythingToExecutions(FlushEvent event) throws HibernateException {
54
55         log.trace("flushing session");
56         
57         EventSource session = event.getSession();
58         
59         final PersistenceContext persistenceContext = session.getPersistenceContext();
60         session.getInterceptor().preFlush( persistenceContext.getEntitiesByKey().values().iterator() );
61
62         prepareEntityFlushes(session);
63         // we could move this inside if we wanted to
64
// tolerate collection initializations during
65
// collection dirty checking:
66
prepareCollectionFlushes(session);
67         // now, any collections that are initialized
68
// inside this block do not get updated - they
69
// are ignored until the next flush
70

71         persistenceContext.setFlushing(true);
72         try {
73             flushEntities(event);
74             flushCollections(session);
75         }
76         finally {
77             persistenceContext.setFlushing(false);
78         }
79
80         //some statistics
81
if ( log.isDebugEnabled() ) {
82             log.debug( "Flushed: " +
83                     session.getActionQueue().numberOfInsertions() + " insertions, " +
84                     session.getActionQueue().numberOfUpdates() + " updates, " +
85                     session.getActionQueue().numberOfDeletions() + " deletions to " +
86                     persistenceContext.getEntityEntries().size() + " objects"
87             );
88             log.debug( "Flushed: " +
89                     session.getActionQueue().numberOfCollectionCreations() + " (re)creations, " +
90                     session.getActionQueue().numberOfCollectionUpdates() + " updates, " +
91                     session.getActionQueue().numberOfCollectionRemovals() + " removals to " +
92                     persistenceContext.getCollectionEntries().size() + " collections"
93             );
94             new Printer( session.getFactory() ).toString( persistenceContext.getEntitiesByKey().values().iterator(), session.getEntityMode() );
95         }
96     }
97
98     /**
99      * process cascade save/update at the start of a flush to discover
100      * any newly referenced entity that must be passed to saveOrUpdate(),
101      * and also apply orphan delete
102      */

103     private void prepareEntityFlushes(EventSource session) throws HibernateException {
104         
105         log.debug("processing flush-time cascades");
106
107         final Map.Entry JavaDoc[] list = IdentityMap.concurrentEntries( session.getPersistenceContext().getEntityEntries() );
108         //safe from concurrent modification because of how entryList() is implemented on IdentityMap
109
final int size = list.length;
110         for ( int i=0; i<size; i++ ) {
111             Map.Entry JavaDoc me = list[i];
112             EntityEntry entry = (EntityEntry) me.getValue();
113             Status status = entry.getStatus();
114             if ( status == Status.MANAGED || status == Status.SAVING ) {
115                 cascadeOnFlush( session, entry.getPersister(), me.getKey() );
116             }
117         }
118     }
119     
120     private void cascadeOnFlush(EventSource session, EntityPersister persister, Object JavaDoc object)
121     throws HibernateException {
122         session.getPersistenceContext().incrementCascadeLevel();
123         try {
124             new Cascade( getCascadingAction(), Cascade.BEFORE_FLUSH, session )
125                 .cascade( persister, object, getAnything() );
126         }
127         finally {
128             session.getPersistenceContext().decrementCascadeLevel();
129         }
130     }
131     
132     protected Object JavaDoc getAnything() { return null; }
133     
134     protected CascadingAction getCascadingAction() {
135         return CascadingAction.SAVE_UPDATE;
136     }
137
138     /**
139      * Initialize the flags of the CollectionEntry, including the
140      * dirty check.
141      */

142     private void prepareCollectionFlushes(SessionImplementor session) throws HibernateException {
143
144         // Initialize dirty flags for arrays + collections with composite elements
145
// and reset reached, doupdate, etc.
146

147         log.debug("dirty checking collections");
148
149         final List JavaDoc list = IdentityMap.entries( session.getPersistenceContext().getCollectionEntries() );
150         final int size = list.size();
151         for ( int i = 0; i < size; i++ ) {
152             Map.Entry JavaDoc e = ( Map.Entry JavaDoc ) list.get( i );
153             ( (CollectionEntry) e.getValue() ).preFlush( (PersistentCollection) e.getKey() );
154         }
155     }
156
157     /**
158      * 1. detect any dirty entities
159      * 2. schedule any entity updates
160      * 3. search out any reachable collections
161      */

162     private void flushEntities(FlushEvent event) throws HibernateException {
163
164         log.trace("Flushing entities and processing referenced collections");
165
166         // Among other things, updateReachables() will recursively load all
167
// collections that are moving roles. This might cause entities to
168
// be loaded.
169

170         // So this needs to be safe from concurrent modification problems.
171
// It is safe because of how IdentityMap implements entrySet()
172

173         final EventSource source = event.getSession();
174         
175         final Map.Entry JavaDoc[] list = IdentityMap.concurrentEntries( source.getPersistenceContext().getEntityEntries() );
176         final int size = list.length;
177         for ( int i = 0; i < size; i++ ) {
178
179             // Update the status of the object and if necessary, schedule an update
180

181             Map.Entry JavaDoc me = list[i];
182             EntityEntry entry = (EntityEntry) me.getValue();
183             Status status = entry.getStatus();
184
185             if ( status != Status.LOADING && status != Status.GONE ) {
186                 FlushEntityEvent entityEvent = new FlushEntityEvent( source, me.getKey(), entry );
187                 source.getListeners().getFlushEntityEventListener().onFlushEntity(entityEvent);
188             }
189         }
190
191         source.getActionQueue().sortUpdateActions();
192     }
193
194     /**
195      * process any unreferenced collections and then inspect all known collections,
196      * scheduling creates/removes/updates
197      */

198     private void flushCollections(EventSource session) throws HibernateException {
199
200         log.trace("Processing unreferenced collections");
201
202         List JavaDoc list = IdentityMap.entries( session.getPersistenceContext().getCollectionEntries() );
203         int size = list.size();
204         for ( int i = 0; i < size; i++ ) {
205             Map.Entry JavaDoc me = ( Map.Entry JavaDoc ) list.get( i );
206             CollectionEntry ce = (CollectionEntry) me.getValue();
207             if ( !ce.isReached() && !ce.isIgnore() ) {
208                 Collections.processUnreachableCollection( (PersistentCollection) me.getKey(), session );
209             }
210         }
211
212         // Schedule updates to collections:
213

214         log.trace( "Scheduling collection removes/(re)creates/updates" );
215
216         list = IdentityMap.entries( session.getPersistenceContext().getCollectionEntries() );
217         size = list.size();
218         ActionQueue actionQueue = session.getActionQueue();
219         for ( int i = 0; i < size; i++ ) {
220             Map.Entry JavaDoc me = (Map.Entry JavaDoc) list.get(i);
221             PersistentCollection coll = (PersistentCollection) me.getKey();
222             CollectionEntry ce = (CollectionEntry) me.getValue();
223
224             if ( ce.isDorecreate() ) {
225                 actionQueue.addAction(
226                         new CollectionRecreateAction(
227                                 coll,
228                                 ce.getCurrentPersister(),
229                                 ce.getCurrentKey(),
230                                 session
231                             )
232                     );
233             }
234             if ( ce.isDoremove() ) {
235                 actionQueue.addAction(
236                         new CollectionRemoveAction(
237                                 coll,
238                                 ce.getLoadedPersister(),
239                                 ce.getLoadedKey(),
240                                 ce.isSnapshotEmpty(coll),
241                                 session
242                             )
243                     );
244             }
245             if ( ce.isDoupdate() ) {
246                 actionQueue.addAction(
247                         new CollectionUpdateAction(
248                                 coll,
249                                 ce.getLoadedPersister(),
250                                 ce.getLoadedKey(),
251                                 ce.isSnapshotEmpty(coll),
252                                 session
253                             )
254                     );
255             }
256
257         }
258
259         actionQueue.sortCollectionActions();
260         
261     }
262
263     /**
264      * Execute all SQL and second-level cache updates, in a
265      * special order so that foreign-key constraints cannot
266      * be violated:
267      * <ol>
268      * <li> Inserts, in the order they were performed
269      * <li> Updates
270      * <li> Deletion of collection elements
271      * <li> Insertion of collection elements
272      * <li> Deletes, in the order they were performed
273      * </ol>
274      */

275     protected void performExecutions(EventSource session) throws HibernateException {
276
277         log.trace("executing flush");
278
279         try {
280             // we need to lock the collection caches before
281
// executing entity inserts/updates in order to
282
// account for bidi associations
283
session.getActionQueue().prepareActions();
284             session.getActionQueue().executeActions();
285         }
286         catch (HibernateException he) {
287             log.error("Could not synchronize database state with session", he);
288             throw he;
289         }
290     }
291
292
293     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
294
// Post-flushing section
295
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
296

297     /**
298      * 1. Recreate the collection key -> collection map
299      * 2. rebuild the collection entries
300      * 3. call Interceptor.postFlush()
301      */

302     protected void postFlush(SessionImplementor session) throws HibernateException {
303
304         log.trace( "post flush" );
305
306         final PersistenceContext persistenceContext = session.getPersistenceContext();
307         persistenceContext.getCollectionsByKey().clear();
308         persistenceContext.getBatchFetchQueue()
309                 .clearSubselects(); //the database has changed now, so the subselect results need to be invalidated
310

311         Iterator JavaDoc iter = persistenceContext.getCollectionEntries().entrySet().iterator();
312         while ( iter.hasNext() ) {
313             Map.Entry JavaDoc me = (Map.Entry JavaDoc) iter.next();
314             CollectionEntry collectionEntry = (CollectionEntry) me.getValue();
315             PersistentCollection persistentCollection = (PersistentCollection) me.getKey();
316             collectionEntry.postFlush(persistentCollection);
317             if ( collectionEntry.getLoadedPersister() == null ) {
318                 //if the collection is dereferenced, remove from the session cache
319
//iter.remove(); //does not work, since the entrySet is not backed by the set
320
persistenceContext.getCollectionEntries()
321                         .remove(persistentCollection);
322             }
323             else {
324                 //otherwise recreate the mapping between the collection and its key
325
CollectionKey collectionKey = new CollectionKey(
326                         collectionEntry.getLoadedPersister(),
327                         collectionEntry.getLoadedKey(),
328                         session.getEntityMode()
329                     );
330                 persistenceContext.getCollectionsByKey()
331                         .put(collectionKey, persistentCollection);
332             }
333         }
334         
335         session.getInterceptor()
336                 .postFlush( persistenceContext.getEntitiesByKey().values().iterator() );
337
338     }
339
340 }
341
Popular Tags