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