KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > orm > hibernate > SessionFactoryUtils


1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.orm.hibernate;
18
19 import java.util.HashMap JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.Map JavaDoc;
22 import java.util.Set JavaDoc;
23
24 import javax.sql.DataSource JavaDoc;
25 import javax.transaction.Status JavaDoc;
26 import javax.transaction.Transaction JavaDoc;
27 import javax.transaction.TransactionManager JavaDoc;
28
29 import net.sf.hibernate.Criteria;
30 import net.sf.hibernate.FlushMode;
31 import net.sf.hibernate.HibernateException;
32 import net.sf.hibernate.Interceptor;
33 import net.sf.hibernate.JDBCException;
34 import net.sf.hibernate.NonUniqueResultException;
35 import net.sf.hibernate.ObjectDeletedException;
36 import net.sf.hibernate.PersistentObjectException;
37 import net.sf.hibernate.Query;
38 import net.sf.hibernate.QueryException;
39 import net.sf.hibernate.Session;
40 import net.sf.hibernate.SessionFactory;
41 import net.sf.hibernate.StaleObjectStateException;
42 import net.sf.hibernate.TransientObjectException;
43 import net.sf.hibernate.UnresolvableObjectException;
44 import net.sf.hibernate.WrongClassException;
45 import net.sf.hibernate.connection.ConnectionProvider;
46 import net.sf.hibernate.engine.SessionFactoryImplementor;
47 import org.apache.commons.logging.Log;
48 import org.apache.commons.logging.LogFactory;
49
50 import org.springframework.core.CollectionFactory;
51 import org.springframework.dao.DataAccessException;
52 import org.springframework.dao.DataAccessResourceFailureException;
53 import org.springframework.dao.IncorrectResultSizeDataAccessException;
54 import org.springframework.dao.InvalidDataAccessApiUsageException;
55 import org.springframework.jdbc.datasource.DataSourceUtils;
56 import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
57 import org.springframework.jdbc.support.SQLExceptionTranslator;
58 import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
59 import org.springframework.transaction.jta.SpringJtaSynchronizationAdapter;
60 import org.springframework.transaction.support.TransactionSynchronizationManager;
61 import org.springframework.util.Assert;
62
63 /**
64  * Helper class featuring methods for Hibernate Session handling,
65  * allowing for reuse of Hibernate Session instances within transactions.
66  * Also provides support for exception translation.
67  *
68  * <p>Supports synchronization with both Spring-managed JTA transactions
69  * (see {@link org.springframework.transaction.jta.JtaTransactionManager})
70  * and non-Spring JTA transactions (i.e. plain JTA or EJB CMT),
71  * transparently providing transaction-scoped Hibernate Sessions.
72  * Note that for non-Spring JTA transactions, a JTA TransactionManagerLookup
73  * has to be specified in the Hibernate configuration.
74  *
75  * <p>Used internally by {@link HibernateTemplate}, {@link HibernateInterceptor}
76  * and {@link HibernateTransactionManager}. Can also be used directly in
77  * application code.
78  *
79  * <p>Note: Spring's Hibernate support in this package requires Hibernate 2.1.
80  * Dedicated Hibernate3 support can be found in a separate package:
81  * <code>org.springframework.orm.hibernate3</code>.
82  *
83  * @author Juergen Hoeller
84  * @since 02.05.2003
85  * @see #getSession
86  * @see #releaseSession
87  * @see HibernateTransactionManager
88  * @see org.springframework.transaction.jta.JtaTransactionManager
89  * @see org.springframework.transaction.support.TransactionSynchronizationManager
90  */

91 public abstract class SessionFactoryUtils {
92
93     /**
94      * Order value for TransactionSynchronization objects that clean up Hibernate Sessions.
95      * Returns <code>DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100</code>
96      * to execute Session cleanup before JDBC Connection cleanup, if any.
97      * @see org.springframework.jdbc.datasource.DataSourceUtils#CONNECTION_SYNCHRONIZATION_ORDER
98      */

99     public static final int SESSION_SYNCHRONIZATION_ORDER =
100             DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100;
101
102     static final Log logger = LogFactory.getLog(SessionFactoryUtils.class);
103
104     private static final ThreadLocal JavaDoc deferredCloseHolder = new ThreadLocal JavaDoc();
105
106
107     /**
108      * Determine the DataSource of the given SessionFactory.
109      * @param sessionFactory the SessionFactory to check
110      * @return the DataSource, or <code>null</code> if none found
111      * @see net.sf.hibernate.engine.SessionFactoryImplementor#getConnectionProvider
112      * @see LocalDataSourceConnectionProvider
113      */

114     public static DataSource JavaDoc getDataSource(SessionFactory sessionFactory) {
115         if (sessionFactory instanceof SessionFactoryImplementor) {
116             ConnectionProvider cp = ((SessionFactoryImplementor) sessionFactory).getConnectionProvider();
117             if (cp instanceof LocalDataSourceConnectionProvider) {
118                 return ((LocalDataSourceConnectionProvider) cp).getDataSource();
119             }
120         }
121         return null;
122     }
123
124     /**
125      * Create an appropriate SQLExceptionTranslator for the given SessionFactory.
126      * If a DataSource is found, a SQLErrorCodeSQLExceptionTranslator for the DataSource
127      * is created; else, a SQLStateSQLExceptionTranslator as fallback.
128      * @param sessionFactory the SessionFactory to create the translator for
129      * @return the SQLExceptionTranslator
130      * @see #getDataSource
131      * @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
132      * @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator
133      */

134     public static SQLExceptionTranslator newJdbcExceptionTranslator(SessionFactory sessionFactory) {
135         DataSource JavaDoc ds = getDataSource(sessionFactory);
136         if (ds != null) {
137             return new SQLErrorCodeSQLExceptionTranslator(ds);
138         }
139         return new SQLStateSQLExceptionTranslator();
140     }
141
142     /**
143      * Try to retrieve the JTA TransactionManager from the given SessionFactory
144      * and/or Session. Check the passed-in SessionFactory for implementing
145      * SessionFactoryImplementor (the usual case), falling back to the
146      * SessionFactory reference that the Session itself carries (for example,
147      * when using Hibernate's JCA Connector, i.e. JCASessionFactoryImpl).
148      * @param sessionFactory Hibernate SessionFactory
149      * @param session Hibernate Session (can also be <code>null</code>)
150      * @return the JTA TransactionManager, if any
151      * @see javax.transaction.TransactionManager
152      * @see SessionFactoryImplementor#getTransactionManager
153      * @see Session#getSessionFactory
154      * @see net.sf.hibernate.impl.SessionFactoryImpl
155      * @see net.sf.hibernate.jca.JCASessionFactoryImpl
156      */

157     public static TransactionManager JavaDoc getJtaTransactionManager(SessionFactory sessionFactory, Session session) {
158         SessionFactoryImplementor sessionFactoryImpl = null;
159         if (sessionFactory instanceof SessionFactoryImplementor) {
160             sessionFactoryImpl = ((SessionFactoryImplementor) sessionFactory);
161         }
162         else if (session != null) {
163             SessionFactory internalFactory = session.getSessionFactory();
164             if (internalFactory instanceof SessionFactoryImplementor) {
165                 sessionFactoryImpl = (SessionFactoryImplementor) internalFactory;
166             }
167         }
168         return (sessionFactoryImpl != null ? sessionFactoryImpl.getTransactionManager() : null);
169     }
170
171
172     /**
173      * Get a Hibernate Session for the given SessionFactory. Is aware of and will
174      * return any existing corresponding Session bound to the current thread, for
175      * example when using {@link HibernateTransactionManager}. Will create a new
176      * Session otherwise, if "allowCreate" is <code>true</code>.
177      * <p>This is the <code>getSession</code> method used by typical data access code,
178      * in combination with <code>releaseSession</code> called when done with
179      * the Session. Note that HibernateTemplate allows to write data access code
180      * without caring about such resource handling.
181      * @param sessionFactory Hibernate SessionFactory to create the session with
182      * @param allowCreate whether a non-transactional Session should be created
183      * when no transactional Session can be found for the current thread
184      * @return the Hibernate Session
185      * @throws DataAccessResourceFailureException if the Session couldn't be created
186      * @throws IllegalStateException if no thread-bound Session found and
187      * "allowCreate" is <code>false</code>
188      * @see #releaseSession
189      * @see HibernateTemplate
190      */

191     public static Session getSession(SessionFactory sessionFactory, boolean allowCreate)
192         throws DataAccessResourceFailureException, IllegalStateException JavaDoc {
193
194         return getSession(sessionFactory, null, null, allowCreate);
195     }
196
197     /**
198      * Get a Hibernate Session for the given SessionFactory. Is aware of and will
199      * return any existing corresponding Session bound to the current thread, for
200      * example when using {@link HibernateTransactionManager}. Will always create
201      * a new Session otherwise.
202      * <p>Supports setting a Session-level Hibernate entity interceptor that allows
203      * to inspect and change property values before writing to and reading from the
204      * database. Such an interceptor can also be set at the SessionFactory level
205      * (i.e. on LocalSessionFactoryBean), on HibernateTransactionManager, or on
206      * HibernateInterceptor/HibernateTemplate.
207      * @param sessionFactory Hibernate SessionFactory to create the session with
208      * @param entityInterceptor Hibernate entity interceptor, or <code>null</code> if none
209      * @param jdbcExceptionTranslator SQLExceptionTranslator to use for flushing the
210      * Session on transaction synchronization (may be <code>null</code>; only used
211      * when actually registering a transaction synchronization)
212      * @return the Hibernate Session
213      * @throws DataAccessResourceFailureException if the Session couldn't be created
214      * @see LocalSessionFactoryBean#setEntityInterceptor
215      * @see HibernateInterceptor#setEntityInterceptor
216      * @see HibernateTemplate#setEntityInterceptor
217      */

218     public static Session getSession(
219             SessionFactory sessionFactory, Interceptor entityInterceptor,
220             SQLExceptionTranslator jdbcExceptionTranslator) throws DataAccessResourceFailureException {
221
222         return getSession(sessionFactory, entityInterceptor, jdbcExceptionTranslator, true);
223     }
224
225     /**
226      * Get a Hibernate Session for the given SessionFactory. Is aware of and will
227      * return any existing corresponding Session bound to the current thread, for
228      * example when using {@link HibernateTransactionManager}. Will create a new
229      * Session otherwise, if "allowCreate" is <code>true</code>.
230      * @param sessionFactory Hibernate SessionFactory to create the session with
231      * @param entityInterceptor Hibernate entity interceptor, or <code>null</code> if none
232      * @param jdbcExceptionTranslator SQLExceptionTranslator to use for flushing the
233      * Session on transaction synchronization (may be <code>null</code>)
234      * @param allowCreate whether a non-transactional Session should be created
235      * when no transactional Session can be found for the current thread
236      * @return the Hibernate Session
237      * @throws DataAccessResourceFailureException if the Session couldn't be created
238      * @throws IllegalStateException if no thread-bound Session found and
239      * "allowCreate" is <code>false</code>
240      */

241     private static Session getSession(
242             SessionFactory sessionFactory, Interceptor entityInterceptor,
243             SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate)
244             throws DataAccessResourceFailureException, IllegalStateException JavaDoc {
245
246         Assert.notNull(sessionFactory, "No SessionFactory specified");
247
248         SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
249         if (sessionHolder != null && !sessionHolder.isEmpty()) {
250             // pre-bound Hibernate Session
251
Session session = null;
252             if (TransactionSynchronizationManager.isSynchronizationActive() &&
253                     sessionHolder.doesNotHoldNonDefaultSession()) {
254                 // Spring transaction management is active ->
255
// register pre-bound Session with it for transactional flushing.
256
session = sessionHolder.getValidatedSession();
257                 if (!sessionHolder.isSynchronizedWithTransaction()) {
258                     logger.debug("Registering Spring transaction synchronization for existing Hibernate Session");
259                     TransactionSynchronizationManager.registerSynchronization(
260                             new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false));
261                     sessionHolder.setSynchronizedWithTransaction(true);
262                     // Switch to FlushMode.AUTO if we're not within a read-only transaction.
263
FlushMode flushMode = session.getFlushMode();
264                     if (FlushMode.NEVER.equals(flushMode) &&
265                             !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
266                         session.setFlushMode(FlushMode.AUTO);
267                         sessionHolder.setPreviousFlushMode(flushMode);
268                     }
269                 }
270             }
271             else {
272                 // No Spring transaction management active -> try JTA transaction synchronization.
273
session = getJtaSynchronizedSession(sessionHolder, sessionFactory, jdbcExceptionTranslator);
274             }
275             if (session != null) {
276                 return session;
277             }
278         }
279
280         try {
281             logger.debug("Opening Hibernate Session");
282             Session session = (entityInterceptor != null ?
283                 sessionFactory.openSession(entityInterceptor) : sessionFactory.openSession());
284
285             // Set Session to FlushMode.NEVER if we're within a read-only transaction.
286
// Use same Session for further Hibernate actions within the transaction.
287
// Thread object will get removed by synchronization at transaction completion.
288
if (TransactionSynchronizationManager.isSynchronizationActive()) {
289                 // We're within a Spring-managed transaction, possibly from JtaTransactionManager.
290
logger.debug("Registering Spring transaction synchronization for new Hibernate Session");
291                 SessionHolder holderToUse = sessionHolder;
292                 if (holderToUse == null) {
293                     holderToUse = new SessionHolder(session);
294                 }
295                 else {
296                     holderToUse.addSession(session);
297                 }
298                 if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
299                     session.setFlushMode(FlushMode.NEVER);
300                 }
301                 TransactionSynchronizationManager.registerSynchronization(
302                         new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true));
303                 holderToUse.setSynchronizedWithTransaction(true);
304                 if (holderToUse != sessionHolder) {
305                     TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse);
306                 }
307             }
308             else {
309                 // No Spring transaction management active -> try JTA transaction synchronization.
310
registerJtaSynchronization(session, sessionFactory, jdbcExceptionTranslator, sessionHolder);
311             }
312
313             // Check whether we are allowed to return the Session.
314
if (!allowCreate && !isSessionTransactional(session, sessionFactory)) {
315                 closeSession(session);
316                 throw new IllegalStateException JavaDoc("No Hibernate Session bound to thread, " +
317                         "and configuration does not allow creation of non-transactional one here");
318             }
319
320             return session;
321         }
322         catch (HibernateException ex) {
323             throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
324         }
325     }
326
327     /**
328      * Retrieve a Session from the given SessionHolder, potentially from a
329      * JTA transaction synchronization.
330      * @param sessionHolder the SessionHolder to check
331      * @param sessionFactory the SessionFactory to get the JTA TransactionManager from
332      * @param jdbcExceptionTranslator SQLExceptionTranslator to use for flushing the
333      * Session on transaction synchronization (may be <code>null</code>)
334      * @return the associated Session, if any
335      * @throws DataAccessResourceFailureException if the Session couldn't be created
336      */

337     private static Session getJtaSynchronizedSession(
338         SessionHolder sessionHolder, SessionFactory sessionFactory,
339         SQLExceptionTranslator jdbcExceptionTranslator) throws DataAccessResourceFailureException {
340
341         // JTA synchronization is only possible with a javax.transaction.TransactionManager.
342
// We'll check the Hibernate SessionFactory: If a TransactionManagerLookup is specified
343
// in Hibernate configuration, it will contain a TransactionManager reference.
344
TransactionManager JavaDoc jtaTm = getJtaTransactionManager(sessionFactory, sessionHolder.getAnySession());
345         if (jtaTm != null) {
346             // Check whether JTA transaction management is active ->
347
// fetch pre-bound Session for the current JTA transaction, if any.
348
// (just necessary for JTA transaction suspension, with an individual
349
// Hibernate Session per currently active/suspended transaction)
350
try {
351                 // Look for transaction-specific Session.
352
Transaction JavaDoc jtaTx = jtaTm.getTransaction();
353                 if (jtaTx != null) {
354                     int jtaStatus = jtaTx.getStatus();
355                     if (jtaStatus == Status.STATUS_ACTIVE || jtaStatus == Status.STATUS_MARKED_ROLLBACK) {
356                         Session session = sessionHolder.getValidatedSession(jtaTx);
357                         if (session == null && !sessionHolder.isSynchronizedWithTransaction()) {
358                             // No transaction-specific Session found: If not already marked as
359
// synchronized with transaction, register the default thread-bound
360
// Session as JTA-transactional. If there is no default Session,
361
// we're a new inner JTA transaction with an outer one being suspended:
362
// In that case, we'll return null to trigger opening of a new Session.
363
session = sessionHolder.getValidatedSession();
364                             if (session != null) {
365                                 logger.debug("Registering JTA transaction synchronization for existing Hibernate Session");
366                                 sessionHolder.addSession(jtaTx, session);
367                                 jtaTx.registerSynchronization(
368                                         new SpringJtaSynchronizationAdapter(
369                                                 new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false),
370                                                 jtaTm));
371                                 sessionHolder.setSynchronizedWithTransaction(true);
372                                 // Switch to FlushMode.AUTO if we're not within a read-only transaction.
373
FlushMode flushMode = session.getFlushMode();
374                                 if (FlushMode.NEVER.equals(flushMode)) {
375                                     session.setFlushMode(FlushMode.AUTO);
376                                     sessionHolder.setPreviousFlushMode(flushMode);
377                                 }
378                             }
379                         }
380                         return session;
381                     }
382                 }
383                 // No transaction active -> simply return default thread-bound Session, if any
384
// (possibly from OpenSessionInViewFilter/Interceptor).
385
return sessionHolder.getValidatedSession();
386             }
387             catch (Throwable JavaDoc ex) {
388                 throw new DataAccessResourceFailureException("Could not check JTA transaction", ex);
389             }
390         }
391         else {
392             // No JTA TransactionManager -> simply return default thread-bound Session, if any
393
// (possibly from OpenSessionInViewFilter/Interceptor).
394
return sessionHolder.getValidatedSession();
395         }
396     }
397
398     /**
399      * Register a JTA synchronization for the given Session, if any.
400      * @param sessionHolder the existing thread-bound SessionHolder, if any
401      * @param session the Session to register
402      * @param sessionFactory the SessionFactory that the Session was created with
403      * @param jdbcExceptionTranslator SQLExcepionTranslator to use for flushing the
404      * Session on transaction synchronization (may be <code>null</code>)
405      */

406     private static void registerJtaSynchronization(Session session, SessionFactory sessionFactory,
407             SQLExceptionTranslator jdbcExceptionTranslator, SessionHolder sessionHolder) {
408
409         // JTA synchronization is only possible with a javax.transaction.TransactionManager.
410
// We'll check the Hibernate SessionFactory: If a TransactionManagerLookup is specified
411
// in Hibernate configuration, it will contain a TransactionManager reference.
412
TransactionManager JavaDoc jtaTm = getJtaTransactionManager(sessionFactory, session);
413         if (jtaTm != null) {
414             try {
415                 Transaction JavaDoc jtaTx = jtaTm.getTransaction();
416                 if (jtaTx != null) {
417                     int jtaStatus = jtaTx.getStatus();
418                     if (jtaStatus == Status.STATUS_ACTIVE || jtaStatus == Status.STATUS_MARKED_ROLLBACK) {
419                         logger.debug("Registering JTA transaction synchronization for new Hibernate Session");
420                         SessionHolder holderToUse = sessionHolder;
421                         // Register JTA Transaction with existing SessionHolder.
422
// Create a new SessionHolder if none existed before.
423
if (holderToUse == null) {
424                             holderToUse = new SessionHolder(jtaTx, session);
425                         }
426                         else {
427                             holderToUse.addSession(jtaTx, session);
428                         }
429                         jtaTx.registerSynchronization(
430                                 new SpringJtaSynchronizationAdapter(
431                                         new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true),
432                                         jtaTm));
433                         holderToUse.setSynchronizedWithTransaction(true);
434                         if (holderToUse != sessionHolder) {
435                             TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse);
436                         }
437                     }
438                 }
439             }
440             catch (Throwable JavaDoc ex) {
441                 throw new DataAccessResourceFailureException(
442                         "Could not register synchronization with JTA TransactionManager", ex);
443             }
444         }
445     }
446
447
448     /**
449      * Get a new Hibernate Session from the given SessionFactory.
450      * Will return a new Session even if there already is a pre-bound
451      * Session for the given SessionFactory.
452      * <p>Within a transaction, this method will create a new Session
453      * that shares the transaction's JDBC Connection. More specifically,
454      * it will use the same JDBC Connection as the pre-bound Hibernate Session.
455      * @param sessionFactory Hibernate SessionFactory to create the session with
456      * @return the new Session
457      */

458     public static Session getNewSession(SessionFactory sessionFactory) {
459         return getNewSession(sessionFactory, null);
460     }
461
462     /**
463      * Get a new Hibernate Session from the given SessionFactory.
464      * Will return a new Session even if there already is a pre-bound
465      * Session for the given SessionFactory.
466      * <p>Within a transaction, this method will create a new Session
467      * that shares the transaction's JDBC Connection. More specifically,
468      * it will use the same JDBC Connection as the pre-bound Hibernate Session.
469      * @param sessionFactory Hibernate SessionFactory to create the session with
470      * @param entityInterceptor Hibernate entity interceptor, or <code>null</code> if none
471      * @return the new Session
472      */

473     public static Session getNewSession(SessionFactory sessionFactory, Interceptor entityInterceptor) {
474         Assert.notNull(sessionFactory, "No SessionFactory specified");
475
476         try {
477             SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
478             if (sessionHolder != null && !sessionHolder.isEmpty()) {
479                 if (entityInterceptor != null) {
480                     return sessionFactory.openSession(sessionHolder.getAnySession().connection(), entityInterceptor);
481                 }
482                 else {
483                     return sessionFactory.openSession(sessionHolder.getAnySession().connection());
484                 }
485             }
486             else {
487                 if (entityInterceptor != null) {
488                     return sessionFactory.openSession(entityInterceptor);
489                 }
490                 else {
491                     return sessionFactory.openSession();
492                 }
493             }
494         }
495         catch (HibernateException ex) {
496             throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
497         }
498     }
499
500
501     /**
502      * Return whether the given Hibernate Session is transactional, that is,
503      * bound to the current thread by Spring's transaction facilities.
504      * @param session the Hibernate Session to check
505      * @param sessionFactory Hibernate SessionFactory that the Session was created with
506      * (may be <code>null</code>)
507      * @return whether the Session is transactional
508      */

509     public static boolean isSessionTransactional(Session session, SessionFactory sessionFactory) {
510         if (sessionFactory == null) {
511             return false;
512         }
513         SessionHolder sessionHolder =
514                 (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
515         return (sessionHolder != null && sessionHolder.containsSession(session));
516     }
517
518     /**
519      * Apply the current transaction timeout, if any, to the given
520      * Hibernate Query object.
521      * @param query the Hibernate Query object
522      * @param sessionFactory Hibernate SessionFactory that the Query was created for
523      * (may be <code>null</code>)
524      * @see net.sf.hibernate.Query#setTimeout
525      */

526     public static void applyTransactionTimeout(Query query, SessionFactory sessionFactory) {
527         Assert.notNull(query, "No Query object specified");
528         if (sessionFactory != null) {
529             SessionHolder sessionHolder =
530                     (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
531             if (sessionHolder != null && sessionHolder.hasTimeout()) {
532                 query.setTimeout(sessionHolder.getTimeToLiveInSeconds());
533             }
534         }
535     }
536
537     /**
538      * Apply the current transaction timeout, if any, to the given
539      * Hibernate Criteria object.
540      * @param criteria the Hibernate Criteria object
541      * @param sessionFactory Hibernate SessionFactory that the Criteria was created for
542      * @see net.sf.hibernate.Criteria#setTimeout
543      */

544     public static void applyTransactionTimeout(Criteria criteria, SessionFactory sessionFactory) {
545         Assert.notNull(criteria, "No Criteria object specified");
546         SessionHolder sessionHolder =
547             (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
548         if (sessionHolder != null && sessionHolder.hasTimeout()) {
549             criteria.setTimeout(sessionHolder.getTimeToLiveInSeconds());
550         }
551     }
552
553     /**
554      * Convert the given HibernateException to an appropriate exception
555      * from the <code>org.springframework.dao</code> hierarchy.
556      * <p>Note that it is advisable to handle {@link net.sf.hibernate.JDBCException}
557      * specifically by using a {@link org.springframework.jdbc.support.SQLExceptionTranslator}
558      * for the underlying SQLException.
559      * @param ex HibernateException that occured
560      * @return the corresponding DataAccessException instance
561      * @see HibernateAccessor#convertHibernateAccessException
562      * @see HibernateTransactionManager#convertHibernateAccessException
563      */

564     public static DataAccessException convertHibernateAccessException(HibernateException ex) {
565         if (ex instanceof JDBCException) {
566             // SQLException during Hibernate access: only passed in here from custom code,
567
// as HibernateTemplate etc will use SQLExceptionTranslator-based handling.
568
return new HibernateJdbcException((JDBCException) ex);
569         }
570
571         if (ex instanceof PersistentObjectException) {
572             return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
573         }
574         if (ex instanceof TransientObjectException) {
575             return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
576         }
577         if (ex instanceof ObjectDeletedException) {
578             return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
579         }
580         if (ex instanceof QueryException) {
581             return new HibernateQueryException((QueryException) ex);
582         }
583         if (ex instanceof UnresolvableObjectException) {
584             return new HibernateObjectRetrievalFailureException((UnresolvableObjectException) ex);
585         }
586         if (ex instanceof WrongClassException) {
587             return new HibernateObjectRetrievalFailureException((WrongClassException) ex);
588         }
589         if (ex instanceof NonUniqueResultException) {
590             return new IncorrectResultSizeDataAccessException(ex.getMessage(), 1);
591         }
592         if (ex instanceof StaleObjectStateException) {
593             return new HibernateOptimisticLockingFailureException((StaleObjectStateException) ex);
594         }
595
596         // fallback
597
return new HibernateSystemException(ex);
598     }
599
600
601     /**
602      * Determine whether deferred close is active for the current thread
603      * and the given SessionFactory.
604      * @param sessionFactory the Hibernate SessionFactory to check
605      * @return whether deferred close is active
606      */

607     public static boolean isDeferredCloseActive(SessionFactory sessionFactory) {
608         Assert.notNull(sessionFactory, "No SessionFactory specified");
609         Map JavaDoc holderMap = (Map JavaDoc) deferredCloseHolder.get();
610         return (holderMap != null && holderMap.containsKey(sessionFactory));
611     }
612
613     /**
614      * Initialize deferred close for the current thread and the given SessionFactory.
615      * Sessions will not be actually closed on close calls then, but rather at a
616      * processDeferredClose call at a finishing point (like request completion).
617      * <p>Used by OpenSessionInViewFilter and OpenSessionInViewInterceptor
618      * when not configured for a single session.
619      * @param sessionFactory Hibernate SessionFactory
620      * @see #processDeferredClose
621      * @see #releaseSession
622      * @see org.springframework.orm.hibernate.support.OpenSessionInViewFilter#setSingleSession
623      * @see org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor#setSingleSession
624      */

625     public static void initDeferredClose(SessionFactory sessionFactory) {
626         Assert.notNull(sessionFactory, "No SessionFactory specified");
627         logger.debug("Initializing deferred close of Hibernate Sessions");
628         Map JavaDoc holderMap = (Map JavaDoc) deferredCloseHolder.get();
629         if (holderMap == null) {
630             holderMap = new HashMap JavaDoc();
631             deferredCloseHolder.set(holderMap);
632         }
633         holderMap.put(sessionFactory, CollectionFactory.createLinkedSetIfPossible(4));
634     }
635
636     /**
637      * Process Sessions that have been registered for deferred close
638      * for the given SessionFactory.
639      * @param sessionFactory Hibernate SessionFactory
640      * @see #initDeferredClose
641      * @see #releaseSession
642      */

643     public static void processDeferredClose(SessionFactory sessionFactory) {
644         Assert.notNull(sessionFactory, "No SessionFactory specified");
645
646         Map JavaDoc holderMap = (Map JavaDoc) deferredCloseHolder.get();
647         if (holderMap == null || !holderMap.containsKey(sessionFactory)) {
648             throw new IllegalStateException JavaDoc("Deferred close not active for SessionFactory [" + sessionFactory + "]");
649         }
650
651         logger.debug("Processing deferred close of Hibernate Sessions");
652         Set JavaDoc sessions = (Set JavaDoc) holderMap.remove(sessionFactory);
653         for (Iterator JavaDoc it = sessions.iterator(); it.hasNext();) {
654             closeSession((Session) it.next());
655         }
656
657         if (holderMap.isEmpty()) {
658             deferredCloseHolder.set(null);
659         }
660     }
661
662     /**
663      * Close the given Session, created via the given factory,
664      * if it is not managed externally (i.e. not bound to the thread).
665      * @param session the Hibernate Session to close (may be <code>null</code>)
666      * @param sessionFactory Hibernate SessionFactory that the Session was created with
667      * (may be <code>null</code>)
668      */

669     public static void releaseSession(Session session, SessionFactory sessionFactory) {
670         if (session == null) {
671             return;
672         }
673         // Only close non-transactional Sessions.
674
if (!isSessionTransactional(session, sessionFactory)) {
675             closeSessionOrRegisterDeferredClose(session, sessionFactory);
676         }
677     }
678
679     /**
680      * Close the given Session or register it for deferred close.
681      * @param session the Hibernate Session to close
682      * @param sessionFactory Hibernate SessionFactory that the Session was created with
683      * (may be <code>null</code>)
684      * @see #initDeferredClose
685      * @see #processDeferredClose
686      */

687     static void closeSessionOrRegisterDeferredClose(Session session, SessionFactory sessionFactory) {
688         Map JavaDoc holderMap = (Map JavaDoc) deferredCloseHolder.get();
689         if (holderMap != null && sessionFactory != null && holderMap.containsKey(sessionFactory)) {
690             logger.debug("Registering Hibernate Session for deferred close");
691             // Switch Session to FlushMode.NEVER for remaining lifetime.
692
session.setFlushMode(FlushMode.NEVER);
693             Set JavaDoc sessions = (Set JavaDoc) holderMap.get(sessionFactory);
694             sessions.add(session);
695         }
696         else {
697             closeSession(session);
698         }
699     }
700
701     /**
702      * Perform actual closing of the Hibernate Session,
703      * catching and logging any cleanup exceptions thrown.
704      * @param session the Hibernate Session to close (may be <code>null</code>)
705      * @see net.sf.hibernate.Session#close()
706      */

707     public static void closeSession(Session session) {
708         if (session != null) {
709             logger.debug("Closing Hibernate Session");
710             try {
711                 session.close();
712             }
713             catch (HibernateException ex) {
714                 logger.debug("Could not close Hibernate Session", ex);
715             }
716             catch (Throwable JavaDoc ex) {
717                 logger.debug("Unexpected exception on closing Hibernate Session", ex);
718             }
719         }
720     }
721
722 }
723
Popular Tags