Source code: org/hibernate/impl/SessionImpl.java
1 //$Id: SessionImpl.java,v 1.126 2005/04/24 00:11:46 oneovthafew Exp $
2 package org.hibernate.impl;
3
4 import java.io.IOException;
5 import java.io.ObjectInputStream;
6 import java.io.ObjectOutputStream;
7 import java.io.Serializable;
8 import java.sql.Connection;
9 import java.util.Collection;
10 import java.util.Collections;
11 import java.util.HashMap;
12 import java.util.HashSet;
13 import java.util.Iterator;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Set;
17
18 import org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20 import org.dom4j.Element;
21 import org.hibernate.CacheMode;
22 import org.hibernate.Criteria;
23 import org.hibernate.EntityMode;
24 import org.hibernate.Filter;
25 import org.hibernate.FlushMode;
26 import org.hibernate.HibernateException;
27 import org.hibernate.Interceptor;
28 import org.hibernate.LockMode;
29 import org.hibernate.MappingException;
30 import org.hibernate.ObjectDeletedException;
31 import org.hibernate.ObjectNotFoundException;
32 import org.hibernate.Query;
33 import org.hibernate.QueryException;
34 import org.hibernate.ReplicationMode;
35 import org.hibernate.SQLQuery;
36 import org.hibernate.ScrollMode;
37 import org.hibernate.ScrollableResults;
38 import org.hibernate.Session;
39 import org.hibernate.SessionFactory;
40 import org.hibernate.Transaction;
41 import org.hibernate.TransientObjectException;
42 import org.hibernate.UnresolvableObjectException;
43 import org.hibernate.collection.PersistentCollection;
44 import org.hibernate.engine.ActionQueue;
45 import org.hibernate.engine.CollectionEntry;
46 import org.hibernate.engine.EntityEntry;
47 import org.hibernate.engine.EntityKey;
48 import org.hibernate.engine.FilterDefinition;
49 import org.hibernate.engine.NamedQueryDefinition;
50 import org.hibernate.engine.NamedSQLQueryDefinition;
51 import org.hibernate.engine.PersistenceContext;
52 import org.hibernate.engine.QueryParameters;
53 import org.hibernate.engine.SessionFactoryImplementor;
54 import org.hibernate.engine.SessionImplementor;
55 import org.hibernate.engine.Status;
56 import org.hibernate.event.AutoFlushEvent;
57 import org.hibernate.event.PersistEvent;
58 import org.hibernate.event.DeleteEvent;
59 import org.hibernate.event.DirtyCheckEvent;
60 import org.hibernate.event.EvictEvent;
61 import org.hibernate.event.FlushEvent;
62 import org.hibernate.event.InitializeCollectionEvent;
63 import org.hibernate.event.LoadEvent;
64 import org.hibernate.event.LoadEventListener;
65 import org.hibernate.event.LockEvent;
66 import org.hibernate.event.MergeEvent;
67 import org.hibernate.event.RefreshEvent;
68 import org.hibernate.event.ReplicateEvent;
69 import org.hibernate.event.SaveOrUpdateEvent;
70 import org.hibernate.event.SessionEventListenerConfig;
71 import org.hibernate.hql.FilterTranslator;
72 import org.hibernate.hql.QuerySplitter;
73 import org.hibernate.hql.QueryTranslator;
74 import org.hibernate.jdbc.Batcher;
75 import org.hibernate.jdbc.JDBCContext;
76 import org.hibernate.loader.criteria.CriteriaLoader;
77 import org.hibernate.loader.custom.CustomLoader;
78 import org.hibernate.loader.custom.CustomQuery;
79 import org.hibernate.persister.collection.CollectionPersister;
80 import org.hibernate.persister.entity.EntityPersister;
81 import org.hibernate.persister.entity.OuterJoinLoadable;
82 import org.hibernate.pretty.MessageHelper;
83 import org.hibernate.proxy.HibernateProxy;
84 import org.hibernate.proxy.LazyInitializer;
85 import org.hibernate.type.Type;
86 import org.hibernate.util.ArrayHelper;
87 import org.hibernate.util.CollectionHelper;
88 import org.hibernate.util.EmptyIterator;
89 import org.hibernate.util.JoinedIterator;
90 import org.hibernate.util.StringHelper;
91
92
93 /**
94 * Concrete implementation of a Session, and also the central, organizing component
95 * of Hibernate's internal implementation. As such, this class exposes two interfaces;
96 * Session itself, to the application, and SessionImplementor, to other components
97 * of Hibernate. This class is not threadsafe.
98 *
99 * @author Gavin King
100 */
101 public final class SessionImpl implements SessionImplementor, JDBCContext.Context {
102
103 // todo : need to find a clean way to handle the "event source" role
104 // a seperate classs responsible for generating/dispatching events just duplicates most of the Session methods...
105 // passing around seperate references to interceptor, factory, actionQueue, and persistentContext is not manageable...
106
107 private static final Log log = LogFactory.getLog(SessionImpl.class);
108
109 private transient SessionFactoryImpl factory;
110 private EntityMode entityMode = EntityMode.POJO;
111
112 private final long timestamp;
113 private boolean closed = false;
114 private FlushMode flushMode = FlushMode.AUTO;
115 private CacheMode cacheMode = CacheMode.NORMAL;
116
117 private Interceptor interceptor;
118
119 private transient int dontFlushFromFind = 0;
120
121 private ActionQueue actionQueue;
122 private PersistenceContext persistenceContext;
123 private transient JDBCContext jdbcContext;
124 private SessionEventListenerConfig listeners;
125
126 private boolean flushBeforeCompletionEnabled;
127 private boolean autoCloseSessionEnabled;
128
129 private Map enabledFilters = new HashMap();
130
131 private boolean isRootSession = true;
132 private Map childSessionsByEntityMode;
133
134
135 public Session getSession(EntityMode entityMode) {
136 if ( this.entityMode == entityMode ) {
137 return this;
138 }
139
140 if ( childSessionsByEntityMode == null ) {
141 childSessionsByEntityMode = new HashMap();
142 }
143
144 SessionImpl rtn = (SessionImpl) childSessionsByEntityMode.get( entityMode );
145 if ( rtn == null ) {
146 rtn = new SessionImpl( this, entityMode );
147 childSessionsByEntityMode.put( entityMode, rtn );
148 }
149
150 return rtn;
151 }
152
153
154 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
155 log.trace("deserializing session");
156
157 interceptor = (Interceptor) ois.readObject();
158 factory = (SessionFactoryImpl) ois.readObject();
159 jdbcContext = (JDBCContext) ois.readObject();
160 ois.defaultReadObject();
161 }
162
163 private void writeObject(ObjectOutputStream oos) throws IOException {
164 if ( isConnected() ) throw new IllegalStateException( "Cannot serialize a Session while connected" );
165
166 log.trace( "serializing session" );
167
168 oos.writeObject(interceptor);
169 oos.writeObject(factory);
170 oos.writeObject(jdbcContext);
171 oos.defaultWriteObject();
172
173 }
174
175 public void clear() {
176 persistenceContext.clear();
177 actionQueue.clear();
178 }
179
180 private SessionImpl(SessionImpl parent, EntityMode entityMode) {
181 this.factory = parent.factory;
182
183 this.timestamp = parent.timestamp;
184
185 this.jdbcContext = parent.jdbcContext;
186
187 this.interceptor = parent.interceptor;
188 this.listeners = parent.listeners;
189
190 this.actionQueue = new ActionQueue(this);
191
192 this.entityMode = entityMode;
193 this.persistenceContext = new PersistenceContext(this);
194
195 this.isRootSession = false;
196
197 if ( factory.getStatistics().isStatisticsEnabled() ) {
198 factory.getStatisticsImplementor().openSession();
199 }
200
201 log.debug( "opened session [" + entityMode + "]" );
202 }
203
204 SessionImpl(
205 final Connection connection,
206 final SessionFactoryImpl factory,
207 final boolean autoclose,
208 final long timestamp,
209 final Interceptor interceptor,
210 final SessionEventListenerConfig listeners,
211 final EntityMode entityMode,
212 final boolean flushBeforeCompletionEnabled,
213 final boolean autoCloseSessionEnabled) {
214 this.factory = factory;
215
216 this.timestamp = timestamp;
217
218 this.entityMode = entityMode;
219
220 this.interceptor = interceptor;
221 this.listeners = listeners;
222
223 this.actionQueue = new ActionQueue( this );
224 this.persistenceContext = new PersistenceContext( this );
225
226 this.isRootSession = true;
227
228 this.flushBeforeCompletionEnabled = flushBeforeCompletionEnabled;
229 this.autoCloseSessionEnabled = autoCloseSessionEnabled;
230
231 this.jdbcContext = new JDBCContext( this, connection, autoclose );
232
233 if ( factory.getStatistics().isStatisticsEnabled() ) {
234 factory.getStatisticsImplementor().openSession();
235 }
236
237 if ( log.isDebugEnabled() ) log.debug( "opened session at timestamp: " + timestamp );
238 }
239
240 public Batcher getBatcher() {
241 return jdbcContext.getBatcher();
242 }
243
244 public SessionFactoryImplementor getFactory() {
245 return factory;
246 }
247
248 public long getTimestamp() {
249 return timestamp;
250 }
251
252 public Connection close() throws HibernateException {
253
254 log.trace( "closing session" );
255
256 if ( factory.getStatistics().isStatisticsEnabled() )
257 factory.getStatisticsImplementor().closeSession();
258
259 try {
260 try {
261 if ( childSessionsByEntityMode != null ) {
262 Iterator childSessions = childSessionsByEntityMode.values().iterator();
263 while ( childSessions.hasNext() ) {
264 final SessionImpl child = ( SessionImpl ) childSessions.next();
265 child.close();
266 }
267 }
268 }
269 catch( Throwable t ) {
270 // just ignore
271 }
272
273 if ( isRootSession ) {
274 return jdbcContext.release();
275 }
276 else {
277 return null;
278 }
279 }
280 finally {
281 closed = true;
282 cleanup();
283 }
284 }
285
286 public boolean isAggressiveReleaseEnabled() {
287 return factory.getSettings().isAggressiveReleaseEnabled();
288 }
289
290 public boolean isAutoCloseSessionEnabled() {
291 return autoCloseSessionEnabled;
292 }
293
294 public boolean isOpen() {
295 return !closed;
296 }
297
298 public boolean isFlushModeNever() {
299 return getFlushMode() == FlushMode.NEVER;
300 }
301
302 public boolean isFlushBeforeCompletionEnabled() {
303 return flushBeforeCompletionEnabled;
304 }
305
306 public void managedFlush() {
307
308 log.trace("automatically flushing session");
309 flush();
310
311 if ( childSessionsByEntityMode != null ) {
312 Iterator iter = childSessionsByEntityMode.values().iterator();
313 while ( iter.hasNext() ) {
314 ( (Session) iter.next() ).flush();
315 }
316 }
317
318 }
319
320 public boolean shouldAutoClose() {
321 return isAutoCloseSessionEnabled() && isOpen();
322 }
323
324 public void managedClose() {
325 log.trace("automatically closing session");
326 close();
327 }
328
329 public Connection connection() throws HibernateException {
330 return jdbcContext.connection();
331 }
332
333 public boolean isConnected() {
334 return jdbcContext.isConnected();
335 }
336
337 public Connection disconnect() throws HibernateException {
338 log.debug( "disconnecting session" );
339 return jdbcContext.disconnect();
340 }
341
342 public void reconnect() throws HibernateException {
343 log.debug( "reconnecting session" );
344 jdbcContext.reconnect();
345 }
346
347 public void reconnect(Connection conn) throws HibernateException {
348 log.debug( "reconnecting session" );
349 jdbcContext.reconnect( conn );
350 }
351
352 public void beforeTransactionCompletion(Transaction tx) {
353 log.trace( "before transaction completion" );
354
355 if ( !isRootSession ) {
356 log.trace( "skipping beforeTransactionCompletion processing as this is not root session" );
357 return;
358 }
359
360 try {
361 interceptor.beforeTransactionCompletion(tx);
362 }
363 catch (Throwable t) {
364 log.error("exception in interceptor beforeTransactionCompletion()", t);
365 }
366 }
367
368 public void afterTransactionCompletion(boolean success, Transaction tx) {
369 log.trace( "after transaction completion" );
370
371 persistenceContext.afterTransactionCompletion();
372 actionQueue.afterTransactionCompletion(success);
373
374 if ( isRootSession ) {
375
376 try {
377 interceptor.afterTransactionCompletion(tx);
378 }
379 catch (Throwable t) {
380 log.error("exception in interceptor beforeTransactionCompletion()", t);
381 }
382
383 }
384
385 }
386
387 /**
388 * clear all the internal collections, just
389 * to help the garbage collector, does not
390 * clear anything that is needed during the
391 * afterTransactionCompletion() phase
392 */
393 private void cleanup() {
394 persistenceContext.clear();
395 }
396
397 public LockMode getCurrentLockMode(Object object) throws HibernateException {
398 if ( object == null ) throw new NullPointerException( "null object passed to getCurrentLockMode()" );
399 if ( object instanceof HibernateProxy ) {
400 object = ( (HibernateProxy) object ).getHibernateLazyInitializer().getImplementation(this);
401 if ( object == null ) return LockMode.NONE;
402 }
403 EntityEntry e = persistenceContext.getEntry(object);
404 if ( e == null ) throw new TransientObjectException( "Given object not associated with the session" );
405 if ( e.getStatus() != Status.MANAGED ) throw new ObjectDeletedException(
406 "The given object was deleted",
407 e.getId(), e.getPersister().getEntityName()
408 );
409 return e.getLockMode();
410 }
411
412 public Object getEntityUsingInterceptor(EntityKey key) throws HibernateException {
413 // todo : should this get moved to PersistentContext?
414 // logically, is PersistentContext the "thing" to which an interceptor gets attached?
415 final Object result = persistenceContext.getEntity(key);
416 if ( result == null ) {
417 final Object newObject = interceptor.getEntity( key.getEntityName(), key.getIdentifier() );
418 if ( newObject != null ) lock(newObject, LockMode.NONE);
419 return newObject;
420 }
421 else {
422 return result;
423 }
424 }
425
426 public void saveOrUpdate(Object object) throws HibernateException {
427 saveOrUpdate(null, object);
428 }
429
430 public void saveOrUpdate(String entityName, Object obj) throws HibernateException {
431 SaveOrUpdateEvent event = new SaveOrUpdateEvent(entityName, obj, this);
432 listeners.getSaveOrUpdateEventListener().onSaveOrUpdate(event);
433 }
434
435 public void save(Object obj, Serializable id) throws HibernateException {
436 save(null, obj, id);
437 }
438
439 public Serializable save(Object obj) throws HibernateException {
440 return save(null, obj);
441 }
442
443 public Serializable save(String entityName, Object object) throws HibernateException {
444 SaveOrUpdateEvent event = new SaveOrUpdateEvent(entityName, object, this);
445 return listeners.getSaveEventListener().onSaveOrUpdate(event);
446 }
447
448 public void save(String entityName, Object object, Serializable id) throws HibernateException {
449 SaveOrUpdateEvent event = new SaveOrUpdateEvent(entityName, object, id, this);
450 listeners.getSaveEventListener().onSaveOrUpdate(event);
451 }
452
453 public void update(Object obj) throws HibernateException {
454 update(null, obj);
455 }
456
457 public void update(Object obj, Serializable id) throws HibernateException {
458 update(null, obj, id);
459 }
460
461 public void update(String entityName, Object object) throws HibernateException {
462 SaveOrUpdateEvent event = new SaveOrUpdateEvent(entityName, object, this);
463 listeners.getUpdateEventListener().onSaveOrUpdate(event);
464 }
465
466 public void update(String entityName, Object object, Serializable id) throws HibernateException {
467 SaveOrUpdateEvent event = new SaveOrUpdateEvent(entityName, object, id, this);
468 listeners.getUpdateEventListener().onSaveOrUpdate(event);
469 }
470
471 public void lock(Object object, LockMode lockMode) throws HibernateException {
472 listeners.getLockEventListener().onLock( new LockEvent(object, lockMode, this) );
473 }
474
475 public void lock(String entityName, Object object, LockMode lockMode) throws HibernateException {
476 LockEvent event = new LockEvent(entityName, object, lockMode, this);
477 listeners.getLockEventListener().onLock(event);
478 }
479
480 public void persist(String entityName, Object object, Map copiedAlready)
481 throws HibernateException {
482 PersistEvent event = new PersistEvent(entityName, object, this);
483 listeners.getCreateEventListener().onPersist(event, copiedAlready);
484 }
485
486 public void persist(String entityName, Object object)
487 throws HibernateException {
488 PersistEvent event = new PersistEvent(entityName, object, this);
489 listeners.getCreateEventListener().onPersist(event);
490 }
491
492 public void persist(Object object) throws HibernateException {
493 persist(null, object);
494 }
495
496 public Object merge(String entityName, Object object)
497 throws HibernateException {
498 MergeEvent event = new MergeEvent(entityName, object, this);
499 return listeners.getMergeEventListener().onMerge(event);
500 }
501
502 public Object merge(Object object) throws HibernateException {
503 return merge(null, object);
504 }
505
506 public void merge(String entityName, Object object, Map copiedAlready) throws HibernateException {
507 MergeEvent event = new MergeEvent(entityName, object, this);
508 listeners.getMergeEventListener().onMerge(event, copiedAlready);
509 }
510
511 public Object saveOrUpdateCopy(String entityName, Object object)
512 throws HibernateException {
513 MergeEvent event = new MergeEvent(entityName, object, this);
514 return listeners.getSaveOrUpdateCopyEventListener().onMerge(event);
515 }
516
517 public Object saveOrUpdateCopy(Object object) throws HibernateException {
518 return saveOrUpdateCopy(null, object);
519 }
520
521 public Object saveOrUpdateCopy(String entityName, Object object, Serializable id)
522 throws HibernateException {
523 MergeEvent event = new MergeEvent(entityName, object, id, this);
524 return listeners.getSaveOrUpdateCopyEventListener().onMerge(event);
525 }
526
527 public Object saveOrUpdateCopy(Object object, Serializable id)
528 throws HibernateException {
529 return saveOrUpdateCopy(null, object, id);
530 }
531
532 public void saveOrUpdateCopy(String entityName, Object object, Map copiedAlready)
533 throws HibernateException {
534 MergeEvent event = new MergeEvent(entityName, object, this);
535 listeners.getSaveOrUpdateCopyEventListener().onMerge(event, copiedAlready);
536 }
537
538 /**
539 * Delete a persistent object
540 */
541 public void delete(Object object) throws HibernateException {
542 DeleteEvent event = new DeleteEvent(object, this);
543 listeners.getDeleteEventListener().onDelete(event);
544 }
545
546 /**
547 * Delete a persistent object
548 */
549 public void delete(String entityName, Object object, boolean isCascadeDeleteEnabled) throws HibernateException {
550 DeleteEvent event = new DeleteEvent(entityName, object, isCascadeDeleteEnabled, this);
551 listeners.getDeleteEventListener().onDelete(event);
552 }
553
554 public void load(Object object, Serializable id) throws HibernateException {
555 LoadEvent event = new LoadEvent(id, object, this);
556 listeners.getLoadEventListener().onLoad(event, null);
557 }
558
559 public Object load(Class entityClass, Serializable id) throws HibernateException {
560 return load( entityClass.getName(), id );
561 }
562
563 public Object load(String entityName, Serializable id) throws HibernateException {
564 LoadEvent event = new LoadEvent(id, entityName, false, this);
565 Object result = listeners.getLoadEventListener().onLoad(event, LoadEventListener.LOAD);
566
567 ObjectNotFoundException.throwIfNull(result, id, entityName);
568 return result;
569 }
570
571 public Object get(Class entityClass, Serializable id) throws HibernateException {
572 return get( entityClass.getName(), id );
573 }
574
575 public Object get(String entityName, Serializable id) throws HibernateException {
576 LoadEvent event = new LoadEvent(id, entityName, false, this);
577 return listeners.getLoadEventListener().onLoad(event, LoadEventListener.GET);
578 }
579
580 /**
581 * Load the data for the object with the specified id into a newly created object.
582 * This is only called when lazily initializing a proxy.
583 * Do NOT return a proxy.
584 */
585 public Object immediateLoad(String entityName, Serializable id) throws HibernateException {
586
587 if ( log.isDebugEnabled() ) {
588 EntityPersister persister = getFactory().getEntityPersister(entityName);
589 log.debug( "initializing proxy: " + MessageHelper.infoString( persister, id, getFactory() ) );
590 }
591
592 LoadEvent event = new LoadEvent(id, entityName, true, this);
593 Object result = listeners.getLoadEventListener().onLoad(event, LoadEventListener.IMMEDIATE_LOAD);
594
595 ObjectNotFoundException.throwIfNull(result, id, entityName); //should it be UnresolvableObject?
596 return result;
597 }
598
599 public Object internalLoad(String entityName, Serializable id, boolean nullable) throws HibernateException {
600 // todo : remove
601 LoadEvent event = new LoadEvent(id, entityName, true, this);
602 Object result = listeners.getLoadEventListener()
603 .onLoad(event, nullable ? LoadEventListener.INTERNAL_LOAD_NULLABLE : LoadEventListener.INTERNAL_LOAD);
604 if (!nullable) UnresolvableObjectException.throwIfNull(result, id, entityName);
605 return result;
606 }
607
608 public Object load(Class entityClass, Serializable id, LockMode lockMode) throws HibernateException {
609 return load( entityClass.getName(), id, lockMode );
610 }
611
612 public Object load(String entityName, Serializable id, LockMode lockMode) throws HibernateException {
613 LoadEvent event = new LoadEvent(id, entityName, lockMode, this);
614 return listeners.getLoadEventListener().onLoad(event, LoadEventListener.LOAD);
615 }
616
617 public Object get(Class entityClass, Serializable id, LockMode lockMode) throws HibernateException {
618 return get( entityClass.getName(), id, lockMode );
619 }
620
621 public Object get(String entityName, Serializable id, LockMode lockMode) throws HibernateException {
622 LoadEvent event = new LoadEvent(id, entityName, lockMode, this);
623 return listeners.getLoadEventListener().onLoad(event, LoadEventListener.GET);
624 }
625
626 public void refresh(Object object) throws HibernateException {
627 listeners.getRefreshEventListener().onRefresh( new RefreshEvent(object, this) );
628 }
629
630 public void refresh(Object object, LockMode lockMode) throws HibernateException {
631 listeners.getRefreshEventListener().onRefresh( new RefreshEvent(object, lockMode, this) );
632 }
633
634 public void replicate(Object obj, ReplicationMode replicationMode) throws HibernateException {
635 ReplicateEvent event = new ReplicateEvent(obj, replicationMode, this);
636 listeners.getReplicateEventListener().onReplicate(event);
637 }
638
639 public void replicate(String entityName, Object obj, ReplicationMode replicationMode)
640 throws HibernateException {
641 ReplicateEvent event = new ReplicateEvent(entityName, obj, replicationMode, this);
642 listeners.getReplicateEventListener().onReplicate(event);
643 }
644
645 /**
646 * remove any hard references to the entity that are held by the infrastructure
647 * (references held by application or other persistant instances are okay)
648 */
649 public void evict(Object object) throws HibernateException {
650 listeners.getEvictEventListener().onEvict( new EvictEvent(object, this) );
651 }
652
653 /**
654 * detect in-memory changes, determine if the changes are to tables
655 * named in the query and, if so, complete execution the flush
656 */
657 private boolean autoFlushIfRequired(Set querySpaces) throws HibernateException {
658 AutoFlushEvent event = new AutoFlushEvent(querySpaces, this);
659 return listeners.getAutoFlushEventListener().onAutoFlush(event);
660 }
661
662 public boolean isDirty() throws HibernateException {
663 log.debug("checking session dirtiness");
664 if ( actionQueue.areInsertionsOrDeletionsQueued() ) {
665 log.debug("session dirty (scheduled updates and insertions)");
666 return true;
667 }
668 else {
669 DirtyCheckEvent event = new DirtyCheckEvent(this);
670 return listeners.getDirtyCheckEventListener().onDirtyCheck(event);
671 }
672 }
673
674 public void flush() throws HibernateException {
675 if ( persistenceContext.getCascadeLevel() > 0 ) {
676 throw new HibernateException("Flush during cascade is dangerous");
677 }
678 listeners.getFlushEventListener().onFlush( new FlushEvent(this) );
679 }
680
681 public void forceFlush(EntityEntry e) throws HibernateException {
682 if ( log.isDebugEnabled() ) {
683 log.debug(
684 "flushing to force deletion of re-saved object: " +
685 MessageHelper.infoString( e.getPersister(), e.getId(), getFactory() )
686 );
687 }
688
689 if ( persistenceContext.getCascadeLevel() > 0 ) {
690 throw new ObjectDeletedException(
691 "deleted object would be re-saved by cascade (remove deleted object from associations)",
692 e.getId(),
693 e.getPersister().getEntityName()
694 );
695 }
696
697 flush();
698 }
699
700 public Filter enableFilter(String filterName) {
701 FilterImpl filter = new FilterImpl( factory.getFilterDefinition(filterName) );
702 enabledFilters.put(filterName, filter);
703 return filter;
704 }
705
706 public Filter getEnabledFilter(String filterName) {
707 return (Filter) enabledFilters.get(filterName);
708 }
709
710 public void disableFilter(String filterName) {
711 enabledFilters.remove(filterName);
712 }
713
714 public Object getFilterParameterValue(String filterParameterName) {
715 String[] parsed = parseFilterParameterName(filterParameterName);
716 FilterImpl filter = (FilterImpl) enabledFilters.get( parsed[0] );
717 if (filter == null) {
718 throw new IllegalArgumentException("Filter [" + parsed[0] + "] currently not enabled");
719 }
720 return filter.getParameter( parsed[1] );
721 }
722
723 public Type getFilterParameterType(String filterParameterName) {
724 String[] parsed = parseFilterParameterName(filterParameterName);
725 FilterDefinition filterDef = factory.getFilterDefinition( parsed[0] );
726 if (filterDef == null) {
727 throw new IllegalArgumentException("Filter [" + parsed[0] + "] not defined");
728 }
729 Type type = filterDef.getParameterType( parsed[1] );
730 if (type == null) {
731 // this is an internal error of some sort...
732 throw new InternalError("Unable to locate type for filter parameter");
733 }
734 return type;
735 }
736
737 public Map getEnabledFilters() {
738 // First, validate all the enabled filters...
739 //TODO: this implementation has bad performance
740 Iterator itr = enabledFilters.values().iterator();
741 while ( itr.hasNext() ) {
742 final Filter filter = (Filter) itr.next();
743 filter.validate();
744 }
745 return enabledFilters;
746 }
747
748 private String[] parseFilterParameterName(String filterParameterName) {
749 int dot = filterParameterName.indexOf('.');
750 if (dot <= 0) {
751 throw new IllegalArgumentException("Invalid filter-parameter name format"); // TODO: what type?
752 }
753 String filterName = filterParameterName.substring(0, dot);
754 String parameterName = filterParameterName.substring(dot+1);
755 return new String[] {filterName, parameterName};
756 }
757
758
759 /**
760 * Retrieve a list of persistent objects using a hibernate query
761 */
762 public List find(String query) throws HibernateException {
763 return list( query, new QueryParameters() );
764 }
765
766 public List find(String query, Object value, Type type) throws HibernateException {
767 return list( query, new QueryParameters(type, value) );
768 }
769
770 public List find(String query, Object[] values, Type[] types) throws HibernateException {
771 return list( query, new QueryParameters(types, values) );
772 }
773
774 public List list(String query, QueryParameters queryParameters) throws HibernateException {
775
776 if ( log.isTraceEnabled() ) {
777 log.trace( "find: " + query );
778 queryParameters.traceParameters(factory);
779 }
780
781 queryParameters.validateParameters();
782 QueryTranslator[] q = getQueries(query, false);
783
784 List results = CollectionHelper.EMPTY_LIST;
785
786 dontFlushFromFind++; //stops flush being called multiple times if this method is recursively called
787
788 //execute the queries and return all result lists as a single list
789 try {
790 for ( int i = 0; i < q.length; i++ ) {
791 List currentResults = q[i].list(this, queryParameters);
792 currentResults.addAll(results);
793 results = currentResults;
794 }
795 }
796 finally {
797 dontFlushFromFind--;
798 }
799 return results;
800 }
801
802 public int executeUpdate(String query, QueryParameters queryParameters) throws HibernateException {
803
804 if ( log.isTraceEnabled() ) {
805 log.trace( "executeUpdate: " + query );
806 queryParameters.traceParameters(factory);
807 }
808
809 queryParameters.validateParameters();
810 QueryTranslator[] queryTranslators = getQueries(query, false);
811
812 int result = 0;
813 for (int i=0; i<queryTranslators.length; i++) {
814 result += queryTranslators[i].executeUpdate( queryParameters, this );
815 }
816 return result;
817 }
818
819 private QueryTranslator[] getQueries(String query, boolean scalar) throws HibernateException {
820
821 // take the union of the query spaces (ie. the queried tables)
822 QueryTranslator[] q = factory.getQuery( query, scalar, getEnabledFilters() );
823 return prepareQueries(q);
824
825 }
826
827 private QueryTranslator[] prepareQueries(QueryTranslator[] q) {
828 HashSet qs = new HashSet();
829 for ( int i = 0; i < q.length; i++ ) {
830 qs.addAll( q[i].getQuerySpaces() );
831 }
832
833 autoFlushIfRequired(qs);
834
835 return q;
836 }
837
838 public Iterator iterate(String query) throws HibernateException {
839 return iterate( query, new QueryParameters() );
840 }
841
842 public Iterator iterate(String query, Object value, Type type) throws HibernateException {
843 return iterate( query, new QueryParameters(type, value) );
844 }
845
846 public Iterator iterate(String query, Object[] values, Type[] types) throws HibernateException {
847 return iterate( query, new QueryParameters(types, values) );
848 }
849
850 public Iterator iterate(String query, QueryParameters queryParameters) throws HibernateException {
851
852 if ( log.isTraceEnabled() ) {
853 log.trace( "iterate: " + query );
854 queryParameters.traceParameters(factory);
855 }
856
857 queryParameters.validateParameters();
858 QueryTranslator[] q = getQueries(query, true);
859
860 if ( q.length == 0 ) return EmptyIterator.INSTANCE;
861
862 Iterator result = null;
863 Iterator[] results = null;
864 boolean many = q.length > 1;
865 if (many) results = new Iterator[q.length];
866
867 dontFlushFromFind++; //stops flush being called multiple times if this method is recursively called
868
869 try {
870
871 //execute the queries and return all results as a single iterator
872 for ( int i = 0; i < q.length; i++ ) {
873 result = q[i].iterate(queryParameters, this);
874 if (many) results[i] = result;
875 }
876
877 return many ? new JoinedIterator(results) : result;
878
879 }
880 finally {
881 dontFlushFromFind--;
882 }
883 }
884
885 public ScrollableResults scroll(String query, QueryParameters queryParameters) throws HibernateException {
886
887 if ( log.isTraceEnabled() ) {
888 log.trace( "scroll: " + query );
889 queryParameters.traceParameters( factory );
890 }
891
892 QueryTranslator[] q = factory.getQuery( query, false, getEnabledFilters() );
893 if ( q.length != 1 ) throw new QueryException( "implicit polymorphism not supported for scroll() queries" );
894 autoFlushIfRequired( q[0].getQuerySpaces() );
895
896 dontFlushFromFind++; //stops flush being called multiple times if this method is recursively called
897 try {
898 return q[0].scroll(queryParameters, this);
899 }
900 finally {
901 dontFlushFromFind--;
902 }
903 }
904
905 public int delete(String query) throws HibernateException {
906 return delete( query, ArrayHelper.EMPTY_OBJECT_ARRAY, ArrayHelper.EMPTY_TYPE_ARRAY );
907 }
908
909 public int delete(String query, Object value, Type type) throws HibernateException {
910 return delete( query, new Object[]{value}, new Type[]{type} );
911 }
912
913 public int delete(String query, Object[] values, Type[] types) throws HibernateException {
914 if ( query == null ) {
915 throw new IllegalArgumentException("attempt to perform delete-by-query with null query");
916 }
917
918 if ( log.isTraceEnabled() ) {
919 log.trace( "delete: " + query );
920 if ( values.length != 0 ) {
921 log.trace( "parameters: " + StringHelper.toString( values ) );
922 }
923 }
924
925 List list = find( query, values, types );
926 int deletionCount = list.size();
927 for ( int i = 0; i < deletionCount; i++ ) {
928 delete( list.get( i ) );
929 }
930
931 return deletionCount;
932 }
933
934 public Query createFilter(Object collection, String queryString) {
935 return new CollectionFilterImpl(queryString, collection, this);
936 }
937
938 public Query createQuery(String queryString) {
939 return new QueryImpl(queryString, this);
940 }
941
942 private Query createQuery(String queryString, FlushMode queryFlushMode) {
943 return new QueryImpl(queryString, queryFlushMode, this);
944 }
945
946 public Query getNamedQuery(String queryName) throws MappingException {
947 NamedQueryDefinition nqd = factory.getNamedQuery(queryName);
948 final Query query;
949 if ( nqd != null ) {
950 query = createQuery(
951 nqd.getQueryString(),
952 nqd.getFlushMode()
953 );
954 if ( factory.getSettings().isCommentsEnabled() ) {
955 query.setComment("named query " + queryName);
956 }
957 }
958 else {
959 NamedSQLQueryDefinition nsqlqd = factory.getNamedSQLQuery( queryName );
960 if (nsqlqd==null) {
961 throw new MappingException("Named query not known: " + queryName);
962 }
963 query = new SQLQueryImpl(nsqlqd, this);
964 nqd = nsqlqd;
965 if ( factory.getSettings().isCommentsEnabled() ) {
966 query.setComment("named SQL query " + queryName);
967 }
968 }
969 query.setCacheable( nqd.isCacheable() );
970 query.setCacheRegion( nqd.getCacheRegion() );
971 if ( nqd.getTimeout()!=null ) query.setTimeout( nqd.getTimeout().intValue() );
972 if ( nqd.getFetchSize()!=null ) query.setFetchSize( nqd.getFetchSize().intValue() );
973 return query;
974 }
975
976 public Object instantiate(String entityName, Serializable id) throws HibernateException {
977 return instantiate( factory.getEntityPersister(entityName), id );
978 }
979
980 /**
981 * give the interceptor an opportunity to override the default instantiation
982 */
983 public Object instantiate(EntityPersister persister, Serializable id) throws HibernateException {
984 Object result = interceptor.instantiate( persister.getEntityName(), entityMode, id );
985 if ( result == null ) result = persister.instantiate( id, entityMode );
986 return result;
987 }
988
989 public EntityMode getEntityMode() {
990 return entityMode;
991 }
992
993 public void setFlushMode(FlushMode flushMode) {
994 if ( log.isTraceEnabled() ) log.trace("setting flush mode to: " + flushMode);
995 this.flushMode = flushMode;
996 }
997
998 public FlushMode getFlushMode() {
999 return flushMode;
1000 }
1001
1002 public CacheMode getCacheMode() {
1003 return cacheMode;
1004 }
1005
1006 public void setCacheMode(CacheMode cacheMode) {
1007 if ( log.isTraceEnabled() ) log.trace("setting cache mode to: " + cacheMode);
1008 this.cacheMode= cacheMode;
1009 }
1010
1011 public Transaction beginTransaction() throws HibernateException {
1012 if ( !isRootSession ) {
1013 log.warn("Transaction started on non-root session");
1014 }
1015 Transaction tx = jdbcContext.beginTransaction();
1016 interceptor.afterTransactionBegin(tx);
1017 return tx;
1018 }
1019
1020 public EntityPersister getEntityPersister(final String entityName, final Object object) {
1021 if (entityName==null) {
1022 return factory.getEntityPersister( guessEntityName(object) );
1023 }
1024 else {
1025 return factory.getEntityPersister( entityName ).getSubclassEntityPersister( object, getFactory(), entityMode );
1026 }
1027 }
1028
1029 // not for internal use:
1030 public Serializable getIdentifier(Object object) throws HibernateException {
1031 if ( object instanceof HibernateProxy ) {
1032 LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer();
1033 if ( li.getSession() != this ) {
1034 throw new TransientObjectException( "The proxy was not associated with this session" );
1035 }
1036 return li.getIdentifier();
1037 }
1038 else {
1039 EntityEntry entry = persistenceContext.getEntry(object);
1040 if ( entry == null ) {
1041 throw new TransientObjectException( "The instance was not associated with this session" );
1042 }
1043 return entry.getId();
1044 }
1045 }
1046
1047 /**
1048 * Get the id value for an object that is actually associated with the session. This
1049 * is a bit stricter than getEntityIdentifierIfNotUnsaved().
1050 */
1051 public Serializable getEntityIdentifier(Object object) {
1052 if ( object instanceof HibernateProxy ) {
1053 return getProxyIdentifier(object);
1054 }
1055 else {
1056 EntityEntry entry = persistenceContext.getEntry(object);
1057 return entry != null ? entry.getId() : null;
1058 }
1059 }
1060
1061 private Serializable getProxyIdentifier(Object proxy) {
1062 return ( (HibernateProxy) proxy ).getHibernateLazyInitializer().getIdentifier();
1063 }
1064
1065 public Collection filter(Object collection, String filter) throws HibernateException {
1066 return listFilter( collection, filter, new QueryParameters( new Type[1], new Object[1] ) );
1067 }
1068
1069 public Collection filter(Object collection, String filter, Object value, Type type) throws HibernateException {
1070 return listFilter( collection, filter, new QueryParameters( new Type[]{null, type}, new Object[]{null, value} ) );
1071 }
1072
1073 public Collection filter(Object collection, String filter, Object[] values, Type[] types)
1074 throws HibernateException {
1075 Object[] vals = new Object[values.length + 1];
1076 Type[] typs = new Type[types.length + 1];
1077 System.arraycopy( values, 0, vals, 1, values.length );
1078 System.arraycopy( types, 0, typs, 1, types.length );
1079 return listFilter( collection, filter, new QueryParameters( typs, vals ) );
1080 }
1081
1082 /**
1083 * 1. determine the collection role of the given collection (this may require a flush, if the