1 /*
2 * Hibernate, Relational Persistence for Idiomatic Java
3 *
4 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
5 * indicated by the @author tags or express copyright attribution
6 * statements applied by the authors. All third-party contributions are
7 * distributed under license by Red Hat Middleware LLC.
8 *
9 * This copyrighted material is made available to anyone wishing to use, modify,
10 * copy, or redistribute it subject to the terms and conditions of the GNU
11 * Lesser General Public License, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this distribution; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301 USA
23 *
24 */
25 package org.hibernate.impl;
26
27 import java.io.IOException;
28 import java.io.ObjectInputStream;
29 import java.io.ObjectOutputStream;
30 import java.io.Serializable;
31 import java.sql.Connection;
32 import java.sql.SQLException;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.HashMap;
36 import java.util.HashSet;
37 import java.util.Iterator;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.Set;
41
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 import org.hibernate.CacheMode;
46 import org.hibernate.ConnectionReleaseMode;
47 import org.hibernate.Criteria;
48 import org.hibernate.EntityMode;
49 import org.hibernate.Filter;
50 import org.hibernate.FlushMode;
51 import org.hibernate.HibernateException;
52 import org.hibernate.Interceptor;
53 import org.hibernate.LockMode;
54 import org.hibernate.MappingException;
55 import org.hibernate.ObjectDeletedException;
56 import org.hibernate.Query;
57 import org.hibernate.QueryException;
58 import org.hibernate.ReplicationMode;
59 import org.hibernate.SQLQuery;
60 import org.hibernate.ScrollMode;
61 import org.hibernate.ScrollableResults;
62 import org.hibernate.Session;
63 import org.hibernate.SessionException;
64 import org.hibernate.SessionFactory;
65 import org.hibernate.Transaction;
66 import org.hibernate.TransientObjectException;
67 import org.hibernate.UnresolvableObjectException;
68 import org.hibernate.EntityNameResolver;
69 import org.hibernate.collection.PersistentCollection;
70 import org.hibernate.engine.ActionQueue;
71 import org.hibernate.engine.CollectionEntry;
72 import org.hibernate.engine.EntityEntry;
73 import org.hibernate.engine.EntityKey;
74 import org.hibernate.engine.FilterDefinition;
75 import org.hibernate.engine.PersistenceContext;
76 import org.hibernate.engine.QueryParameters;
77 import org.hibernate.engine.StatefulPersistenceContext;
78 import org.hibernate.engine.Status;
79 import org.hibernate.engine.query.FilterQueryPlan;
80 import org.hibernate.engine.query.HQLQueryPlan;
81 import org.hibernate.engine.query.NativeSQLQueryPlan;
82 import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
83 import org.hibernate.event.AutoFlushEvent;
84 import org.hibernate.event.AutoFlushEventListener;
85 import org.hibernate.event.DeleteEvent;
86 import org.hibernate.event.DeleteEventListener;
87 import org.hibernate.event.DirtyCheckEvent;
88 import org.hibernate.event.DirtyCheckEventListener;
89 import org.hibernate.event.EventListeners;
90 import org.hibernate.event.EventSource;
91 import org.hibernate.event.EvictEvent;
92 import org.hibernate.event.EvictEventListener;
93 import org.hibernate.event.FlushEvent;
94 import org.hibernate.event.FlushEventListener;
95 import org.hibernate.event.InitializeCollectionEvent;
96 import org.hibernate.event.InitializeCollectionEventListener;
97 import org.hibernate.event.LoadEvent;
98 import org.hibernate.event.LoadEventListener;
99 import org.hibernate.event.LoadEventListener.LoadType;
100 import org.hibernate.event.LockEvent;
101 import org.hibernate.event.LockEventListener;
102 import org.hibernate.event.MergeEvent;
103 import org.hibernate.event.MergeEventListener;
104 import org.hibernate.event.PersistEvent;
105 import org.hibernate.event.PersistEventListener;
106 import org.hibernate.event.RefreshEvent;
107 import org.hibernate.event.RefreshEventListener;
108 import org.hibernate.event.ReplicateEvent;
109 import org.hibernate.event.ReplicateEventListener;
110 import org.hibernate.event.SaveOrUpdateEvent;
111 import org.hibernate.event.SaveOrUpdateEventListener;
112 import org.hibernate.exception.JDBCExceptionHelper;
113 import org.hibernate.jdbc.Batcher;
114 import org.hibernate.jdbc.JDBCContext;
115 import org.hibernate.jdbc.Work;
116 import org.hibernate.loader.criteria.CriteriaLoader;
117 import org.hibernate.loader.custom.CustomLoader;
118 import org.hibernate.loader.custom.CustomQuery;
119 import org.hibernate.persister.collection.CollectionPersister;
120 import org.hibernate.persister.entity.EntityPersister;
121 import org.hibernate.persister.entity.OuterJoinLoadable;
122 import org.hibernate.pretty.MessageHelper;
123 import org.hibernate.proxy.HibernateProxy;
124 import org.hibernate.proxy.LazyInitializer;
125 import org.hibernate.stat.SessionStatistics;
126 import org.hibernate.stat.SessionStatisticsImpl;
127 import org.hibernate.type.Type;
128 import org.hibernate.util.ArrayHelper;
129 import org.hibernate.util.CollectionHelper;
130 import org.hibernate.util.StringHelper;
131
132
133 /**
134 * Concrete implementation of a Session, and also the central, organizing component
135 * of Hibernate's internal implementation. As such, this class exposes two interfaces;
136 * Session itself, to the application, and SessionImplementor, to other components
137 * of Hibernate. This class is not threadsafe.
138 *
139 * @author Gavin King
140 */
141 public final class SessionImpl extends AbstractSessionImpl
142 implements EventSource, org.hibernate.classic.Session, JDBCContext.Context {
143
144 // todo : need to find a clean way to handle the "event source" role
145 // a seperate classs responsible for generating/dispatching events just duplicates most of the Session methods...
146 // passing around seperate reto interceptor, factory, actionQueue, and persistentContext is not manageable...
147
148 private static final Logger log = LoggerFactory.getLogger(SessionImpl.class);
149
150 private transient EntityMode entityMode = EntityMode.POJO;
151 private transient boolean autoClear; //for EJB3
152
153 private transient long timestamp;
154 private transient FlushMode flushMode = FlushMode.AUTO;
155 private transient CacheMode cacheMode = CacheMode.NORMAL;
156
157 private transient Interceptor interceptor;
158
159 private transient int dontFlushFromFind = 0;
160
161 private transient ActionQueue actionQueue;
162 private transient StatefulPersistenceContext persistenceContext;
163 private transient JDBCContext jdbcContext;
164 private transient EventListeners listeners;
165
166 private transient boolean flushBeforeCompletionEnabled;
167 private transient boolean autoCloseSessionEnabled;
168 private transient ConnectionReleaseMode connectionReleaseMode;
169
170 private transient String fetchProfile;
171
172 private transient Map enabledFilters = new HashMap();
173
174 private transient Session rootSession;
175 private transient Map childSessionsByEntityMode;
176
177 private EntityNameResolver entityNameResolver = new CoordinatingEntityNameResolver();
178
179 /**
180 * Constructor used in building "child sessions".
181 *
182 * @param parent The parent session
183 * @param entityMode
184 */
185 private SessionImpl(SessionImpl parent, EntityMode entityMode) {
186 super( parent.factory );
187 this.rootSession = parent;
188 this.timestamp = parent.timestamp;
189 this.jdbcContext = parent.jdbcContext;
190 this.interceptor = parent.interceptor;
191 this.listeners = parent.listeners;
192 this.actionQueue = new ActionQueue( this );
193 this.entityMode = entityMode;
194 this.persistenceContext = new StatefulPersistenceContext( this );
195 this.flushBeforeCompletionEnabled = false;
196 this.autoCloseSessionEnabled = false;
197 this.connectionReleaseMode = null;
198
199 if ( factory.getStatistics().isStatisticsEnabled() ) {
200 factory.getStatisticsImplementor().openSession();
201 }
202
203 log.debug( "opened session [" + entityMode + "]" );
204 }
205
206 /**
207 * Constructor used for openSession(...) processing, as well as construction
208 * of sessions for getCurrentSession().
209 *
210 * @param connection The user-supplied connection to use for this session.
211 * @param factory The factory from which this session was obtained
212 * @param autoclose NOT USED
213 * @param timestamp The timestamp for this session
214 * @param interceptor The interceptor to be applied to this session
215 * @param entityMode The entity-mode for this session
216 * @param flushBeforeCompletionEnabled Should we auto flush before completion of transaction
217 * @param autoCloseSessionEnabled Should we auto close after completion of transaction
218 * @param connectionReleaseMode The mode by which we should release JDBC connections.
219 */
220 SessionImpl(
221 final Connection connection,
222 final SessionFactoryImpl factory,
223 final boolean autoclose,
224 final long timestamp,
225 final Interceptor interceptor,
226 final EntityMode entityMode,
227 final boolean flushBeforeCompletionEnabled,
228 final boolean autoCloseSessionEnabled,
229 final ConnectionReleaseMode connectionReleaseMode) {
230 super( factory );
231 this.rootSession = null;
232 this.timestamp = timestamp;
233 this.entityMode = entityMode;
234 this.interceptor = interceptor;
235 this.listeners = factory.getEventListeners();
236 this.actionQueue = new ActionQueue( this );
237 this.persistenceContext = new StatefulPersistenceContext( this );
238 this.flushBeforeCompletionEnabled = flushBeforeCompletionEnabled;
239 this.autoCloseSessionEnabled = autoCloseSessionEnabled;
240 this.connectionReleaseMode = connectionReleaseMode;
241 this.jdbcContext = new JDBCContext( this, connection, interceptor );
242
243 if ( factory.getStatistics().isStatisticsEnabled() ) {
244 factory.getStatisticsImplementor().openSession();
245 }
246
247 if ( log.isDebugEnabled() ) {
248 log.debug( "opened session at timestamp: " + timestamp );
249 }
250 }
251
252 public Session getSession(EntityMode entityMode) {
253 if ( this.entityMode == entityMode ) {
254 return this;
255 }
256
257 if ( rootSession != null ) {
258 rootSession.getSession( entityMode );
259 }
260
261 errorIfClosed();
262 checkTransactionSynchStatus();
263
264 SessionImpl rtn = null;
265 if ( childSessionsByEntityMode == null ) {
266 childSessionsByEntityMode = new HashMap();
267 }
268 else {
269 rtn = (SessionImpl) childSessionsByEntityMode.get( entityMode );
270 }
271
272 if ( rtn == null ) {
273 rtn = new SessionImpl( this, entityMode );
274 childSessionsByEntityMode.put( entityMode, rtn );
275 }
276
277 return rtn;
278 }
279
280 public void clear() {
281 errorIfClosed();
282 checkTransactionSynchStatus();
283 persistenceContext.clear();
284 actionQueue.clear();
285 }
286
287 public Batcher getBatcher() {
288 errorIfClosed();
289 checkTransactionSynchStatus();
290 // TODO : should remove this exposure
291 // and have all references to the session's batcher use the ConnectionManager.
292 return jdbcContext.getConnectionManager().getBatcher();
293 }
294
295 public long getTimestamp() {
296 checkTransactionSynchStatus();
297 return timestamp;
298 }
299
300 public Connection close() throws HibernateException {
301 log.trace( "closing session" );
302 if ( isClosed() ) {
303 throw new SessionException( "Session was already closed" );
304 }
305
306
307 if ( factory.getStatistics().isStatisticsEnabled() ) {
308 factory.getStatisticsImplementor().closeSession();
309 }
310
311 try {
312 try {
313 if ( childSessionsByEntityMode != null ) {
314 Iterator childSessions = childSessionsByEntityMode.values().iterator();
315 while ( childSessions.hasNext() ) {
316 final SessionImpl child = ( SessionImpl ) childSessions.next();
317 child.close();
318 }
319 }
320 }
321 catch( Throwable t ) {
322 // just ignore
323 }
324
325 if ( rootSession == null ) {
326 return jdbcContext.getConnectionManager().close();
327 }
328 else {
329 return null;
330 }
331 }
332 finally {
333 setClosed();
334 cleanup();
335 }
336 }
337
338 public ConnectionReleaseMode getConnectionReleaseMode() {
339 checkTransactionSynchStatus();
340 return connectionReleaseMode;
341 }
342
343 public boolean isAutoCloseSessionEnabled() {
344 return autoCloseSessionEnabled;
345 }
346
347 public boolean isOpen() {
348 checkTransactionSynchStatus();
349 return !isClosed();
350 }
351
352 public boolean isFlushModeNever() {
353 return FlushMode.isManualFlushMode( getFlushMode() );
354 }
355
356 public boolean isFlushBeforeCompletionEnabled() {
357 return flushBeforeCompletionEnabled;
358 }
359
360 public void managedFlush() {
361 if ( isClosed() ) {
362 log.trace( "skipping auto-flush due to session closed" );
363 return;
364 }
365 log.trace("automatically flushing session");
366 flush();
367
368 if ( childSessionsByEntityMode != null ) {
369 Iterator iter = childSessionsByEntityMode.values().iterator();
370 while ( iter.hasNext() ) {
371 ( (Session) iter.next() ).flush();
372 }
373 }
374 }
375
376 public boolean shouldAutoClose() {
377 return isAutoCloseSessionEnabled() && !isClosed();
378 }
379
380 public void managedClose() {
381 log.trace( "automatically closing session" );
382 close();
383 }
384
385 public Connection connection() throws HibernateException {
386 errorIfClosed();
387 return jdbcContext.borrowConnection();
388 }
389
390 public boolean isConnected() {
391 checkTransactionSynchStatus();
392 return !isClosed() && jdbcContext.getConnectionManager().isCurrentlyConnected();
393 }
394
395 public boolean isTransactionInProgress() {
396 checkTransactionSynchStatus();
397 return !isClosed() && jdbcContext.isTransactionInProgress();
398 }
399
400 public Connection disconnect() throws HibernateException {
401 errorIfClosed();
402 log.debug( "disconnecting session" );
403 return jdbcContext.getConnectionManager().manualDisconnect();
404 }
405
406 public void reconnect() throws HibernateException {
407 errorIfClosed();
408 log.debug( "reconnecting session" );
409 checkTransactionSynchStatus();
410 jdbcContext.getConnectionManager().manualReconnect();
411 }
412
413 public void reconnect(Connection conn) throws HibernateException {
414 errorIfClosed();
415 log.debug( "reconnecting session" );
416 checkTransactionSynchStatus();
417 jdbcContext.getConnectionManager().manualReconnect( conn );
418 }
419
420 public void beforeTransactionCompletion(Transaction tx) {
421 log.trace( "before transaction completion" );
422 if ( rootSession == null ) {
423 try {
424 interceptor.beforeTransactionCompletion(tx);
425 }
426 catch (Throwable t) {
427 log.error("exception in interceptor beforeTransactionCompletion()", t);
428 }
429 }
430 }
431
432 public void setAutoClear(boolean enabled) {
433 errorIfClosed();
434 autoClear = enabled;
435 }
436
437 /**
438 * Check if there is a Hibernate or JTA transaction in progress and,
439 * if there is not, flush if necessary, make sure the connection has
440 * been committed (if it is not in autocommit mode) and run the after
441 * completion processing
442 */
443 public void afterOperation(boolean success) {
444 if ( !jdbcContext.isTransactionInProgress() ) {
445 jdbcContext.afterNontransactionalQuery( success );
446 }
447 }
448
449 public void afterTransactionCompletion(boolean success, Transaction tx) {
450 log.trace( "after transaction completion" );
451 persistenceContext.afterTransactionCompletion();
452 actionQueue.afterTransactionCompletion(success);
453 if ( rootSession == null && tx != null ) {
454 try {
455 interceptor.afterTransactionCompletion(tx);
456 }
457 catch (Throwable t) {
458 log.error("exception in interceptor afterTransactionCompletion()", t);
459 }
460 }
461 if ( autoClear ) {
462 clear();
463 }
464 }
465
466 /**
467 * clear all the internal collections, just
468 * to help the garbage collector, does not
469 * clear anything that is needed during the
470 * afterTransactionCompletion() phase
471 */
472 private void cleanup() {
473 persistenceContext.clear();
474 }
475
476 public LockMode getCurrentLockMode(Object object) throws HibernateException {
477 errorIfClosed();
478 checkTransactionSynchStatus();
479 if ( object == null ) {
480 throw new NullPointerException( "null object passed to getCurrentLockMode()" );
481 }
482 if ( object instanceof HibernateProxy ) {
483 object = ( (HibernateProxy) object ).getHibernateLazyInitializer().getImplementation(this);
484 if ( object == null ) {
485 return LockMode.NONE;
486 }
487 }
488 EntityEntry e = persistenceContext.getEntry(object);
489 if ( e == null ) {
490 throw new TransientObjectException( "Given object not associated with the session" );
491 }
492 if ( e.getStatus() != Status.MANAGED ) {
493 throw new ObjectDeletedException(
494 "The given object was deleted",
495 e.getId(),
496 e.getPersister().getEntityName()
497 );
498 }
499 return e.getLockMode();
500 }
501
502 public Object getEntityUsingInterceptor(EntityKey key) throws HibernateException {
503 errorIfClosed();
504 // todo : should this get moved to PersistentContext?
505 // logically, is PersistentContext the "thing" to which an interceptor gets attached?
506 final Object result = persistenceContext.getEntity(key);
507 if ( result == null ) {
508 final Object newObject = interceptor.getEntity( key.getEntityName(), key.getIdentifier() );
509 if ( newObject != null ) {
510 lock( newObject, LockMode.NONE );
511 }
512 return newObject;
513 }
514 else {
515 return result;
516 }
517 }
518
519
520 // saveOrUpdate() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
521
522 public void saveOrUpdate(Object object) throws HibernateException {
523 saveOrUpdate(null, object);
524 }
525
526 public void saveOrUpdate(String entityName, Object obj) throws HibernateException {
527 fireSaveOrUpdate( new SaveOrUpdateEvent(entityName, obj, this) );
528 }
529
530 private void fireSaveOrUpdate(SaveOrUpdateEvent event) {
531 errorIfClosed();
532 checkTransactionSynchStatus();
533 SaveOrUpdateEventListener[] saveOrUpdateEventListener = listeners.getSaveOrUpdateEventListeners();
534 for ( int i = 0; i < saveOrUpdateEventListener.length; i++ ) {
535 saveOrUpdateEventListener[i].onSaveOrUpdate(event);
536 }
537 }
538
539
540 // save() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
541
542 public void save(Object obj, Serializable id) throws HibernateException {
543 save(null, obj, id);
544 }
545
546 public Serializable save(Object obj) throws HibernateException {
547 return save(null, obj);
548 }
549
550 public Serializable save(String entityName, Object object) throws HibernateException {
551 return fireSave( new SaveOrUpdateEvent(entityName, object, this) );
552 }
553
554 public void save(String entityName, Object object, Serializable id) throws HibernateException {
555 fireSave( new SaveOrUpdateEvent(entityName, object, id, this) );
556 }
557
558 private Serializable fireSave(SaveOrUpdateEvent event) {
559 errorIfClosed();
560 checkTransactionSynchStatus();
561 SaveOrUpdateEventListener[] saveEventListener = listeners.getSaveEventListeners();
562 for ( int i = 0; i < saveEventListener.length; i++ ) {
563 saveEventListener[i].onSaveOrUpdate(event);
564 }
565 return event.getResultId();
566 }
567
568
569 // update() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
570
571 public void update(Object obj) throws HibernateException {
572 update(null, obj);
573 }
574
575 public void update(Object obj, Serializable id) throws HibernateException {
576 update(null, obj, id);
577 }
578
579 public void update(String entityName, Object object) throws HibernateException {
580 fireUpdate( new SaveOrUpdateEvent(entityName, object, this) );
581 }
582
583 public void update(String entityName, Object object, Serializable id) throws HibernateException {
584 fireUpdate(new SaveOrUpdateEvent(entityName, object, id, this));
585 }
586
587 private void fireUpdate(SaveOrUpdateEvent event) {
588 errorIfClosed();
589 checkTransactionSynchStatus();
590 SaveOrUpdateEventListener[] updateEventListener = listeners.getUpdateEventListeners();
591 for ( int i = 0; i < updateEventListener.length; i++ ) {
592 updateEventListener[i].onSaveOrUpdate(event);
593 }
594 }
595
596
597 // lock() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
598
599 public void lock(String entityName, Object object, LockMode lockMode) throws HibernateException {
600 fireLock( new LockEvent(entityName, object, lockMode, this) );
601 }
602
603 public void lock(Object object, LockMode lockMode) throws HibernateException {
604 fireLock( new LockEvent(object, lockMode, this) );
605 }
606
607 private void fireLock(LockEvent lockEvent) {
608 errorIfClosed();
609 checkTransactionSynchStatus();
610 LockEventListener[] lockEventListener = listeners.getLockEventListeners();
611 for ( int i = 0; i < lockEventListener.length; i++ ) {
612 lockEventListener[i].onLock( lockEvent );
613 }
614 }
615
616
617 // persist() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
618
619 public void persist(String entityName, Object object) throws HibernateException {
620 firePersist( new PersistEvent(entityName, object, this) );
621 }
622
623 public void persist(Object object) throws HibernateException {
624 persist(null, object);
625 }
626
627 public void persist(String entityName, Object object, Map copiedAlready)
628 throws HibernateException {
629 firePersist( copiedAlready, new PersistEvent(entityName, object, this) );
630 }
631
632 private void firePersist(Map copiedAlready, PersistEvent event) {
633 errorIfClosed();
634 checkTransactionSynchStatus();
635 PersistEventListener[] persistEventListener = listeners.getPersistEventListeners();
636 for ( int i = 0; i < persistEventListener.length; i++ ) {
637 persistEventListener[i].onPersist(event, copiedAlready);
638 }
639 }
640
641 private void firePersist(PersistEvent event) {
642 errorIfClosed();
643 checkTransactionSynchStatus();
644 PersistEventListener[] createEventListener = listeners.getPersistEventListeners();
645 for ( int i = 0; i < createEventListener.length; i++ ) {
646 createEventListener[i].onPersist(event);
647 }
648 }
649
650
651 // persistOnFlush() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
652
653 public void persistOnFlush(String entityName, Object object)
654 throws HibernateException {
655 firePersistOnFlush( new PersistEvent(entityName, object, this) );
656 }
657
658 public void persistOnFlush(Object object) throws HibernateException {
659 persist(null, object);
660 }
661
662 public void persistOnFlush(String entityName, Object object, Map copiedAlready)
663 throws HibernateException {
664 firePersistOnFlush( copiedAlready, new PersistEvent(entityName, object, this) );
665 }
666
667 private void firePersistOnFlush(Map copiedAlready, PersistEvent event) {
668 errorIfClosed();
669 checkTransactionSynchStatus();
670 PersistEventListener[] persistEventListener = listeners.getPersistOnFlushEventListeners();
671 for ( int i = 0; i < persistEventListener.length; i++ ) {
672 persistEventListener[i].onPersist(event, copiedAlready);
673 }
674 }
675
676 private void firePersistOnFlush(PersistEvent event) {
677 errorIfClosed();
678 checkTransactionSynchStatus();
679 PersistEventListener[] createEventListener = listeners.getPersistOnFlushEventListeners();
680 for ( int i = 0; i < createEventListener.length; i++ ) {
681 createEventListener[i].onPersist(event);
682 }
683 }
684
685
686 // merge() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
687
688 public Object merge(String entityName, Object object) throws HibernateException {
689 return fireMerge( new MergeEvent(entityName, object, this) );
690 }
691
692 public Object merge(Object object) throws HibernateException {
693 return merge(null, object);
694 }
695
696 public void merge(String entityName, Object object, Map copiedAlready) throws HibernateException {
697 fireMerge( copiedAlready, new MergeEvent(entityName, object, this) );
698 }
699
700 private Object fireMerge(MergeEvent event) {
701 errorIfClosed();
702 checkTransactionSynchStatus();
703 MergeEventListener[] mergeEventListener = listeners.getMergeEventListeners();
704 for ( int i = 0; i < mergeEventListener.length; i++ ) {
705 mergeEventListener[i].onMerge(event);
706 }
707 return event.getResult();
708 }
709
710 private void fireMerge(Map copiedAlready, MergeEvent event) {
711 errorIfClosed();
712 checkTransactionSynchStatus();
713 MergeEventListener[] mergeEventListener = listeners.getMergeEventListeners();
714 for ( int i = 0; i < mergeEventListener.length; i++ ) {
715 mergeEventListener[i].onMerge(event, copiedAlready);
716 }
717 }
718
719
720 // saveOrUpdateCopy() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
721
722 public Object saveOrUpdateCopy(String entityName, Object object)
723 throws HibernateException {
724 return fireSaveOrUpdateCopy( new MergeEvent(entityName, object, this) );
725 }
726
727 public Object saveOrUpdateCopy(Object object) throws HibernateException {
728 return saveOrUpdateCopy( null, object );
729 }
730
731 public Object saveOrUpdateCopy(String entityName, Object object, Serializable id)
732 throws HibernateException {
733 return fireSaveOrUpdateCopy( new MergeEvent(entityName, object, id, this) );
734 }
735
736 public Object saveOrUpdateCopy(Object object, Serializable id)
737 throws HibernateException {
738 return saveOrUpdateCopy( null, object, id );
739 }
740
741 public void saveOrUpdateCopy(String entityName, Object object, Map copiedAlready)
742 throws HibernateException {
743 fireSaveOrUpdateCopy( copiedAlready, new MergeEvent( entityName, object, this ) );
744 }
745
746 private void fireSaveOrUpdateCopy(Map copiedAlready, MergeEvent event) {
747 errorIfClosed();
748 checkTransactionSynchStatus();
749 MergeEventListener[] saveOrUpdateCopyEventListener = listeners.getSaveOrUpdateCopyEventListeners();
750 for ( int i = 0; i < saveOrUpdateCopyEventListener.length; i++ ) {
751 saveOrUpdateCopyEventListener[i].onMerge(event, copiedAlready);
752 }
753 }
754
755 private Object fireSaveOrUpdateCopy(MergeEvent event) {
756 errorIfClosed();
757 checkTransactionSynchStatus();
758 MergeEventListener[] saveOrUpdateCopyEventListener = listeners.getSaveOrUpdateCopyEventListeners();
759 for ( int i = 0; i < saveOrUpdateCopyEventListener.length; i++ ) {
760 saveOrUpdateCopyEventListener[i].onMerge(event);
761 }
762 return event.getResult();
763 }
764
765
766 // delete() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
767
768 /**
769 * Delete a persistent object
770 */
771 public void delete(Object object) throws HibernateException {
772 fireDelete( new DeleteEvent(object, this) );
773 }
774
775 /**
776 * Delete a persistent object (by explicit entity name)
777 */
778 public void delete(String entityName, Object object) throws HibernateException {
779 fireDelete( new DeleteEvent( entityName, object, this ) );
780 }
781
782 /**
783 * Delete a persistent object
784 */
785 public void delete(String entityName, Object object, boolean isCascadeDeleteEnabled, Set transientEntities) throws HibernateException {
786 fireDelete( new DeleteEvent( entityName, object, isCascadeDeleteEnabled, this ), transientEntities );
787 }
788
789 private void fireDelete(DeleteEvent event) {
790 errorIfClosed();
791 checkTransactionSynchStatus();
792 DeleteEventListener[] deleteEventListener = listeners.getDeleteEventListeners();
793 for ( int i = 0; i < deleteEventListener.length; i++ ) {
794 deleteEventListener[i].onDelete( event );
795 }
796 }
797
798 private void fireDelete(DeleteEvent event, Set transientEntities) {
799 errorIfClosed();
800 checkTransactionSynchStatus();
801 DeleteEventListener[] deleteEventListener = listeners.getDeleteEventListeners();
802 for ( int i = 0; i < deleteEventListener.length; i++ ) {
803 deleteEventListener[i].onDelete( event, transientEntities );
804 }
805 }
806
807
808 // load()/get() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
809
810 public void load(Object object, Serializable id) throws HibernateException {
811 LoadEvent event = new LoadEvent(id, object, this);
812 fireLoad( event, LoadEventListener.RELOAD );
813 }
814
815 public Object load(Class entityClass, Serializable id) throws HibernateException {
816 return load( entityClass.getName(), id );
817 }
818
819 public Object load(String entityName, Serializable id) throws HibernateException {
820 LoadEvent event = new LoadEvent(id, entityName, false, this);
821 boolean success = false;
822 try {
823 fireLoad( event, LoadEventListener.LOAD );
824 if ( event.getResult() == null ) {
825 getFactory().getEntityNotFoundDelegate().handleEntityNotFound( entityName, id );
826 }
827 success = true;
828 return event.getResult();
829 }
830 finally {
831 afterOperation(success);
832 }
833 }
834
835 public Object get(Class entityClass, Serializable id) throws HibernateException {
836 return get( entityClass.getName(), id );
837 }
838
839 public Object get(String entityName, Serializable id) throws HibernateException {
840 LoadEvent event = new LoadEvent(id, entityName, false, this);
841 boolean success = false;
842 try {
843 fireLoad(event, LoadEventListener.GET);
844 success = true;
845 return event.getResult();
846 }
847 finally {
848 afterOperation(success);
849 }
850 }
851
852 /**
853 * Load the data for the object with the specified id into a newly created object.
854 * This is only called when lazily initializing a proxy.
855 * Do NOT return a proxy.
856 */
857 public Object immediateLoad(String entityName, Serializable id) throws HibernateException {
858 if ( log.isDebugEnabled() ) {
859 EntityPersister persister = getFactory().getEntityPersister(entityName);
860 log.debug( "initializing proxy: " + MessageHelper.infoString( persister, id, getFactory() ) );
861 }
862
863 LoadEvent event = new LoadEvent(id, entityName, true, this);
864 fireLoad(event, LoadEventListener.IMMEDIATE_LOAD);
865 return event.getResult();
866 }
867
868 public Object internalLoad(String entityName, Serializable id, boolean eager, boolean nullable) throws HibernateException {
869 // todo : remove
870 LoadEventListener.LoadType type = nullable ?
871 LoadEventListener.INTERNAL_LOAD_NULLABLE :
872 eager ? LoadEventListener.INTERNAL_LOAD_EAGER : LoadEventListener.INTERNAL_LOAD_LAZY;
873 LoadEvent event = new LoadEvent(id, entityName, true, this);
874 fireLoad(event, type);
875 if ( !nullable ) {
876 UnresolvableObjectException.throwIfNull( event.getResult(), id, entityName );
877 }
878 return event.getResult();
879 }
880
881 public Object load(Class entityClass, Serializable id, LockMode lockMode) throws HibernateException {
882 return load( entityClass.getName(), id, lockMode );
883 }
884
885 public Object load(String entityName, Serializable id, LockMode lockMode) throws HibernateException {
886 LoadEvent event = new LoadEvent(id, entityName, lockMode, this);
887 fireLoad( event, LoadEventListener.LOAD );
888 return event.getResult();
889 }
890
891 public Object get(Class entityClass, Serializable id, LockMode lockMode) throws HibernateException {
892 return get( entityClass.getName(), id, lockMode );
893 }
894
895 public Object get(String entityName, Serializable id, LockMode lockMode) throws HibernateException {
896 LoadEvent event = new LoadEvent(id, entityName, lockMode, this);
897 fireLoad(event, LoadEventListener.GET);
898 return event.getResult();
899 }
900
901 private void fireLoad(LoadEvent event, LoadType loadType) {
902 errorIfClosed();
903 checkTransactionSynchStatus();
904 LoadEventListener[] loadEventListener = listeners.getLoadEventListeners();
905 for ( int i = 0; i < loadEventListener.length; i++ ) {
906 loadEventListener[i].onLoad(event, loadType);
907 }
908 }
909
910
911 // refresh() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
912
913 public void refresh(Object object) throws HibernateException {
914 fireRefresh( new RefreshEvent(object, this) );
915 }
916
917 public void refresh(Object object, LockMode lockMode) throws HibernateException {
918 fireRefresh( new RefreshEvent(object, lockMode, this) );
919 }
920
921 public void refresh(Object object, Map refreshedAlready) throws HibernateException {
922 fireRefresh( refreshedAlready, new RefreshEvent(object, this) );
923 }
924
925 private void fireRefresh(RefreshEvent refreshEvent) {
926 errorIfClosed();
927 checkTransactionSynchStatus();
928 RefreshEventListener[] refreshEventListener = listeners.getRefreshEventListeners();
929 for ( int i = 0; i < refreshEventListener.length; i++ ) {
930 refreshEventListener[i].onRefresh( refreshEvent );
931 }
932 }
933
934 private void fireRefresh(Map refreshedAlready, RefreshEvent refreshEvent) {
935 errorIfClosed();
936 checkTransactionSynchStatus();
937 RefreshEventListener[] refreshEventListener = listeners.getRefreshEventListeners();
938 for ( int i = 0; i < refreshEventListener.length; i++ ) {
939 refreshEventListener[i].onRefresh( refreshEvent, refreshedAlready );
940 }
941 }
942
943
944 // replicate() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
945
946 public void replicate(Object obj, ReplicationMode replicationMode) throws HibernateException {
947 fireReplicate( new ReplicateEvent(obj, replicationMode, this) );
948 }
949
950 public void replicate(String entityName, Object obj, ReplicationMode replicationMode)
951 throws HibernateException {
952 fireReplicate( new ReplicateEvent(entityName, obj, replicationMode, this) );
953 }
954
955 private void fireReplicate(ReplicateEvent event) {
956 errorIfClosed();
957 checkTransactionSynchStatus();
958 ReplicateEventListener[] replicateEventListener = listeners.getReplicateEventListeners();
959 for ( int i = 0; i < replicateEventListener.length; i++ ) {
960 replicateEventListener[i].onReplicate(event);
961 }
962 }
963
964
965 // evict() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
966
967 /**
968 * remove any hard references to the entity that are held by the infrastructure
969 * (references held by application or other persistant instances are okay)
970 */
971 public void evict(Object object) throws HibernateException {
972 fireEvict( new EvictEvent(object, this) );
973 }
974
975 private void fireEvict(EvictEvent evictEvent) {
976 errorIfClosed();
977 checkTransactionSynchStatus();
978 EvictEventListener[] evictEventListener = listeners.getEvictEventListeners();
979 for ( int i = 0; i < evictEventListener.length; i++ ) {
980 evictEventListener[i].onEvict( evictEvent );
981 }
982 }
983
984 /**
985 * detect in-memory changes, determine if the changes are to tables
986 * named in the query and, if so, complete execution the flush
987 */
988 protected boolean autoFlushIfRequired(Set querySpaces) throws HibernateException {
989 errorIfClosed();
990 if ( ! isTransactionInProgress() ) {
991 // do not auto-flush while outside a transaction
992 return false;
993 }
994 AutoFlushEvent event = new AutoFlushEvent(querySpaces, this);
995 AutoFlushEventListener[] autoFlushEventListener = listeners.getAutoFlushEventListeners();
996 for ( int i = 0; i < autoFlushEventListener.length; i++ ) {
997 autoFlushEventListener[i].onAutoFlush(event);
998 }
999 return event.isFlushRequired();
1000 }
1001
1002 public boolean isDirty() throws HibernateException {
1003 errorIfClosed();
1004 checkTransactionSynchStatus();
1005 log.debug("checking session dirtiness");
1006 if ( actionQueue.areInsertionsOrDeletionsQueued() ) {
1007 log.debug("session dirty (scheduled updates and insertions)");
1008 return true;
1009 }
1010 else {
1011 DirtyCheckEvent event = new DirtyCheckEvent(this);
1012 DirtyCheckEventListener[] dirtyCheckEventListener = listeners.getDirtyCheckEventListeners();
1013 for ( int i = 0; i < dirtyCheckEventListener.length; i++ ) {
1014 dirtyCheckEventListener[i].onDirtyCheck(event);
1015 }
1016 return event.isDirty();
1017 }
1018 }
1019
1020 public void flush() throws HibernateException {
1021 errorIfClosed();
1022 checkTransactionSynchStatus();
1023 if ( persistenceContext.getCascadeLevel() > 0 ) {
1024 throw new HibernateException("Flush during cascade is dangerous");
1025 }
1026 FlushEventListener[] flushEventListener = listeners.getFlushEventListeners();
1027 for ( int i = 0; i < flushEventListener.length; i++ ) {
1028 flushEventListener[i].onFlush( new FlushEvent(this) );
1029 }
1030 }
1031
1032 public void forceFlush(EntityEntry entityEntry) throws HibernateException {
1033 errorIfClosed();
1034 if ( log.isDebugEnabled() ) {
1035 log.debug(
1036 "flushing to force deletion of re-saved object: " +
1037 MessageHelper.infoString( entityEntry.getPersister(), entityEntry.getId(), getFactory() )
1038 );
1039 }
1040
1041 if ( persistenceContext.getCascadeLevel() > 0 ) {
1042 throw new ObjectDeletedException(
1043 "deleted object would be re-saved by cascade (remove deleted object from associations)",
1044 entityEntry.getId(),
1045 entityEntry.getPersister().getEntityName()
1046 );
1047 }
1048
1049 flush();
1050 }
1051
1052 public Filter getEnabledFilter(String filterName) {
1053 checkTransactionSynchStatus();
1054 return (Filter) enabledFilters.get(filterName);
1055 }
1056
1057 public Filter enableFilter(String filterName) {
1058 errorIfClosed();
1059 checkTransactionSynchStatus();
1060 FilterImpl filter = new FilterImpl( factory.getFilterDefinition(filterName) );
1061 enabledFilters.put(filterName, filter);
1062 return filter;
1063 }
1064
1065 public void disableFilter(String filterName) {
1066 errorIfClosed();
1067 checkTransactionSynchStatus();
1068 enabledFilters.remove(filterName);
1069 }
1070
1071 public Object getFilterParameterValue(String filterParameterName) {
1072 errorIfClosed();
1073 checkTransactionSynchStatus();
1074 String[] parsed = parseFilterParameterName(filterParameterName);
1075 FilterImpl filter = (FilterImpl) enabledFilters.get( parsed[0] );
1076 if (filter == null) {
1077 throw new IllegalArgumentException("Filter [" + parsed[0] + "] currently not enabled");
1078 }
1079 return filter.getParameter( parsed[1] );
1080 }
1081
1082 public Type getFilterParameterType(String filterParameterName) {
1083 errorIfClosed();
1084 checkTransactionSynchStatus();
1085 String[] parsed = parseFilterParameterName(filterParameterName);
1086 FilterDefinition filterDef = factory.getFilterDefinition( parsed[0] );
1087 if (filterDef == null) {
1088 throw new IllegalArgumentException("Filter [" + parsed[0] + "] not defined");
1089 }
1090 Type type = filterDef.getParameterType( parsed[1] );
1091 if (type == null) {
1092 // this is an internal error of some sort...
1093 throw new InternalError("Unable to locate type for filter parameter");
1094 }
1095 return type;
1096 }
1097
1098 public Map getEnabledFilters() {
1099 errorIfClosed();
1100 checkTransactionSynchStatus();
1101 // First, validate all the enabled filters...
1102 //TODO: this implementation has bad performance
1103 Iterator itr = enabledFilters.values().iterator();
1104 while ( itr.hasNext() ) {
1105 final Filter filter = (Filter) itr.next();
1106 filter.validate();
1107 }
1108 return enabledFilters;
1109 }
1110
1111 private String[] parseFilterParameterName(String filterParameterName) {
1112 int dot = filterParameterName.indexOf('.');
1113 if (dot <= 0) {
1114 throw new IllegalArgumentException("Invalid filter-parameter name format"); // TODO: what type?
1115 }
1116 String filterName = filterParameterName.substring(0, dot);
1117 String parameterName = filterParameterName.substring(dot+1);
1118 return new String[] {filterName, parameterName};
1119 }
1120
1121
1122 /**
1123 * Retrieve a list of persistent objects using a hibernate query
1124 */
1125 public List find(String query) throws HibernateException {
1126 return list( query, new QueryParameters() );
1127 }
1128
1129 public List find(String query, Object value, Type type) throws HibernateException {
1130 return list( query, new QueryParameters(type, value) );
1131 }
1132
1133 public List find(String query, Object[] values, Type[] types) throws HibernateException {
1134 return list( query, new QueryParameters(types, values) );
1135 }
1136
1137 public List list(String query, QueryParameters queryParameters) throws HibernateException {
1138 errorIfClosed();
1139 checkTransactionSynchStatus();
1140 queryParameters.validateParameters();
1141 HQLQueryPlan plan = getHQLQueryPlan( query, false );
1142 autoFlushIfRequired( plan.getQuerySpaces() );
1143
1144 List results = CollectionHelper.EMPTY_LIST;
1145 boolean success = false;
1146
1147 dontFlushFromFind++; //stops flush being called multiple times if this method is recursively called
1148 try {
1149 results = plan.performList( queryParameters, this );
1150 success = true;
1151 }
1152 finally {
1153 dontFlushFromFind--;
1154 afterOperation(success);
1155 }
1156 return results;
1157 }
1158
1159 public int executeUpdate(String query, QueryParameters queryParameters) throws HibernateException {
1160 errorIfClosed();
1161 checkTransactionSynchStatus();
1162 queryParameters.validateParameters();
1163 HQLQueryPlan plan = getHQLQueryPlan( query, false );
1164 autoFlushIfRequired( plan.getQuerySpaces() );
1165
1166 boolean success = false;
1167 int result = 0;
1168 try {
1169 result = plan.performExecuteUpdate( queryParameters, this );
1170 success = true;
1171 }
1172 finally {
1173 afterOperation(success);
1174 }
1175 return result;
1176 }
1177
1178 public int executeNativeUpdate(NativeSQLQuerySpecification nativeQuerySpecification,
1179 QueryParameters queryParameters) throws HibernateException {
1180 errorIfClosed();
1181 checkTransactionSynchStatus();
1182 queryParameters.validateParameters();
1183 NativeSQLQueryPlan plan = getNativeSQLQueryPlan(nativeQuerySpecification);
1184
1185
1186 autoFlushIfRequired( plan.getCustomQuery().getQuerySpaces() );
1187
1188 boolean success = false;
1189 int result = 0;
1190 try {
1191 result = plan.performExecuteUpdate(queryParameters, this);
1192 success = true;
1193 } finally {
1194 afterOperation(success);
1195 }
1196 return result;
1197 }
1198
1199 public Iterator iterate(String query) throws HibernateException {
1200 return iterate( query, new QueryParameters() );
1201 }
1202
1203 public Iterator iterate(String query, Object value, Type type) throws HibernateException {
1204 return iterate( query, new QueryParameters(type, value) );
1205 }
1206
1207 public Iterator iterate(String query, Object[] values, Type[] types) throws HibernateException {
1208 return iterate( query, new QueryParameters(types, values) );
1209 }
1210
1211 public Iterator iterate(String query, QueryParameters queryParameters) throws HibernateException {
1212 errorIfClosed();
1213 checkTransactionSynchStatus();
1214 queryParameters.validateParameters();
1215 HQLQueryPlan plan = getHQLQueryPlan( query, true );
1216 autoFlushIfRequired( plan.getQuerySpaces() );
1217
1218 dontFlushFromFind++; //stops flush being called multiple times if this method is recursively called
1219 try {
1220 return plan.performIterate( queryParameters, this );
1221 }
1222 finally {
1223 dontFlushFromFind--;
1224 }
1225 }
1226
1227 public ScrollableResults scroll(String query, QueryParameters queryParameters) throws HibernateException {
1228 errorIfClosed();
1229 checkTransactionSynchStatus();
1230 HQLQueryPlan plan = getHQLQueryPlan( query, false );
1231 autoFlushIfRequired( plan.getQuerySpaces() );
1232 dontFlushFromFind++;
1233 try {
1234 return plan.performScroll( queryParameters, this );
1235 }
1236 finally {
1237 dontFlushFromFind--;
1238 }
1239 }
1240
1241 public int delete(String query) throws HibernateException {
1242 return delete( query, ArrayHelper.EMPTY_OBJECT_ARRAY, ArrayHelper.EMPTY_TYPE_ARRAY );
1243 }
1244
1245 public int delete(String query, Object value, Type type) throws HibernateException {
1246 return delete( query, new Object[]{value}, new Type[]{type} );
1247 }
1248
1249 public int delete(String query, Object[] values, Type[] types) throws HibernateException {
1250 errorIfClosed();
1251 checkTransactionSynchStatus();
1252 if ( query == null ) {
1253 throw new IllegalArgumentException("attempt to perform delete-by-query with null query");
1254 }
1255
1256 if ( log.isTraceEnabled() ) {
1257 log.trace( "delete: " + query );
1258 if ( values.length != 0 ) {
1259 log.trace( "parameters: " + StringHelper.toString( values ) );
1260 }
1261 }
1262
1263 List list = find( query, values, types );
1264 int deletionCount = list.size();
1265 for ( int i = 0; i < deletionCount; i++ ) {
1266 delete( list.get( i ) );
1267 }
1268
1269 return deletionCount;
1270 }
1271
1272 public Query createFilter(Object collection, String queryString) {
1273 errorIfClosed();
1274 checkTransactionSynchStatus();
1275 CollectionFilterImpl filter = new CollectionFilterImpl(
1276 queryString,
1277 collection,
1278 this,
1279 getFilterQueryPlan( collection, queryString, null, false ).getParameterMetadata()
1280 );
1281 filter.setComment( queryString );
1282 return filter;
1283 }
1284
1285 public Query getNamedQuery(String queryName) throws MappingException {
1286 errorIfClosed();
1287 checkTransactionSynchStatus();
1288 return super.getNamedQuery(queryName);
1289 }
1290
1291 public Object instantiate(String entityName, Serializable id) throws HibernateException {
1292 return instantiate( factory.getEntityPersister(entityName), id );
1293 }
1294
1295 /**
1296 * give the interceptor an opportunity to override the default instantiation
1297 */
1298 public Object instantiate(EntityPersister persister, Serializable id) throws HibernateException {
1299 errorIfClosed();
1300 checkTransactionSynchStatus();
1301 Object result = interceptor.instantiate( persister.getEntityName(), entityMode, id );
1302 if ( result == null ) {
1303 result = persister.instantiate( id, entityMode );
1304 }
1305 return result;
1306 }
1307
1308 public EntityMode getEntityMode() {
1309 checkTransactionSynchStatus();
1310 return entityMode;
1311 }
1312
1313 public void setFlushMode(FlushMode flushMode) {
1314 errorIfClosed();
1315 checkTransactionSynchStatus();
1316 if ( log.isTraceEnabled() ) {
1317 log.trace("setting flush mode to: " + flushMode);
1318 }
1319 this.flushMode = flushMode;
1320 }
1321
1322 public FlushMode getFlushMode() {
1323 checkTransactionSynchStatus();
1324 return flushMode;
1325 }
1326
1327 public CacheMode getCacheMode() {
1328 checkTransactionSynchStatus();
1329 return cacheMode;
1330 }
1331
1332 public void setCacheMode(CacheMode cacheMode) {
1333 errorIfClosed();
1334 checkTransactionSynchStatus();
1335 if ( log.isTraceEnabled() ) {
1336 log.trace("setting cache mode to: " + cacheMode);
1337 }
1338 this.cacheMode= cacheMode;
1339 }
1340
1341 public Transaction getTransaction() throws HibernateException {
1342 errorIfClosed();
1343 return jdbcContext.getTransaction();
1344 }
1345
1346 public Transaction beginTransaction() throws HibernateException {
1347 errorIfClosed();
1348 if ( rootSession != null ) {
1349 // todo : should seriously consider not allowing a txn to begin from a child session
1350 // can always route the request to the root session...
1351 log.warn( "Transaction started on non-root session" );
1352 }
1353 Transaction result = getTransaction();
1354 result.begin();
1355 return result;
1356 }
1357
1358 public void afterTransactionBegin(Transaction tx) {
1359 errorIfClosed();
1360 interceptor.afterTransactionBegin(tx);
1361 }
1362
1363 public EntityPersister getEntityPersister(final String entityName, final Object object) {
1364 errorIfClosed();
1365 if (entityName==null) {
1366 return factory.getEntityPersister( guessEntityName( object ) );
1367 }
1368 else {
1369 // try block is a hack around fact that currently tuplizers are not
1370 // given the opportunity to resolve a subclass entity name. this
1371 // allows the (we assume custom) interceptor the ability to
1372 // influence this decision if we were not able to based on the
1373 // given entityName
1374 try {
1375 return factory.getEntityPersister( entityName )
1376 .getSubclassEntityPersister( object, getFactory(), entityMode );
1377 }
1378 catch( HibernateException e ) {
1379 try {
1380 return getEntityPersister( null, object );
1381 }
1382 catch( HibernateException e2 ) {
1383 throw e;
1384 }
1385 }
1386 }
1387 }
1388
1389 // not for internal use:
1390 public Serializable getIdentifier(Object object) throws HibernateException {
1391 errorIfClosed();
1392 checkTransactionSynchStatus();
1393 if ( object instanceof HibernateProxy ) {
1394 LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer();
1395 if ( li.getSession() != this ) {
1396 throw new TransientObjectException( "The proxy was not associated with this session" );
1397 }
1398 return li.getIdentifier();
1399 }
1400 else {
1401 EntityEntry entry = persistenceContext.getEntry(object);
1402 if ( entry == null ) {
1403 throw new TransientObjectException( "The instance was not associated with this session" );
1404 }
1405 return entry.getId();
1406 }
1407 }
1408
1409 /**
1410 * Get the id value for an object that is actually associated with the session. This
1411 * is a bit stricter than getEntityIdentifierIfNotUnsaved().
1412 */
1413 public Serializable getContextEntityIdentifier(Object object) {
1414 errorIfClosed();
1415 if ( object instanceof HibernateProxy ) {
1416 return getProxyIdentifier(object);
1417 }
1418 else {
1419 EntityEntry entry = persistenceContext.getEntry(object);
1420 return entry != null ? entry.getId() : null;
1421 }
1422 }
1423
1424 private Serializable getProxyIdentifier(Object proxy) {
1425 return ( (HibernateProxy) proxy ).getHibernateLazyInitializer().getIdentifier();
1426 }
1427
1428 public Collection filter(Object collection, String filter) throws HibernateException {
1429 return listFilter( collection, filter, new QueryParameters( new Type[1], new Object[1] ) );
1430 }
1431
1432 public Collection filter(Object collection, String filter, Object value, Type type) throws HibernateException {
1433 return listFilter( collection, filter, new QueryParameters( new Type[]{null, type}, new Object[]{null, value} ) );
1434 }
1435
1436 public Collection filter(Object collection, String filter, Object[] values, Type[] types)
1437 throws HibernateException {
1438 Object[] vals = new Object[values.length + 1];
1439 Type[] typs = new Type[types.length + 1];
1440 System.arraycopy( values, 0, vals, 1, values.length );
1441 System.arraycopy( types, 0, typs, 1, types.length );
1442 return listFilter( collection, filter, new QueryParameters( typs, vals ) );
1443 }
1444
1445 private FilterQueryPlan getFilterQueryPlan(
1446 Object collection,
1447 String filter,
1448 QueryParameters parameters,
1449 boolean shallow) throws HibernateException {
1450 if ( collection == null ) {
1451 throw new NullPointerException( "null collection passed to filter" );
1452 }
1453
1454 CollectionEntry entry = persistenceContext.getCollectionEntryOrNull( collection );
1455 final CollectionPersister roleBeforeFlush = (entry == null) ? null : entry.getLoadedPersister();
1456
1457 FilterQueryPlan plan = null;
1458 if ( roleBeforeFlush == null ) {
1459 // if it was previously unreferenced, we need to flush in order to
1460 // get its state into the database in order to execute query
1461 flush();
1462 entry = persistenceContext.getCollectionEntryOrNull( collection );
1463 CollectionPersister roleAfterFlush = (entry == null) ? null : entry.getLoadedPersister();
1464 if ( roleAfterFlush == null ) {
1465 throw new QueryException( "The collection was unreferenced" );
1466 }
1467 plan = factory.getQueryPlanCache().getFilterQueryPlan( filter, roleAfterFlush.getRole(), shallow, getEnabledFilters() );
1468 }
1469 else {
1470 // otherwise, we only need to flush if there are in-memory changes
1471 // to the queried tables
1472 plan = factory.getQueryPlanCache().getFilterQueryPlan( filter, roleBeforeFlush.getRole(), shallow, getEnabledFilters() );
1473 if ( autoFlushIfRequired( plan.getQuerySpaces() ) ) {
1474 // might need to run a different filter entirely after the flush
1475 // because the collection role may have changed
1476 entry = persistenceContext.getCollectionEntryOrNull( collection );
1477 CollectionPersister roleAfterFlush = (entry == null) ? null : entry.getLoadedPersister();
1478 if ( roleBeforeFlush != roleAfterFlush ) {
1479 if ( roleAfterFlush == null ) {
1480 throw new QueryException( "The collection was dereferenced" );
1481 }
1482 plan = factory.getQueryPlanCache().getFilterQueryPlan( filter, roleAfterFlush.getRole(), shallow, getEnabledFilters() );
1483 }
1484 }
1485 }
1486
1487 if ( parameters != null ) {
1488 parameters.getPositionalParameterValues()[0] = entry.getLoadedKey();
1489 parameters.getPositionalParameterTypes()[0] = entry.getLoadedPersister().getKeyType();
1490 }
1491
1492 return plan;
1493 }
1494
1495 public List listFilter(Object collection, String filter, QueryParameters queryParameters)
1496 throws HibernateException {
1497 errorIfClosed();
1498 checkTransactionSynchStatus();
1499 FilterQueryPlan plan = getFilterQueryPlan( collection, filter, queryParameters, false );
1500 List results = CollectionHelper.EMPTY_LIST;
1501
1502 boolean success = false;
1503 dontFlushFromFind++; //stops flush being called multiple times if this method is recursively called
1504 try {
1505 results = plan.performList( queryParameters, this );
1506 success = true;
1507 }
1508 finally {
1509 dontFlushFromFind--;
1510 afterOperation(success);
1511 }
1512 return results;
1513 }
1514
1515 public Iterator iterateFilter(Object collection, String filter, QueryParameters queryParameters)
1516 throws HibernateException {
1517 errorIfClosed();
1518 checkTransactionSynchStatus();
1519 FilterQueryPlan plan = getFilterQueryPlan( collection, filter, queryParameters, true );
1520 return plan.performIterate( queryParameters, this );
1521 }
1522
1523 public Criteria createCriteria(Class persistentClass, String alias) {
1524 errorIfClosed();
1525 checkTransactionSynchStatus();
1526 return new CriteriaImpl( persistentClass.getName(), alias, this );
1527 }
1528
1529 public Criteria createCriteria(String entityName, String alias) {
1530 errorIfClosed();
1531 checkTransactionSynchStatus();
1532 return new CriteriaImpl(entityName, alias, this);
1533 }
1534
1535 public Criteria createCriteria(Class persistentClass) {
1536 errorIfClosed();
1537 checkTransactionSynchStatus();
1538 return new CriteriaImpl( persistentClass.getName(), this );
1539 }
1540
1541 public Criteria createCriteria(String entityName) {
1542 errorIfClosed();
1543 checkTransactionSynchStatus();
1544 return new CriteriaImpl(entityName, this);
1545 }
1546
1547 public ScrollableResults scroll(CriteriaImpl criteria, ScrollMode scrollMode) {
1548 errorIfClosed();
1549 checkTransactionSynchStatus();
1550 String entityName = criteria.getEntityOrClassName();
1551 CriteriaLoader loader = new CriteriaLoader(
1552 getOuterJoinLoadable(entityName),
1553 factory,
1554 criteria,
1555 entityName,
1556 getEnabledFilters()
1557 );
1558 autoFlushIfRequired( loader.getQuerySpaces() );
1559 dontFlushFromFind++;
1560 try {
1561 return loader.scroll(this, scrollMode);
1562 }
1563 finally {
1564 dontFlushFromFind--;
1565 }
1566 }
1567
1568 public List list(CriteriaImpl criteria) throws HibernateException {
1569 errorIfClosed();
1570 checkTransactionSynchStatus();
1571 String[] implementors = factory.getImplementors( criteria.getEntityOrClassName() );
1572 int size = implementors.length;
1573
1574 CriteriaLoader[] loaders = new CriteriaLoader[size];
1575 Set spaces = new HashSet();
1576 for( int i=0; i <size; i++ ) {
1577
1578 loaders[i] = new CriteriaLoader(
1579 getOuterJoinLoadable( implementors[i] ),
1580 factory,
1581 criteria,
1582 implementors[i],
1583 getEnabledFilters()
1584 );
1585
1586 spaces.addAll( loaders[i].getQuerySpaces() );
1587
1588 }
1589
1590 autoFlushIfRequired(spaces);
1591
1592 List results = Collections.EMPTY_LIST;
1593 dontFlushFromFind++;
1594 boolean success = false;
1595 try {
1596 for( int i=0; i<size; i++ ) {
1597 final List currentResults = loaders[i].list(this);
1598 currentResults.addAll(results);
1599 results = currentResults;
1600 }
1601 success = true;
1602 }
1603 finally {
1604 dontFlushFromFind--;
1605 afterOperation(success);
1606 }
1607
1608 return results;
1609 }
1610
1611 private OuterJoinLoadable getOuterJoinLoadable(String entityName) throws MappingException {
1612 EntityPersister persister = factory.getEntityPersister(entityName);
1613 if ( !(persister instanceof OuterJoinLoadable) ) {
1614 throw new MappingException( "class persister is not OuterJoinLoadable: " + entityName );
1615 }
1616 return ( OuterJoinLoadable ) persister;
1617 }
1618
1619 public boolean contains(Object object) {
1620 errorIfClosed();
1621 checkTransactionSynchStatus();
1622 if ( object instanceof HibernateProxy ) {
1623 //do not use proxiesByKey, since not all
1624 //proxies that point to this session's
1625 //instances are in that collection!
1626 LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer();
1627 if ( li.isUninitialized() ) {
1628 //if it is an uninitialized proxy, pointing
1629 //with this session, then when it is accessed,
1630 //the underlying instance will be "contained"
1631 return li.getSession()==this;
1632 }
1633 else {
1634 //if it is initialized, see if the underlying
1635 //instance is contained, since we need to
1636 //account for the fact that it might have been
1637 //evicted
1638 object = li.getImplementation();
1639 }
1640 }
1641 // A session is considered to contain an entity only if the entity has
1642 // an entry in the session's persistence context and the entry reports
1643 // that the entity has not been removed
1644 EntityEntry entry = persistenceContext.getEntry( object );
1645 return entry != null && entry.getStatus() != Status.DELETED && entry.getStatus() != Status.GONE;
1646 }
1647
1648 public Query createQuery(String queryString) {
1649 errorIfClosed();
1650 checkTransactionSynchStatus();
1651 return super.createQuery(queryString);
1652 }
1653
1654 public SQLQuery createSQLQuery(String sql) {
1655 errorIfClosed();
1656 checkTransactionSynchStatus();
1657 return super.createSQLQuery(sql);
1658 }
1659
1660 public Query createSQLQuery(String sql, String returnAlias, Class returnClass) {
1661 errorIfClosed();
1662 checkTransactionSynchStatus();
1663 return new SQLQueryImpl(
1664 sql,
1665 new String[] { returnAlias },
1666 new Class[] { returnClass },
1667 this,
1668 factory.getQueryPlanCache().getSQLParameterMetadata( sql )
1669 );
1670 }
1671
1672 public Query createSQLQuery(String sql, String returnAliases[], Class returnClasses[]) {
1673 errorIfClosed();
1674 checkTransactionSynchStatus();
1675 return new SQLQueryImpl(
1676 sql,
1677 returnAliases,
1678 returnClasses,
1679 this,
1680 factory.getQueryPlanCache().getSQLParameterMetadata( sql )
1681 );
1682 }
1683
1684 public ScrollableResults scrollCustomQuery(CustomQuery customQuery, QueryParameters queryParameters)
1685 throws HibernateException {
1686 errorIfClosed();
1687 checkTransactionSynchStatus();
1688
1689 if ( log.isTraceEnabled() ) {
1690 log.trace( "scroll SQL query: " + customQuery.getSQL() );
1691 }
1692
1693 CustomLoader loader = new CustomLoader( customQuery, getFactory() );
1694
1695 autoFlushIfRequired( loader.getQuerySpaces() );
1696
1697 dontFlushFromFind++; //stops flush being called multiple times if this method is recursively called
1698 try {
1699 return loader.scroll(queryParameters, this);
1700 }
1701 finally {
1702 dontFlushFromFind--;
1703 }
1704 }
1705
1706 // basically just an adapted copy of find(CriteriaImpl)
1707 public List listCustomQuery(CustomQuery customQuery, QueryParameters queryParameters)
1708 throws HibernateException {
1709 errorIfClosed();
1710 checkTransactionSynchStatus();
1711
1712 if ( log.isTraceEnabled() ) {
1713 log.trace( "SQL query: " + customQuery.getSQL() );
1714 }
1715
1716 CustomLoader loader = new CustomLoader( customQuery, getFactory() );
1717
1718 autoFlushIfRequired( loader.getQuerySpaces() );
1719
1720 dontFlushFromFind++;
1721 boolean success = false;
1722 try {
1723 List results = loader.list(this, queryParameters);
1724 success = true;
1725 return results;
1726 }
1727 finally {
1728 dontFlushFromFind--;
1729 afterOperation(success);
1730 }
1731 }
1732
1733 public SessionFactory getSessionFactory() {
1734 checkTransactionSynchStatus();
1735 return factory;
1736 }
1737
1738 public void initializeCollection(PersistentCollection collection, boolean writing)
1739 throws HibernateException {
1740 errorIfClosed();
1741 checkTransactionSynchStatus();
1742 InitializeCollectionEventListener[] listener = listeners.getInitializeCollectionEventListeners();
1743 for ( int i = 0; i < listener.length; i++ ) {
1744 listener[i].onInitializeCollection( new InitializeCollectionEvent(collection, this) );
1745 }
1746 }
1747
1748 public String bestGuessEntityName(Object object) {
1749 if (object instanceof HibernateProxy) {
1750 LazyInitializer initializer = ( ( HibernateProxy ) object ).getHibernateLazyInitializer();
1751 // it is possible for this method to be called during flush processing,
1752 // so make certain that we do not accidently initialize an uninitialized proxy
1753 if ( initializer.isUninitialized() ) {
1754 return initializer.getEntityName();
1755 }
1756 object = initializer.getImplementation();
1757 }
1758 EntityEntry entry = persistenceContext.getEntry(object);
1759 if (entry==null) {
1760 return guessEntityName(object);
1761 }
1762 else {
1763 return entry.getPersister().getEntityName();
1764 }
1765 }
1766
1767 public String getEntityName(Object object) {
1768 errorIfClosed();
1769 checkTransactionSynchStatus();
1770 if (object instanceof HibernateProxy) {
1771 if ( !persistenceContext.containsProxy( object ) ) {
1772 throw new TransientObjectException("proxy was not associated with the session");
1773 }
1774 object = ( (HibernateProxy) object ).getHibernateLazyInitializer().getImplementation();
1775 }
1776
1777 EntityEntry entry = persistenceContext.getEntry(object);
1778 if ( entry == null ) {
1779 throwTransientObjectException( object );
1780 }
1781 return entry.getPersister().getEntityName();
1782 }
1783
1784 private void throwTransientObjectException(Object object) throws HibernateException {
1785 throw new TransientObjectException(
1786 "object references an unsaved transient instance - save the transient instance before flushing: " +
1787 guessEntityName(object)
1788 );
1789 }
1790
1791 public String guessEntityName(Object object) throws HibernateException {
1792 errorIfClosed();
1793 return entityNameResolver.resolveEntityName( object );
1794 }
1795
1796 public void cancelQuery() throws HibernateException {
1797 errorIfClosed();
1798 getBatcher().cancelLastQuery();
1799 }
1800
1801 public Interceptor getInterceptor() {
1802 checkTransactionSynchStatus();
1803 return interceptor;
1804 }
1805
1806 public int getDontFlushFromFind() {
1807 return dontFlushFromFind;
1808 }
1809
1810 public String toString() {
1811 StringBuffer buf = new StringBuffer(500)
1812 .append( "SessionImpl(" );
1813 if ( !isClosed() ) {
1814 buf.append(persistenceContext)
1815 .append(";")
1816 .append(actionQueue);
1817 }
1818 else {
1819 buf.append("<closed>");
1820 }
1821 return buf.append(')').toString();
1822 }
1823
1824 public EventListeners getListeners() {
1825 return listeners;
1826 }
1827
1828 public ActionQueue getActionQueue() {
1829 errorIfClosed();
1830 checkTransactionSynchStatus();
1831 return actionQueue;
1832 }
1833
1834 public PersistenceContext getPersistenceContext() {
1835 errorIfClosed();
1836 checkTransactionSynchStatus();
1837 return persistenceContext;
1838 }
1839
1840 public SessionStatistics getStatistics() {
1841 checkTransactionSynchStatus();
1842 return new SessionStatisticsImpl(this);
1843 }
1844
1845 public boolean isEventSource() {
1846 checkTransactionSynchStatus();
1847 return true;
1848 }
1849
1850 public void setReadOnly(Object entity, boolean readOnly) {
1851 errorIfClosed();
1852 checkTransactionSynchStatus();
1853 persistenceContext.setReadOnly(entity, readOnly);
1854 }
1855
1856 public void doWork(Work work) throws HibernateException {
1857 try {
1858 work.execute( jdbcContext.getConnectionManager().getConnection() );
1859 jdbcContext.getConnectionManager().afterStatement();
1860 }
1861 catch ( SQLException e ) {
1862 throw JDBCExceptionHelper.convert( factory.getSettings().getSQLExceptionConverter(), e, "error executing work" );
1863 }
1864 }
1865
1866 public void afterScrollOperation() {
1867 // nothing to do in a stateful session
1868 }
1869
1870 public String getFetchProfile() {
1871 checkTransactionSynchStatus();
1872 return fetchProfile;
1873 }
1874
1875 public JDBCContext getJDBCContext() {
1876 errorIfClosed();
1877 checkTransactionSynchStatus();
1878 return jdbcContext;
1879 }
1880
1881 public void setFetchProfile(String fetchProfile) {
1882 errorIfClosed();
1883 checkTransactionSynchStatus();
1884 this.fetchProfile = fetchProfile;
1885 }
1886
1887 private void checkTransactionSynchStatus() {
1888 if ( jdbcContext != null && !isClosed() ) {
1889 jdbcContext.registerSynchronizationIfPossible();
1890 }
1891 }
1892
1893 /**
1894 * Used by JDK serialization...
1895 *
1896 * @param ois The input stream from which we are being read...
1897 * @throws IOException Indicates a general IO stream exception
1898 * @throws ClassNotFoundException Indicates a class resolution issue
1899 */
1900 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
1901 log.trace( "deserializing session" );
1902
1903 entityNameResolver = new CoordinatingEntityNameResolver();
1904
1905 boolean isRootSession = ois.readBoolean();
1906 connectionReleaseMode = ConnectionReleaseMode.parse( ( String ) ois.readObject() );
1907 entityMode = EntityMode.parse( ( String ) ois.readObject() );
1908 autoClear = ois.readBoolean();
1909 flushMode = FlushMode.parse( ( String ) ois.readObject() );
1910 cacheMode = CacheMode.parse( ( String ) ois.readObject() );
1911 flushBeforeCompletionEnabled = ois.readBoolean();
1912 autoCloseSessionEnabled = ois.readBoolean();
1913 fetchProfile = ( String ) ois.readObject();
1914 interceptor = ( Interceptor ) ois.readObject();
1915
1916 factory = SessionFactoryImpl.deserialize( ois );
1917 listeners = factory.getEventListeners();
1918
1919 if ( isRootSession ) {
1920 jdbcContext = JDBCContext.deserialize( ois, this, interceptor );
1921 }
1922
1923 persistenceContext = StatefulPersistenceContext.deserialize( ois, this );
1924 actionQueue = ActionQueue.deserialize( ois, this );
1925
1926 enabledFilters = ( Map ) ois.readObject();
1927 childSessionsByEntityMode = ( Map ) ois.readObject();
1928
1929 Iterator iter = enabledFilters.values().iterator();
1930 while ( iter.hasNext() ) {
1931 ( ( FilterImpl ) iter.next() ).afterDeserialize(factory);
1932 }
1933
1934 if ( isRootSession && childSessionsByEntityMode != null ) {
1935 iter = childSessionsByEntityMode.values().iterator();
1936 while ( iter.hasNext() ) {
1937 final SessionImpl child = ( ( SessionImpl ) iter.next() );
1938 child.rootSession = this;
1939 child.jdbcContext = this.jdbcContext;
1940 }
1941 }
1942 }
1943
1944 /**
1945 * Used by JDK serialization...
1946 *
1947 * @param oos The output stream to which we are being written...
1948 * @throws IOException Indicates a general IO stream exception
1949 */
1950 private void writeObject(ObjectOutputStream oos) throws IOException {
1951 if ( !jdbcContext.getConnectionManager().isReadyForSerialization() ) {
1952 throw new IllegalStateException( "Cannot serialize a session while connected" );
1953 }
1954
1955 log.trace( "serializing session" );
1956
1957 oos.writeBoolean( rootSession == null );
1958 oos.writeObject( connectionReleaseMode.toString() );
1959 oos.writeObject( entityMode.toString() );
1960 oos.writeBoolean( autoClear );
1961 oos.writeObject( flushMode.toString() );
1962 oos.writeObject( cacheMode.toString() );
1963 oos.writeBoolean( flushBeforeCompletionEnabled );
1964 oos.writeBoolean( autoCloseSessionEnabled );
1965 oos.writeObject( fetchProfile );
1966 // we need to writeObject() on this since interceptor is user defined
1967 oos.writeObject( interceptor );
1968
1969 factory.serialize( oos );
1970
1971 if ( rootSession == null ) {
1972 jdbcContext.serialize( oos );
1973 }
1974
1975 persistenceContext.serialize( oos );
1976 actionQueue.serialize( oos );
1977
1978 // todo : look at optimizing these...
1979 oos.writeObject( enabledFilters );
1980 oos.writeObject( childSessionsByEntityMode );
1981 }
1982
1983 private class CoordinatingEntityNameResolver implements EntityNameResolver {
1984 public String resolveEntityName(Object entity) {
1985 String entityName = interceptor.getEntityName( entity );
1986 if ( entityName != null ) {
1987 return entityName;
1988 }
1989
1990 Iterator itr = factory.iterateEntityNameResolvers( entityMode );
1991 while ( itr.hasNext() ) {
1992 final EntityNameResolver resolver = ( EntityNameResolver ) itr.next();
1993 entityName = resolver.resolveEntityName( entity );
1994 if ( entityName != null ) {
1995 break;
1996 }
1997 }
1998 if ( entityName != null ) {
1999 return entityName;
2000 }
2001
2002 // the old-time stand-by...
2003 return entity.getClass().getName();
2004 }
2005 }
2006 }