KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > orm > hibernate3 > 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.hibernate3;
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 org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.hibernate.Criteria;
32 import org.hibernate.FlushMode;
33 import org.hibernate.HibernateException;
34 import org.hibernate.Interceptor;
35 import org.hibernate.JDBCException;
36 import org.hibernate.NonUniqueResultException;
37 import org.hibernate.ObjectDeletedException;
38 import org.hibernate.PersistentObjectException;
39 import org.hibernate.PropertyValueException;
40 import org.hibernate.Query;
41 import org.hibernate.QueryException;
42 import org.hibernate.Session;
43 import org.hibernate.SessionFactory;
44 import org.hibernate.StaleObjectStateException;
45 import org.hibernate.StaleStateException;
46 import org.hibernate.TransientObjectException;
47 import org.hibernate.UnresolvableObjectException;
48 import org.hibernate.WrongClassException;
49 import org.hibernate.connection.ConnectionProvider;
50 import org.hibernate.engine.SessionFactoryImplementor;
51 import org.hibernate.exception.ConstraintViolationException;
52 import org.hibernate.exception.DataException;
53 import org.hibernate.exception.JDBCConnectionException;
54 import org.hibernate.exception.LockAcquisitionException;
55 import org.hibernate.exception.SQLGrammarException;
56
57 import org.springframework.core.CollectionFactory;
58 import org.springframework.dao.CannotAcquireLockException;
59 import org.springframework.dao.DataAccessException;
60 import org.springframework.dao.DataAccessResourceFailureException;
61 import org.springframework.dao.DataIntegrityViolationException;
62 import org.springframework.dao.IncorrectResultSizeDataAccessException;
63 import org.springframework.dao.InvalidDataAccessApiUsageException;
64 import org.springframework.dao.InvalidDataAccessResourceUsageException;
65 import org.springframework.jdbc.datasource.DataSourceUtils;
66 import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
67 import org.springframework.jdbc.support.SQLExceptionTranslator;
68 import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
69 import org.springframework.transaction.jta.SpringJtaSynchronizationAdapter;
70 import org.springframework.transaction.support.TransactionSynchronizationManager;
71 import org.springframework.util.Assert;
72
73 /**
74  * Helper class featuring methods for Hibernate Session handling,
75  * allowing for reuse of Hibernate Session instances within transactions.
76  * Also provides support for exception translation.
77  *
78  * <p>Supports synchronization with both Spring-managed JTA transactions
79  * (see {@link org.springframework.transaction.jta.JtaTransactionManager})
80  * and non-Spring JTA transactions (i.e. plain JTA or EJB CMT),
81  * transparently providing transaction-scoped Hibernate Sessions.
82  * Note that for non-Spring JTA transactions, a JTA TransactionManagerLookup
83  * has to be specified in the Hibernate configuration.
84  *
85  * <p>Used internally by {@link HibernateTemplate}, {@link HibernateInterceptor}
86  * and {@link HibernateTransactionManager}. Can also be used directly in
87  * application code.
88  *
89  * @author Juergen Hoeller
90  * @since 1.2
91  * @see #getSession
92  * @see #releaseSession
93  * @see HibernateTransactionManager
94  * @see org.springframework.transaction.jta.JtaTransactionManager
95  * @see org.springframework.transaction.support.TransactionSynchronizationManager
96  */

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

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

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

140     public static SQLExceptionTranslator newJdbcExceptionTranslator(SessionFactory sessionFactory) {
141         DataSource JavaDoc ds = getDataSource(sessionFactory);
142         if (ds != null) {
143             return new SQLErrorCodeSQLExceptionTranslator(ds);
144         }
145         return new SQLStateSQLExceptionTranslator();
146     }
147
148     /**
149      * Try to retrieve the JTA TransactionManager from the given SessionFactory
150      * and/or Session. Check the passed-in SessionFactory for implementing
151      * SessionFactoryImplementor (the usual case), falling back to the
152      * SessionFactory reference that the Session itself carries.
153      * @param sessionFactory Hibernate SessionFactory
154      * @param session Hibernate Session (can also be <code>null</code>)
155      * @return the JTA TransactionManager, if any
156      * @see javax.transaction.TransactionManager
157      * @see SessionFactoryImplementor#getTransactionManager
158      * @see Session#getSessionFactory
159      * @see org.hibernate.impl.SessionFactoryImpl
160      */

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

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

228     public static Session getSession(
229             SessionFactory sessionFactory, Interceptor entityInterceptor,
230             SQLExceptionTranslator jdbcExceptionTranslator) throws DataAccessResourceFailureException {
231
232         try {
233             return doGetSession(sessionFactory, entityInterceptor, jdbcExceptionTranslator, true);
234         }
235         catch (HibernateException ex) {
236             throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
237         }
238     }
239
240     /**
241      * Get a Hibernate Session for the given SessionFactory. Is aware of and will
242      * return any existing corresponding Session bound to the current thread, for
243      * example when using {@link HibernateTransactionManager}. Will create a new
244      * Session otherwise, if "allowCreate" is <code>true</code>.
245      * <p>Throws the original HibernateException, in contrast to {@link #getSession}.
246      * @param sessionFactory Hibernate SessionFactory to create the session with
247      * @param allowCreate whether a non-transactional Session should be created
248      * when no transactional Session can be found for the current thread
249      * @return the Hibernate Session
250      * @throws HibernateException if the Session couldn't be created
251      * @throws IllegalStateException if no thread-bound Session found and allowCreate false
252      */

253     public static Session doGetSession(SessionFactory sessionFactory, boolean allowCreate)
254         throws HibernateException, IllegalStateException JavaDoc {
255
256         return doGetSession(sessionFactory, null, null, allowCreate);
257     }
258
259     /**
260      * Get a Hibernate Session for the given SessionFactory. Is aware of and will
261      * return any existing corresponding Session bound to the current thread, for
262      * example when using {@link HibernateTransactionManager}. Will create a new
263      * Session otherwise, if "allowCreate" is <code>true</code>.
264      * <p>Same as {@link #getSession}, but throwing the original HibernateException.
265      * @param sessionFactory Hibernate SessionFactory to create the session with
266      * @param entityInterceptor Hibernate entity interceptor, or <code>null</code> if none
267      * @param jdbcExceptionTranslator SQLExcepionTranslator to use for flushing the
268      * Session on transaction synchronization (may be <code>null</code>)
269      * @param allowCreate whether a non-transactional Session should be created
270      * when no transactional Session can be found for the current thread
271      * @return the Hibernate Session
272      * @throws HibernateException if the Session couldn't be created
273      * @throws IllegalStateException if no thread-bound Session found and
274      * "allowCreate" is <code>false</code>
275      */

276     private static Session doGetSession(
277             SessionFactory sessionFactory, Interceptor entityInterceptor,
278             SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate)
279             throws HibernateException, IllegalStateException JavaDoc {
280
281         Assert.notNull(sessionFactory, "No SessionFactory specified");
282
283         SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
284         if (sessionHolder != null && !sessionHolder.isEmpty()) {
285             // pre-bound Hibernate Session
286
Session session = null;
287             if (TransactionSynchronizationManager.isSynchronizationActive() &&
288                     sessionHolder.doesNotHoldNonDefaultSession()) {
289                 // Spring transaction management is active ->
290
// register pre-bound Session with it for transactional flushing.
291
session = sessionHolder.getValidatedSession();
292                 if (session != null && !sessionHolder.isSynchronizedWithTransaction()) {
293                     logger.debug("Registering Spring transaction synchronization for existing Hibernate Session");
294                     TransactionSynchronizationManager.registerSynchronization(
295                             new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false));
296                     sessionHolder.setSynchronizedWithTransaction(true);
297                     // Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
298
// with FlushMode.NEVER, which needs to allow flushing within the transaction.
299
FlushMode flushMode = session.getFlushMode();
300                     if (flushMode.lessThan(FlushMode.COMMIT) &&
301                             !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
302                         session.setFlushMode(FlushMode.AUTO);
303                         sessionHolder.setPreviousFlushMode(flushMode);
304                     }
305                 }
306             }
307             else {
308                 // No Spring transaction management active -> try JTA transaction synchronization.
309
session = getJtaSynchronizedSession(sessionHolder, sessionFactory, jdbcExceptionTranslator);
310             }
311             if (session != null) {
312                 return session;
313             }
314         }
315
316         logger.debug("Opening Hibernate Session");
317         Session session = (entityInterceptor != null ?
318                 sessionFactory.openSession(entityInterceptor) : sessionFactory.openSession());
319
320         // Use same Session for further Hibernate actions within the transaction.
321
// Thread object will get removed by synchronization at transaction completion.
322
if (TransactionSynchronizationManager.isSynchronizationActive()) {
323             // We're within a Spring-managed transaction, possibly from JtaTransactionManager.
324
logger.debug("Registering Spring transaction synchronization for new Hibernate Session");
325             SessionHolder holderToUse = sessionHolder;
326             if (holderToUse == null) {
327                 holderToUse = new SessionHolder(session);
328             }
329             else {
330                 holderToUse.addSession(session);
331             }
332             if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
333                 session.setFlushMode(FlushMode.NEVER);
334             }
335             TransactionSynchronizationManager.registerSynchronization(
336                     new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true));
337             holderToUse.setSynchronizedWithTransaction(true);
338             if (holderToUse != sessionHolder) {
339                 TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse);
340             }
341         }
342         else {
343             // No Spring transaction management active -> try JTA transaction synchronization.
344
registerJtaSynchronization(session, sessionFactory, jdbcExceptionTranslator, sessionHolder);
345         }
346
347         // Check whether we are allowed to return the Session.
348
if (!allowCreate && !isSessionTransactional(session, sessionFactory)) {
349             closeSession(session);
350             throw new IllegalStateException JavaDoc("No Hibernate Session bound to thread, " +
351                 "and configuration does not allow creation of non-transactional one here");
352         }
353
354         return session;
355     }
356
357     /**
358      * Retrieve a Session from the given SessionHolder, potentially from a
359      * JTA transaction synchronization.
360      * @param sessionHolder the SessionHolder to check
361      * @param sessionFactory the SessionFactory to get the JTA TransactionManager from
362      * @param jdbcExceptionTranslator SQLExcepionTranslator to use for flushing the
363      * Session on transaction synchronization (may be <code>null</code>)
364      * @return the associated Session, if any
365      * @throws DataAccessResourceFailureException if the Session couldn't be created
366      */

367     private static Session getJtaSynchronizedSession(
368         SessionHolder sessionHolder, SessionFactory sessionFactory,
369         SQLExceptionTranslator jdbcExceptionTranslator) throws DataAccessResourceFailureException {
370
371         // JTA synchronization is only possible with a javax.transaction.TransactionManager.
372
// We'll check the Hibernate SessionFactory: If a TransactionManagerLookup is specified
373
// in Hibernate configuration, it will contain a TransactionManager reference.
374
TransactionManager JavaDoc jtaTm = getJtaTransactionManager(sessionFactory, sessionHolder.getAnySession());
375         if (jtaTm != null) {
376             // Check whether JTA transaction management is active ->
377
// fetch pre-bound Session for the current JTA transaction, if any.
378
// (just necessary for JTA transaction suspension, with an individual
379
// Hibernate Session per currently active/suspended transaction)
380
try {
381                 // Look for transaction-specific Session.
382
Transaction JavaDoc jtaTx = jtaTm.getTransaction();
383                 if (jtaTx != null) {
384                     int jtaStatus = jtaTx.getStatus();
385                     if (jtaStatus == Status.STATUS_ACTIVE || jtaStatus == Status.STATUS_MARKED_ROLLBACK) {
386                         Session session = sessionHolder.getValidatedSession(jtaTx);
387                         if (session == null && !sessionHolder.isSynchronizedWithTransaction()) {
388                             // No transaction-specific Session found: If not already marked as
389
// synchronized with transaction, register the default thread-bound
390
// Session as JTA-transactional. If there is no default Session,
391
// we're a new inner JTA transaction with an outer one being suspended:
392
// In that case, we'll return null to trigger opening of a new Session.
393
session = sessionHolder.getValidatedSession();
394                             if (session != null) {
395                                 logger.debug("Registering JTA transaction synchronization for existing Hibernate Session");
396                                 sessionHolder.addSession(jtaTx, session);
397                                 jtaTx.registerSynchronization(
398                                         new SpringJtaSynchronizationAdapter(
399                                                 new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false),
400                                                 jtaTm));
401                                 sessionHolder.setSynchronizedWithTransaction(true);
402                                 // Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
403
// with FlushMode.NEVER, which needs to allow flushing within the transaction.
404
FlushMode flushMode = session.getFlushMode();
405                                 if (flushMode.lessThan(FlushMode.COMMIT)) {
406                                     session.setFlushMode(FlushMode.AUTO);
407                                     sessionHolder.setPreviousFlushMode(flushMode);
408                                 }
409                             }
410                         }
411                         return session;
412                     }
413                 }
414                 // No transaction active -> simply return default thread-bound Session, if any
415
// (possibly from OpenSessionInViewFilter/Interceptor).
416
return sessionHolder.getValidatedSession();
417             }
418             catch (Throwable JavaDoc ex) {
419                 throw new DataAccessResourceFailureException("Could not check JTA transaction", ex);
420             }
421         }
422         else {
423             // No JTA TransactionManager -> simply return default thread-bound Session, if any
424
// (possibly from OpenSessionInViewFilter/Interceptor).
425
return sessionHolder.getValidatedSession();
426         }
427     }
428
429     /**
430      * Register a JTA synchronization for the given Session, if any.
431      * @param sessionHolder the existing thread-bound SessionHolder, if any
432      * @param session the Session to register
433      * @param sessionFactory the SessionFactory that the Session was created with
434      * @param jdbcExceptionTranslator SQLExcepionTranslator to use for flushing the
435      * Session on transaction synchronization (may be <code>null</code>)
436      */

437     private static void registerJtaSynchronization(Session session, SessionFactory sessionFactory,
438             SQLExceptionTranslator jdbcExceptionTranslator, SessionHolder sessionHolder) {
439
440         // JTA synchronization is only possible with a javax.transaction.TransactionManager.
441
// We'll check the Hibernate SessionFactory: If a TransactionManagerLookup is specified
442
// in Hibernate configuration, it will contain a TransactionManager reference.
443
TransactionManager JavaDoc jtaTm = getJtaTransactionManager(sessionFactory, session);
444         if (jtaTm != null) {
445             try {
446                 Transaction JavaDoc jtaTx = jtaTm.getTransaction();
447                 if (jtaTx != null) {
448                     int jtaStatus = jtaTx.getStatus();
449                     if (jtaStatus == Status.STATUS_ACTIVE || jtaStatus == Status.STATUS_MARKED_ROLLBACK) {
450                         logger.debug("Registering JTA transaction synchronization for new Hibernate Session");
451                         SessionHolder holderToUse = sessionHolder;
452                         // Register JTA Transaction with existing SessionHolder.
453
// Create a new SessionHolder if none existed before.
454
if (holderToUse == null) {
455                             holderToUse = new SessionHolder(jtaTx, session);
456                         }
457                         else {
458                             holderToUse.addSession(jtaTx, session);
459                         }
460                         jtaTx.registerSynchronization(
461                                 new SpringJtaSynchronizationAdapter(
462                                         new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true),
463                                         jtaTm));
464                         holderToUse.setSynchronizedWithTransaction(true);
465                         if (holderToUse != sessionHolder) {
466                             TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse);
467                         }
468                     }
469                 }
470             }
471             catch (Throwable JavaDoc ex) {
472                 throw new DataAccessResourceFailureException(
473                         "Could not register synchronization with JTA TransactionManager", ex);
474             }
475         }
476     }
477
478
479     /**
480      * Get a new Hibernate Session from the given SessionFactory.
481      * Will return a new Session even if there already is a pre-bound
482      * Session for the given SessionFactory.
483      * <p>Within a transaction, this method will create a new Session
484      * that shares the transaction's JDBC Connection. More specifically,
485      * it will use the same JDBC Connection as the pre-bound Hibernate Session.
486      * @param sessionFactory Hibernate SessionFactory to create the session with
487      * @return the new Session
488      */

489     public static Session getNewSession(SessionFactory sessionFactory) {
490         return getNewSession(sessionFactory, null);
491     }
492
493     /**
494      * Get a new Hibernate Session from the given SessionFactory.
495      * Will return a new Session even if there already is a pre-bound
496      * Session for the given SessionFactory.
497      * <p>Within a transaction, this method will create a new Session
498      * that shares the transaction's JDBC Connection. More specifically,
499      * it will use the same JDBC Connection as the pre-bound Hibernate Session.
500      * @param sessionFactory Hibernate SessionFactory to create the session with
501      * @param entityInterceptor Hibernate entity interceptor, or <code>null</code> if none
502      * @return the new Session
503      */

504     public static Session getNewSession(SessionFactory sessionFactory, Interceptor entityInterceptor) {
505         Assert.notNull(sessionFactory, "No SessionFactory specified");
506
507         try {
508             SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
509             if (sessionHolder != null && !sessionHolder.isEmpty()) {
510                 if (entityInterceptor != null) {
511                     return sessionFactory.openSession(sessionHolder.getAnySession().connection(), entityInterceptor);
512                 }
513                 else {
514                     return sessionFactory.openSession(sessionHolder.getAnySession().connection());
515                 }
516             }
517             else {
518                 if (entityInterceptor != null) {
519                     return sessionFactory.openSession(entityInterceptor);
520                 }
521                 else {
522                     return sessionFactory.openSession();
523                 }
524             }
525         }
526         catch (HibernateException ex) {
527             throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
528         }
529     }
530
531
532     /**
533      * Stringify the given Session for debug logging.
534      * Returns output equivalent to <code>Object.toString()</code>:
535      * the fully qualified class name + "@" + the identity hash code.
536      * <p>The only reason why this is necessary is because Hibernate3's
537      * <code>Session.toString()</code> implementation is broken (and won't be fixed):
538      * it logs the toString representation of all persistent objects in the Session,
539      * which might lead to ConcurrentModificationExceptions if the persistent objects
540      * in turn refer to the Session (for example, for lazy loading).
541      * @param session the Hibernate Session to stringify
542      * @return the String representation of the given Session
543      */

544     public static String JavaDoc toString(Session session) {
545         return session.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(session));
546     }
547
548     /**
549      * Return whether the given Hibernate Session is transactional, that is,
550      * bound to the current thread by Spring's transaction facilities.
551      * @param session the Hibernate Session to check
552      * @param sessionFactory Hibernate SessionFactory that the Session was created with
553      * (may be <code>null</code>)
554      * @return whether the Session is transactional
555      */

556     public static boolean isSessionTransactional(Session session, SessionFactory sessionFactory) {
557         if (sessionFactory == null) {
558             return false;
559         }
560         SessionHolder sessionHolder =
561                 (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
562         return (sessionHolder != null && sessionHolder.containsSession(session));
563     }
564
565     /**
566      * Apply the current transaction timeout, if any, to the given
567      * Hibernate Query object.
568      * @param query the Hibernate Query object
569      * @param sessionFactory Hibernate SessionFactory that the Query was created for
570      * (may be <code>null</code>)
571      * @see org.hibernate.Query#setTimeout
572      */

573     public static void applyTransactionTimeout(Query query, SessionFactory sessionFactory) {
574         Assert.notNull(query, "No Query object specified");
575         if (sessionFactory != null) {
576             SessionHolder sessionHolder =
577                     (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
578             if (sessionHolder != null && sessionHolder.hasTimeout()) {
579                 query.setTimeout(sessionHolder.getTimeToLiveInSeconds());
580             }
581         }
582     }
583
584     /**
585      * Apply the current transaction timeout, if any, to the given
586      * Hibernate Criteria object.
587      * @param criteria the Hibernate Criteria object
588      * @param sessionFactory Hibernate SessionFactory that the Criteria was created for
589      * @see org.hibernate.Criteria#setTimeout
590      */

591     public static void applyTransactionTimeout(Criteria criteria, SessionFactory sessionFactory) {
592         Assert.notNull(criteria, "No Criteria object specified");
593         SessionHolder sessionHolder =
594             (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
595         if (sessionHolder != null && sessionHolder.hasTimeout()) {
596             criteria.setTimeout(sessionHolder.getTimeToLiveInSeconds());
597         }
598     }
599
600     /**
601      * Convert the given HibernateException to an appropriate exception
602      * from the <code>org.springframework.dao</code> hierarchy.
603      * @param ex HibernateException that occured
604      * @return the corresponding DataAccessException instance
605      * @see HibernateAccessor#convertHibernateAccessException
606      * @see HibernateTransactionManager#convertHibernateAccessException
607      */

608     public static DataAccessException convertHibernateAccessException(HibernateException ex) {
609         if (ex instanceof JDBCConnectionException) {
610             return new DataAccessResourceFailureException(ex.getMessage(), ex);
611         }
612         if (ex instanceof SQLGrammarException) {
613             return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex);
614         }
615         if (ex instanceof DataException) {
616             return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex);
617         }
618         if (ex instanceof LockAcquisitionException) {
619             return new CannotAcquireLockException(ex.getMessage(), ex);
620         }
621         if (ex instanceof ConstraintViolationException) {
622             return new DataIntegrityViolationException(ex.getMessage(), ex);
623         }
624         if (ex instanceof JDBCException) {
625             return new HibernateJdbcException((JDBCException) ex);
626         }
627         if (ex instanceof PropertyValueException) {
628             return new DataIntegrityViolationException(ex.getMessage(), ex);
629         }
630         if (ex instanceof PersistentObjectException) {
631             return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
632         }
633         if (ex instanceof TransientObjectException) {
634             return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
635         }
636         if (ex instanceof ObjectDeletedException) {
637             return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
638         }
639         if (ex instanceof QueryException) {
640             return new HibernateQueryException((QueryException) ex);
641         }
642         if (ex instanceof UnresolvableObjectException) {
643             return new HibernateObjectRetrievalFailureException((UnresolvableObjectException) ex);
644         }
645         if (ex instanceof WrongClassException) {
646             return new HibernateObjectRetrievalFailureException((WrongClassException) ex);
647         }
648         if (ex instanceof NonUniqueResultException) {
649             return new IncorrectResultSizeDataAccessException(ex.getMessage(), 1);
650         }
651         if (ex instanceof StaleObjectStateException) {
652             return new HibernateOptimisticLockingFailureException((StaleObjectStateException) ex);
653         }
654         if (ex instanceof StaleStateException) {
655             return new HibernateOptimisticLockingFailureException((StaleStateException) ex);
656         }
657
658         // fallback
659
return new HibernateSystemException(ex);
660     }
661
662
663     /**
664      * Determine whether deferred close is active for the current thread
665      * and the given SessionFactory.
666      * @param sessionFactory the Hibernate SessionFactory to check
667      * @return whether deferred close is active
668      */

669     public static boolean isDeferredCloseActive(SessionFactory sessionFactory) {
670         Assert.notNull(sessionFactory, "No SessionFactory specified");
671         Map JavaDoc holderMap = (Map JavaDoc) deferredCloseHolder.get();
672         return (holderMap != null && holderMap.containsKey(sessionFactory));
673     }
674
675     /**
676      * Initialize deferred close for the current thread and the given SessionFactory.
677      * Sessions will not be actually closed on close calls then, but rather at a
678      * {@link #processDeferredClose} call at a finishing point (like request completion).
679      * <p>Used by {@link org.springframework.orm.hibernate3.support.OpenSessionInViewFilter}
680      * and {@link org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor}
681      * when not configured for a single session.
682      * @param sessionFactory the Hibernate SessionFactory to initialize deferred close for
683      * @see #processDeferredClose
684      * @see #releaseSession
685      * @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter#setSingleSession
686      * @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor#setSingleSession
687      */

688     public static void initDeferredClose(SessionFactory sessionFactory) {
689         Assert.notNull(sessionFactory, "No SessionFactory specified");
690         logger.debug("Initializing deferred close of Hibernate Sessions");
691         Map JavaDoc holderMap = (Map JavaDoc) deferredCloseHolder.get();
692         if (holderMap == null) {
693             holderMap = new HashMap JavaDoc();
694             deferredCloseHolder.set(holderMap);
695         }
696         holderMap.put(sessionFactory, CollectionFactory.createLinkedSetIfPossible(4));
697     }
698
699     /**
700      * Process all Hibernate Sessions that have been registered for deferred close
701      * for the given SessionFactory.
702      * @param sessionFactory the Hibernate SessionFactory to process deferred close for
703      * @see #initDeferredClose
704      * @see #releaseSession
705      */

706     public static void processDeferredClose(SessionFactory sessionFactory) {
707         Assert.notNull(sessionFactory, "No SessionFactory specified");
708
709         Map JavaDoc holderMap = (Map JavaDoc) deferredCloseHolder.get();
710         if (holderMap == null || !holderMap.containsKey(sessionFactory)) {
711             throw new IllegalStateException JavaDoc("Deferred close not active for SessionFactory [" + sessionFactory + "]");
712         }
713
714         logger.debug("Processing deferred close of Hibernate Sessions");
715         Set JavaDoc sessions = (Set JavaDoc) holderMap.remove(sessionFactory);
716         for (Iterator JavaDoc it = sessions.iterator(); it.hasNext();) {
717             closeSession((Session) it.next());
718         }
719
720         if (holderMap.isEmpty()) {
721             deferredCloseHolder.set(null);
722         }
723     }
724
725     /**
726      * Close the given Session, created via the given factory,
727      * if it is not managed externally (i.e. not bound to the thread).
728      * @param session the Hibernate Session to close (may be <code>null</code>)
729      * @param sessionFactory Hibernate SessionFactory that the Session was created with
730      * (may be <code>null</code>)
731      */

732     public static void releaseSession(Session session, SessionFactory sessionFactory) {
733         if (session == null) {
734             return;
735         }
736         // Only close non-transactional Sessions.
737
if (!isSessionTransactional(session, sessionFactory)) {
738             closeSessionOrRegisterDeferredClose(session, sessionFactory);
739         }
740     }
741
742     /**
743      * Close the given Session or register it for deferred close.
744      * @param session the Hibernate Session to close
745      * @param sessionFactory Hibernate SessionFactory that the Session was created with
746      * (may be <code>null</code>)
747      * @see #initDeferredClose
748      * @see #processDeferredClose
749      */

750     static void closeSessionOrRegisterDeferredClose(Session session, SessionFactory sessionFactory) {
751         Map JavaDoc holderMap = (Map JavaDoc) deferredCloseHolder.get();
752         if (holderMap != null && sessionFactory != null && holderMap.containsKey(sessionFactory)) {
753             logger.debug("Registering Hibernate Session for deferred close");
754             // Switch Session to FlushMode.NEVER for remaining lifetime.
755
session.setFlushMode(FlushMode.NEVER);
756             Set JavaDoc sessions = (Set JavaDoc) holderMap.get(sessionFactory);
757             sessions.add(session);
758         }
759         else {
760             closeSession(session);
761         }
762     }
763
764     /**
765      * Perform actual closing of the Hibernate Session,
766      * catching and logging any cleanup exceptions thrown.
767      * @param session the Hibernate Session to close (may be <code>null</code>)
768      * @see org.hibernate.Session#close()
769      */

770     public static void closeSession(Session session) {
771         if (session != null) {
772             logger.debug("Closing Hibernate Session");
773             try {
774                 session.close();
775             }
776             catch (HibernateException ex) {
777                 logger.debug("Could not close Hibernate Session", ex);
778             }
779             catch (Throwable JavaDoc ex) {
780                 logger.debug("Unexpected exception on closing Hibernate Session", ex);
781             }
782         }
783     }
784
785 }
786
Popular Tags