KickJava   Java API By Example, From Geeks To Geeks.

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


1 //$Id: DefaultLoadEventListener.java,v 1.19 2005/07/12 20:12:55 oneovthafew Exp $
2
package org.hibernate.event.def;
3
4 import java.io.Serializable JavaDoc;
5
6 import org.apache.commons.logging.Log;
7 import org.apache.commons.logging.LogFactory;
8 import org.hibernate.HibernateException;
9 import org.hibernate.LockMode;
10 import org.hibernate.NonUniqueObjectException;
11 import org.hibernate.ObjectDeletedException;
12 import org.hibernate.ObjectNotFoundException;
13 import org.hibernate.PersistentObjectException;
14 import org.hibernate.cache.CacheConcurrencyStrategy;
15 import org.hibernate.cache.CacheKey;
16 import org.hibernate.cache.entry.CacheEntry;
17 import org.hibernate.engine.EntityEntry;
18 import org.hibernate.engine.EntityKey;
19 import org.hibernate.engine.PersistenceContext;
20 import org.hibernate.engine.SessionFactoryImplementor;
21 import org.hibernate.engine.SessionImplementor;
22 import org.hibernate.engine.Status;
23 import org.hibernate.engine.TwoPhaseLoad;
24 import org.hibernate.engine.Versioning;
25 import org.hibernate.event.EventSource;
26 import org.hibernate.event.LoadEvent;
27 import org.hibernate.event.LoadEventListener;
28 import org.hibernate.event.PostLoadEvent;
29 import org.hibernate.persister.entity.EntityPersister;
30 import org.hibernate.pretty.MessageHelper;
31 import org.hibernate.proxy.HibernateProxy;
32 import org.hibernate.proxy.LazyInitializer;
33 import org.hibernate.type.Type;
34 import org.hibernate.type.TypeFactory;
35
36 /**
37  * Defines the default load event listeners used by hibernate for loading entities
38  * in response to generated load events.
39  *
40  * @author Steve Ebersole
41  */

42 public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener implements LoadEventListener {
43
44     private static final Log log = LogFactory.getLog(DefaultLoadEventListener.class);
45
46     public static final LockMode DEFAULT_LOCK_MODE = LockMode.NONE;
47
48     /**
49      * Handle the given load event.
50      *
51      * @param event The load event to be handled.
52      * @return The result (i.e., the loaded entity).
53      * @throws HibernateException
54      */

55     public Object JavaDoc onLoad(LoadEvent event, LoadEventListener.LoadType loadType) throws HibernateException {
56
57         final SessionImplementor source = event.getSession();
58
59         EntityPersister persister;
60         if ( event.getInstanceToLoad() != null ) {
61             persister = source.getEntityPersister( null, event.getInstanceToLoad() ); //the load() which takes an entity does not pass an entityName
62
event.setEntityClassName( event.getInstanceToLoad().getClass().getName() );
63         }
64         else {
65             persister = source.getFactory().getEntityPersister( event.getEntityClassName() );
66         }
67
68         if ( persister == null ) {
69             throw new HibernateException( "Unable to locate persister: " + event.getEntityClassName() );
70         }
71         
72         EntityKey keyToLoad = new EntityKey( event.getEntityId(), persister, source.getEntityMode() );
73
74         try {
75             if ( loadType.isNakedEntityReturned() ) {
76                 //do not return a proxy!
77
//(this option inidicates we are initializing a proxy)
78
return load(event, persister, keyToLoad, loadType);
79             }
80             else {
81                 //return a proxy if appropriate
82
return event.getLockMode() == LockMode.NONE ?
83                     proxyOrLoad(event, persister, keyToLoad, loadType) :
84                     lockAndLoad(event, persister, keyToLoad, loadType, source);
85             }
86         }
87         catch(HibernateException e) {
88             log.info("Error performing load command", e);
89             throw e;
90         }
91     }
92
93     /**
94      * Perfoms the load of an entity.
95      *
96      * @return The loaded entity.
97      * @throws HibernateException
98      */

99     protected Object JavaDoc load(
100         final LoadEvent event,
101         final EntityPersister persister,
102         final EntityKey keyToLoad,
103         final LoadEventListener.LoadType options)
104     throws HibernateException {
105     
106         if ( event.getInstanceToLoad() != null ) {
107             if ( event.getSession().getPersistenceContext().getEntry( event.getInstanceToLoad() ) != null ) {
108                 throw new PersistentObjectException(
109                         "attempted to load into an instance that was already associated with the session: " +
110                         MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
111                 );
112             }
113             persister.setIdentifier( event.getInstanceToLoad(), event.getEntityId(), event.getSession().getEntityMode() );
114         }
115
116         Object JavaDoc entity = doLoad(event, persister, keyToLoad, options);
117         
118         boolean isOptionalInstance = event.getInstanceToLoad() != null;
119         
120         if ( !options.isAllowNulls() || isOptionalInstance ) {
121             ObjectNotFoundException.throwIfNull( entity, event.getEntityId(), event.getEntityClassName() );
122         }
123
124         if ( isOptionalInstance && entity != event.getInstanceToLoad() ) {
125             throw new NonUniqueObjectException( event.getEntityId(), event.getEntityClassName() );
126         }
127
128         return entity;
129     }
130
131     /**
132      * Based on configured options, will either return a pre-existing proxy,
133      * generate a new proxy, or perform an actual load.
134      *
135      * @return The result of the proxy/load operation.
136      * @throws HibernateException
137      */

138     protected Object JavaDoc proxyOrLoad(
139         final LoadEvent event,
140         final EntityPersister persister,
141         final EntityKey keyToLoad,
142         final LoadEventListener.LoadType options)
143     throws HibernateException {
144         
145         if ( log.isTraceEnabled() ) {
146             log.trace(
147                     "loading entity: " +
148                     MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
149             );
150         }
151
152         if ( !persister.hasProxy() ) {
153             // this class has no proxies (so do a shortcut)
154
return load(event, persister, keyToLoad, options);
155         }
156         else {
157             final PersistenceContext persistenceContext = event.getSession().getPersistenceContext();
158
159             // look for a proxy
160
Object JavaDoc proxy = persistenceContext.getProxy(keyToLoad);
161             if ( proxy != null ) {
162                 return returnNarrowedProxy( event, persister, keyToLoad, options, persistenceContext, proxy );
163             }
164             else {
165                 if ( options.isAllowProxyCreation() ) {
166                     return createProxyIfNecessary( event, persister, keyToLoad, options, persistenceContext );
167                 }
168                 else {
169                     // return a newly loaded object
170
return load(event, persister, keyToLoad, options);
171                 }
172             }
173             
174         }
175     }
176
177     /**
178      * Given that there is a pre-existing proxy.
179      * Initialize it if necessary; narrow if necessary.
180      */

181     private Object JavaDoc returnNarrowedProxy(
182             final LoadEvent event,
183             final EntityPersister persister,
184             final EntityKey keyToLoad,
185             final LoadEventListener.LoadType options,
186             final PersistenceContext persistenceContext,
187             final Object JavaDoc proxy
188     ) {
189         log.trace("entity proxy found in session cache");
190         LazyInitializer li = ( (HibernateProxy) proxy ).getHibernateLazyInitializer();
191         if ( li.isUnwrap() ) {
192             return li.getImplementation();
193         }
194         // return existing or narrowed proxy
195
Object JavaDoc impl = options.isAllowProxyCreation() ?
196                 null : load(event, persister, keyToLoad, options);
197         return persistenceContext.narrowProxy( proxy, persister, keyToLoad, impl );
198     }
199     
200     /**
201      * Given that there is no pre-existing proxy.
202      * Check if the entity is already loaded. If it is, return the entity,
203      * otherwise create and return a proxy.
204      */

205     private Object JavaDoc createProxyIfNecessary(
206             final LoadEvent event,
207             final EntityPersister persister,
208             final EntityKey keyToLoad,
209             final LoadEventListener.LoadType options,
210             final PersistenceContext persistenceContext
211     ) {
212         Object JavaDoc existing = persistenceContext.getEntity(keyToLoad);
213         if ( existing != null ) {
214             // return existing object or initialized proxy (unless deleted)
215
log.trace("entity found in session cache");
216             if ( options.isCheckDeleted() ) {
217                 EntityEntry entry = persistenceContext.getEntry(existing);
218                 throwObjectDeletedIfNecessary(event, entry);
219             }
220             return existing;
221         }
222         else {
223             log.trace("creating new proxy for entity");
224             // return new uninitialized proxy
225
Object JavaDoc proxy = persister.createProxy( event.getEntityId(), event.getSession() );
226             persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey(keyToLoad);
227             persistenceContext.addProxy(keyToLoad, proxy);
228             return proxy;
229         }
230     }
231
232     /**
233      * If the class to be loaded has been configured with a cache, then lock
234      * given id in that cache and then perform the load.
235      *
236      * @return The loaded entity
237      * @throws HibernateException
238      */

239     protected Object JavaDoc lockAndLoad(
240         final LoadEvent event,
241         final EntityPersister persister,
242         final EntityKey keyToLoad,
243         final LoadEventListener.LoadType options,
244         final SessionImplementor source)
245     throws HibernateException {
246         
247         CacheConcurrencyStrategy.SoftLock lock = null;
248         final CacheKey ck;
249         if ( persister.hasCache() ) {
250             ck = new CacheKey(
251                     event.getEntityId(),
252                     persister.getIdentifierType(),
253                     persister.getRootEntityName(),
254                     source.getEntityMode(),
255                     source.getFactory()
256                 );
257             lock = persister.getCache().lock(ck, null );
258         }
259         else {
260             ck = null;
261         }
262
263         Object JavaDoc entity;
264         try {
265             entity = load(event, persister, keyToLoad, options);
266         }
267         finally {
268             if ( persister.hasCache() ) {
269                 persister.getCache().release(ck, lock );
270             }
271         }
272
273         Object JavaDoc proxy = event.getSession().getPersistenceContext()
274                 .proxyFor( persister, keyToLoad, entity );
275         
276         return proxy;
277     }
278
279
280     /**
281      * Coordinates the efforts to load a given entity. First, an attempt is
282      * made to load the entity from the session-level cache. If not found there,
283      * an attempt is made to locate it in second-level cache. Lastly, an
284      * attempt is made to load it directly from the datasource.
285      *
286      * @return The loaded entity.
287      * @throws HibernateException
288      */

289     protected Object JavaDoc doLoad(
290         final LoadEvent event,
291         final EntityPersister persister,
292         final EntityKey keyToLoad,
293         final LoadEventListener.LoadType options)
294     throws HibernateException {
295         
296         if ( log.isTraceEnabled() ) {
297             log.trace(
298                     "attempting to resolve: " +
299                     MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
300             );
301         }
302
303         Object JavaDoc entity = loadFromSessionCache(event, keyToLoad, options);
304         if ( entity != null ) {
305             if ( log.isTraceEnabled() ) {
306                 log.trace(
307                         "resolved object in session cache: " +
308                         MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
309                 );
310             }
311             return entity;
312         }
313
314         // Entity not found in session; before going any further, see if we
315
// already determined that this entity does not exist
316
/*if ( event.getSession().getPersistenceContext().isNonExistant(keyToLoad) ) {
317             if ( log.isTraceEnabled() ) log.trace("entity does not exist");
318             return null;
319         }*/

320
321         entity = loadFromSecondLevelCache(event, persister, options);
322         if ( entity != null ) {
323             if ( log.isTraceEnabled() ) {
324                 log.trace(
325                         "resolved object in second-level cache: " +
326                         MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
327                 );
328             }
329             return entity;
330         }
331
332         if ( log.isTraceEnabled() ) {
333             log.trace(
334                     "object not resolved in any cache: " +
335                     MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
336             );
337         }
338
339         return loadFromDatasource(event, persister, keyToLoad, options);
340     }
341
342     /**
343      * Performs the process of loading an entity from the configured
344      * underlying datasource.
345      *
346      * @return The object loaded from the datasource, or null if not found.
347      * @throws HibernateException
348      */

349     protected Object JavaDoc loadFromDatasource(
350         final LoadEvent event,
351         final EntityPersister persister,
352         final EntityKey keyToLoad,
353         final LoadEventListener.LoadType options)
354     throws HibernateException {
355         
356         final SessionImplementor source = event.getSession();
357         
358         Object JavaDoc entity = persister.load(
359                 event.getEntityId(),
360                 event.getInstanceToLoad(),
361                 event.getLockMode(),
362                 source
363         );
364         
365         /*if ( entity == null ) {
366             //remember it doesn't exist, in case of next time
367             source.getPersistenceContext().addNonExistantEntityKey(keyToLoad);
368         }*/

369         
370         if ( event.isAssociationFetch() && source.getFactory().getStatistics().isStatisticsEnabled() ) {
371             source.getFactory().getStatisticsImplementor().fetchEntity( event.getEntityClassName() );
372         }
373
374         return entity;
375     }
376
377     /**
378      * Attempts to locate the entity in the session-level cache. If
379      * checkDeleted was set to true, then if the entity is found in the
380      * session-level cache, it's current status within the session cache
381      * is checked to see if it has previously been scheduled for deletion.
382      *
383      * @return The entity from the session-level cache, or null.
384      * @throws HibernateException
385      */

386     protected Object JavaDoc loadFromSessionCache(
387         final LoadEvent event,
388         final EntityKey keyToLoad,
389         final LoadEventListener.LoadType options)
390     throws HibernateException {
391         
392         SessionImplementor session = event.getSession();
393         Object JavaDoc old = session.getEntityUsingInterceptor(keyToLoad);
394         if ( old != null ) {
395             // this object was already loaded
396
EntityEntry oldEntry = session.getPersistenceContext().getEntry(old);
397             if ( options.isCheckDeleted() ) {
398                 throwObjectDeletedIfNecessary( event, oldEntry );
399             }
400             upgradeLock( old, oldEntry, event.getLockMode(), session );
401         }
402         return old;
403     }
404
405     private void throwObjectDeletedIfNecessary(LoadEvent event, EntityEntry oldEntry) {
406         Status status = oldEntry.getStatus();
407         if ( status == Status.DELETED || status == Status.GONE ) {
408             throw new ObjectDeletedException(
409                     "The object with that id was deleted",
410                     event.getEntityId(),
411                     event.getEntityClassName()
412             );
413         }
414     }
415
416     /**
417      * Attempts to load the entity from the second-level cache.
418      *
419      * @return The entity from the second-level cache, or null.
420      * @throws HibernateException
421      */

422     protected Object JavaDoc loadFromSecondLevelCache(
423         final LoadEvent event,
424         final EntityPersister persister,
425         final LoadEventListener.LoadType options)
426     throws HibernateException {
427         
428         final SessionImplementor source = event.getSession();
429         
430         final boolean useCache = persister.hasCache() &&
431             source.getCacheMode().isGetEnabled() &&
432             event.getLockMode().lessThan(LockMode.READ);
433         
434         if (useCache) {
435             
436             final SessionFactoryImplementor factory = source.getFactory();
437             
438             final CacheKey ck = new CacheKey(
439                     event.getEntityId(),
440                     persister.getIdentifierType(),
441                     persister.getRootEntityName(),
442                     source.getEntityMode(),
443                     source.getFactory()
444                 );
445             Object JavaDoc ce = persister.getCache()
446                 .get( ck, source.getTimestamp() );
447             
448             if ( factory.getStatistics().isStatisticsEnabled() ) {
449                 if (ce==null) {
450                     factory.getStatisticsImplementor().secondLevelCacheMiss(
451                         persister.getCache().getRegionName()
452                     );
453                 }
454                 else {
455                     factory.getStatisticsImplementor().secondLevelCacheHit(
456                         persister.getCache().getRegionName()
457                     );
458                 }
459             }
460
461             if ( ce != null ) {
462
463                 CacheEntry entry = (CacheEntry) persister.getCacheEntryStructure()
464                     .destructure(ce, factory);
465             
466                 // Entity was found in second-level cache...
467
return assembleCacheEntry(
468                         entry,
469                         event.getEntityId(),
470                         persister,
471                         event
472                 );
473             }
474         }
475         
476         return null;
477     }
478
479     private Object JavaDoc assembleCacheEntry(
480         final CacheEntry entry,
481         final Serializable JavaDoc id,
482         final EntityPersister persister,
483         final LoadEvent event)
484     throws HibernateException {
485         
486         final Object JavaDoc optionalObject = event.getInstanceToLoad();
487         final EventSource session = event.getSession();
488         final SessionFactoryImplementor factory = session.getFactory();
489         
490         if ( log.isTraceEnabled() ) log.trace(
491             "resolved object in second-level cache: " +
492             MessageHelper.infoString( persister, id, factory )
493         );
494
495         EntityPersister subclassPersister = factory.getEntityPersister( entry.getSubclass() );
496         Object JavaDoc result = optionalObject == null ?
497                 session.instantiate( subclassPersister, id ) : optionalObject;
498
499         // make it circular-reference safe
500
TwoPhaseLoad.addUninitializedEntity(
501                 new EntityKey( id, subclassPersister, session.getEntityMode() ),
502                 result,
503                 subclassPersister,
504                 LockMode.NONE,
505                 entry.areLazyPropertiesUnfetched(),
506                 session
507             );
508
509         Type[] types = subclassPersister.getPropertyTypes();
510         Object JavaDoc[] values = entry.assemble( result, id, subclassPersister, session.getInterceptor(), session ); // intializes result by side-effect
511
TypeFactory.deepCopy(
512                 values,
513                 types,
514                 subclassPersister.getPropertyUpdateability(),
515                 values,
516                 session
517             );
518         
519         Object JavaDoc version = Versioning.getVersion( values, subclassPersister );
520         if ( log.isTraceEnabled() ) log.trace( "Cached Version: " + version );
521         
522         final PersistenceContext persistenceContext = session.getPersistenceContext();
523         persistenceContext.addEntry(
524                 result,
525                 Status.MANAGED,
526                 values,
527                 null,
528                 id,
529                 version,
530                 LockMode.NONE,
531                 true,
532                 subclassPersister,
533                 false,
534                 entry.areLazyPropertiesUnfetched()
535             );
536         subclassPersister.afterInitialize( result, entry.areLazyPropertiesUnfetched(), session );
537         persistenceContext.initializeNonLazyCollections();
538         // upgrade the lock if necessary:
539
//lock(result, lockMode);
540

541         //PostLoad is needed for EJB3
542
//TODO: reuse the PostLoadEvent...
543
PostLoadEvent postLoadEvent = new PostLoadEvent(session).setEntity(result)
544                 .setId(id).setPersister(persister);
545         session.getListeners().getPostLoadEventListener().onPostLoad(postLoadEvent);
546         
547         return result;
548     }
549
550 }
551
Popular Tags