1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.orm.hibernate3;
18
19 import java.util.HashMap;
20 import java.util.LinkedHashSet;
21 import java.util.Map;
22 import java.util.Set;
23 import javax.sql.DataSource;
24 import javax.transaction.Status;
25 import javax.transaction.Transaction;
26 import javax.transaction.TransactionManager;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.hibernate.Criteria;
31 import org.hibernate.FlushMode;
32 import org.hibernate.HibernateException;
33 import org.hibernate.Interceptor;
34 import org.hibernate.JDBCException;
35 import org.hibernate.NonUniqueObjectException;
36 import org.hibernate.NonUniqueResultException;
37 import org.hibernate.ObjectDeletedException;
38 import org.hibernate.OptimisticLockException;
39 import org.hibernate.PersistentObjectException;
40 import org.hibernate.PessimisticLockException;
41 import org.hibernate.PropertyValueException;
42 import org.hibernate.Query;
43 import org.hibernate.QueryException;
44 import org.hibernate.QueryTimeoutException;
45 import org.hibernate.Session;
46 import org.hibernate.SessionFactory;
47 import org.hibernate.StaleObjectStateException;
48 import org.hibernate.StaleStateException;
49 import org.hibernate.TransientObjectException;
50 import org.hibernate.UnresolvableObjectException;
51 import org.hibernate.WrongClassException;
52 import org.hibernate.connection.ConnectionProvider;
53 import org.hibernate.engine.SessionFactoryImplementor;
54 import org.hibernate.exception.ConstraintViolationException;
55 import org.hibernate.exception.DataException;
56 import org.hibernate.exception.JDBCConnectionException;
57 import org.hibernate.exception.LockAcquisitionException;
58 import org.hibernate.exception.SQLGrammarException;
59
60 import org.springframework.core.NamedThreadLocal;
61 import org.springframework.dao.CannotAcquireLockException;
62 import org.springframework.dao.DataAccessException;
63 import org.springframework.dao.DataAccessResourceFailureException;
64 import org.springframework.dao.DataIntegrityViolationException;
65 import org.springframework.dao.DuplicateKeyException;
66 import org.springframework.dao.IncorrectResultSizeDataAccessException;
67 import org.springframework.dao.InvalidDataAccessApiUsageException;
68 import org.springframework.dao.InvalidDataAccessResourceUsageException;
69 import org.springframework.dao.PessimisticLockingFailureException;
70 import org.springframework.jdbc.datasource.DataSourceUtils;
71 import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
72 import org.springframework.jdbc.support.SQLExceptionTranslator;
73 import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
74 import org.springframework.transaction.jta.SpringJtaSynchronizationAdapter;
75 import org.springframework.transaction.support.TransactionSynchronizationManager;
76 import org.springframework.util.Assert;
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 public abstract class SessionFactoryUtils {
105
106
107
108
109
110
111
112 public static final int SESSION_SYNCHRONIZATION_ORDER =
113 DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100;
114
115 static final Log logger = LogFactory.getLog(SessionFactoryUtils.class);
116
117 private static final ThreadLocal<Map<SessionFactory, Set<Session>>> deferredCloseHolder =
118 new NamedThreadLocal<Map<SessionFactory, Set<Session>>>("Hibernate Sessions registered for deferred close");
119
120
121
122
123
124
125
126
127
128 public static DataSource getDataSource(SessionFactory sessionFactory) {
129 if (sessionFactory instanceof SessionFactoryImplementor) {
130 ConnectionProvider cp = ((SessionFactoryImplementor) sessionFactory).getConnectionProvider();
131 if (cp instanceof LocalDataSourceConnectionProvider) {
132 return ((LocalDataSourceConnectionProvider) cp).getDataSource();
133 }
134 }
135 return null;
136 }
137
138
139
140
141
142
143
144
145
146
147
148 public static SQLExceptionTranslator newJdbcExceptionTranslator(SessionFactory sessionFactory) {
149 DataSource ds = getDataSource(sessionFactory);
150 if (ds != null) {
151 return new SQLErrorCodeSQLExceptionTranslator(ds);
152 }
153 return new SQLStateSQLExceptionTranslator();
154 }
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169 public static TransactionManager getJtaTransactionManager(SessionFactory sessionFactory, Session session) {
170 SessionFactoryImplementor sessionFactoryImpl = null;
171 if (sessionFactory instanceof SessionFactoryImplementor) {
172 sessionFactoryImpl = ((SessionFactoryImplementor) sessionFactory);
173 }
174 else if (session != null) {
175 SessionFactory internalFactory = session.getSessionFactory();
176 if (internalFactory instanceof SessionFactoryImplementor) {
177 sessionFactoryImpl = (SessionFactoryImplementor) internalFactory;
178 }
179 }
180 return (sessionFactoryImpl != null ? sessionFactoryImpl.getTransactionManager() : null);
181 }
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204 public static Session getSession(SessionFactory sessionFactory, boolean allowCreate)
205 throws DataAccessResourceFailureException, IllegalStateException {
206
207 try {
208 return doGetSession(sessionFactory, null, null, allowCreate);
209 }
210 catch (HibernateException ex) {
211 throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
212 }
213 }
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234 public static Session getSession(
235 SessionFactory sessionFactory, Interceptor entityInterceptor,
236 SQLExceptionTranslator jdbcExceptionTranslator) throws DataAccessResourceFailureException {
237
238 try {
239 return doGetSession(sessionFactory, entityInterceptor, jdbcExceptionTranslator, true);
240 }
241 catch (HibernateException ex) {
242 throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
243 }
244 }
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259 public static Session doGetSession(SessionFactory sessionFactory, boolean allowCreate)
260 throws HibernateException, IllegalStateException {
261
262 return doGetSession(sessionFactory, null, null, allowCreate);
263 }
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282 private static Session doGetSession(
283 SessionFactory sessionFactory, Interceptor entityInterceptor,
284 SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate)
285 throws HibernateException, IllegalStateException {
286
287 Assert.notNull(sessionFactory, "No SessionFactory specified");
288
289 Object resource = TransactionSynchronizationManager.getResource(sessionFactory);
290 if (resource instanceof Session) {
291 return (Session) resource;
292 }
293 SessionHolder sessionHolder = (SessionHolder) resource;
294 if (sessionHolder != null && !sessionHolder.isEmpty()) {
295
296 Session session = null;
297 if (TransactionSynchronizationManager.isSynchronizationActive() &&
298 sessionHolder.doesNotHoldNonDefaultSession()) {
299
300
301 session = sessionHolder.getValidatedSession();
302 if (session != null && !sessionHolder.isSynchronizedWithTransaction()) {
303 logger.debug("Registering Spring transaction synchronization for existing Hibernate Session");
304 TransactionSynchronizationManager.registerSynchronization(
305 new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false));
306 sessionHolder.setSynchronizedWithTransaction(true);
307
308
309 FlushMode flushMode = session.getFlushMode();
310 if (flushMode.lessThan(FlushMode.COMMIT) &&
311 !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
312 session.setFlushMode(FlushMode.AUTO);
313 sessionHolder.setPreviousFlushMode(flushMode);
314 }
315 }
316 }
317 else {
318
319 session = getJtaSynchronizedSession(sessionHolder, sessionFactory, jdbcExceptionTranslator);
320 }
321 if (session != null) {
322 return session;
323 }
324 }
325
326 logger.debug("Opening Hibernate Session");
327 Session session = (entityInterceptor != null ?
328 sessionFactory.openSession(entityInterceptor) : sessionFactory.openSession());
329
330
331
332 if (TransactionSynchronizationManager.isSynchronizationActive()) {
333
334 logger.debug("Registering Spring transaction synchronization for new Hibernate Session");
335 SessionHolder holderToUse = sessionHolder;
336 if (holderToUse == null) {
337 holderToUse = new SessionHolder(session);
338 }
339 else {
340 holderToUse.addSession(session);
341 }
342 if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
343 session.setFlushMode(FlushMode.MANUAL);
344 }
345 TransactionSynchronizationManager.registerSynchronization(
346 new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true));
347 holderToUse.setSynchronizedWithTransaction(true);
348 if (holderToUse != sessionHolder) {
349 TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse);
350 }
351 }
352 else {
353
354 registerJtaSynchronization(session, sessionFactory, jdbcExceptionTranslator, sessionHolder);
355 }
356
357
358 if (!allowCreate && !isSessionTransactional(session, sessionFactory)) {
359 closeSession(session);
360 throw new IllegalStateException("No Hibernate Session bound to thread, " +
361 "and configuration does not allow creation of non-transactional one here");
362 }
363
364 return session;
365 }
366
367
368
369
370
371
372
373
374
375
376
377 private static Session getJtaSynchronizedSession(
378 SessionHolder sessionHolder, SessionFactory sessionFactory,
379 SQLExceptionTranslator jdbcExceptionTranslator) throws DataAccessResourceFailureException {
380
381
382
383
384 TransactionManager jtaTm = getJtaTransactionManager(sessionFactory, sessionHolder.getAnySession());
385 if (jtaTm != null) {
386
387
388
389
390 try {
391
392 Transaction jtaTx = jtaTm.getTransaction();
393 if (jtaTx != null) {
394 int jtaStatus = jtaTx.getStatus();
395 if (jtaStatus == Status.STATUS_ACTIVE || jtaStatus == Status.STATUS_MARKED_ROLLBACK) {
396 Session session = sessionHolder.getValidatedSession(jtaTx);
397 if (session == null && !sessionHolder.isSynchronizedWithTransaction()) {
398
399
400
401
402
403 session = sessionHolder.getValidatedSession();
404 if (session != null) {
405 logger.debug("Registering JTA transaction synchronization for existing Hibernate Session");
406 sessionHolder.addSession(jtaTx, session);
407 jtaTx.registerSynchronization(
408 new SpringJtaSynchronizationAdapter(
409 new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false),
410 jtaTm));
411 sessionHolder.setSynchronizedWithTransaction(true);
412
413
414 FlushMode flushMode = session.getFlushMode();
415 if (flushMode.lessThan(FlushMode.COMMIT)) {
416 session.setFlushMode(FlushMode.AUTO);
417 sessionHolder.setPreviousFlushMode(flushMode);
418 }
419 }
420 }
421 return session;
422 }
423 }
424
425
426 return sessionHolder.getValidatedSession();
427 }
428 catch (Throwable ex) {
429 throw new DataAccessResourceFailureException("Could not check JTA transaction", ex);
430 }
431 }
432 else {
433
434
435 return sessionHolder.getValidatedSession();
436 }
437 }
438
439
440
441
442
443
444
445
446
447 private static void registerJtaSynchronization(Session session, SessionFactory sessionFactory,
448 SQLExceptionTranslator jdbcExceptionTranslator, SessionHolder sessionHolder) {
449
450
451
452
453 TransactionManager jtaTm = getJtaTransactionManager(sessionFactory, session);
454 if (jtaTm != null) {
455 try {
456 Transaction jtaTx = jtaTm.getTransaction();
457 if (jtaTx != null) {
458 int jtaStatus = jtaTx.getStatus();
459 if (jtaStatus == Status.STATUS_ACTIVE || jtaStatus == Status.STATUS_MARKED_ROLLBACK) {
460 logger.debug("Registering JTA transaction synchronization for new Hibernate Session");
461 SessionHolder holderToUse = sessionHolder;
462
463
464 if (holderToUse == null) {
465 holderToUse = new SessionHolder(jtaTx, session);
466 }
467 else {
468 holderToUse.addSession(jtaTx, session);
469 }
470 jtaTx.registerSynchronization(
471 new SpringJtaSynchronizationAdapter(
472 new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true),
473 jtaTm));
474 holderToUse.setSynchronizedWithTransaction(true);
475 if (holderToUse != sessionHolder) {
476 TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse);
477 }
478 }
479 }
480 }
481 catch (Throwable ex) {
482 throw new DataAccessResourceFailureException(
483 "Could not register synchronization with JTA TransactionManager", ex);
484 }
485 }
486 }
487
488
489
490
491
492
493
494
495
496
497
498
499 public static Session getNewSession(SessionFactory sessionFactory) {
500 return getNewSession(sessionFactory, null);
501 }
502
503
504
505
506
507
508
509
510
511
512
513
514 @SuppressWarnings("deprecation")
515 public static Session getNewSession(SessionFactory sessionFactory, Interceptor entityInterceptor) {
516 Assert.notNull(sessionFactory, "No SessionFactory specified");
517
518 try {
519 SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
520 if (sessionHolder != null && !sessionHolder.isEmpty()) {
521 if (entityInterceptor != null) {
522 return sessionFactory.openSession(sessionHolder.getAnySession().connection(), entityInterceptor);
523 }
524 else {
525 return sessionFactory.openSession(sessionHolder.getAnySession().connection());
526 }
527 }
528 else {
529 if (entityInterceptor != null) {
530 return sessionFactory.openSession(entityInterceptor);
531 }
532 else {
533 return sessionFactory.openSession();
534 }
535 }
536 }
537 catch (HibernateException ex) {
538 throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
539 }
540 }
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555 public static String toString(Session session) {
556 return session.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(session));
557 }
558
559
560
561
562
563
564
565 public static boolean hasTransactionalSession(SessionFactory sessionFactory) {
566 if (sessionFactory == null) {
567 return false;
568 }
569 SessionHolder sessionHolder =
570 (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
571 return (sessionHolder != null && !sessionHolder.isEmpty());
572 }
573
574
575
576
577
578
579
580
581
582 public static boolean isSessionTransactional(Session session, SessionFactory sessionFactory) {
583 if (sessionFactory == null) {
584 return false;
585 }
586 SessionHolder sessionHolder =
587 (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
588 return (sessionHolder != null && sessionHolder.containsSession(session));
589 }
590
591
592
593
594
595
596
597
598
599 public static void applyTransactionTimeout(Query query, SessionFactory sessionFactory) {
600 Assert.notNull(query, "No Query object specified");
601 if (sessionFactory != null) {
602 SessionHolder sessionHolder =
603 (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
604 if (sessionHolder != null && sessionHolder.hasTimeout()) {
605 query.setTimeout(sessionHolder.getTimeToLiveInSeconds());
606 }
607 }
608 }
609
610
611
612
613
614
615
616
617 public static void applyTransactionTimeout(Criteria criteria, SessionFactory sessionFactory) {
618 Assert.notNull(criteria, "No Criteria object specified");
619 if (sessionFactory != null) {
620 SessionHolder sessionHolder =
621 (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
622 if (sessionHolder != null && sessionHolder.hasTimeout()) {
623 criteria.setTimeout(sessionHolder.getTimeToLiveInSeconds());
624 }
625 }
626 }
627
628
629
630
631
632
633
634
635
636 public static DataAccessException convertHibernateAccessException(HibernateException ex) {
637 if (ex instanceof JDBCConnectionException) {
638 return new DataAccessResourceFailureException(ex.getMessage(), ex);
639 }
640 if (ex instanceof SQLGrammarException) {
641 SQLGrammarException jdbcEx = (SQLGrammarException) ex;
642 return new InvalidDataAccessResourceUsageException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
643 }
644 if (ex instanceof QueryTimeoutException) {
645 QueryTimeoutException jdbcEx = (QueryTimeoutException) ex;
646 return new org.springframework.dao.QueryTimeoutException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
647 }
648 if (ex instanceof LockAcquisitionException) {
649 LockAcquisitionException jdbcEx = (LockAcquisitionException) ex;
650 return new CannotAcquireLockException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
651 }
652 if (ex instanceof PessimisticLockException) {
653 PessimisticLockException jdbcEx = (PessimisticLockException) ex;
654 return new PessimisticLockingFailureException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
655 }
656 if (ex instanceof ConstraintViolationException) {
657 ConstraintViolationException jdbcEx = (ConstraintViolationException) ex;
658 return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() +
659 "]; constraint [" + jdbcEx.getConstraintName() + "]", ex);
660 }
661 if (ex instanceof DataException) {
662 DataException jdbcEx = (DataException) ex;
663 return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
664 }
665 if (ex instanceof JDBCException) {
666 return new HibernateJdbcException((JDBCException) ex);
667 }
668
669
670 if (ex instanceof QueryException) {
671 return new HibernateQueryException((QueryException) ex);
672 }
673 if (ex instanceof NonUniqueResultException) {
674 return new IncorrectResultSizeDataAccessException(ex.getMessage(), 1, ex);
675 }
676 if (ex instanceof NonUniqueObjectException) {
677 return new DuplicateKeyException(ex.getMessage(), ex);
678 }
679 if (ex instanceof PropertyValueException) {
680 return new DataIntegrityViolationException(ex.getMessage(), ex);
681 }
682 if (ex instanceof PersistentObjectException) {
683 return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
684 }
685 if (ex instanceof TransientObjectException) {
686 return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
687 }
688 if (ex instanceof ObjectDeletedException) {
689 return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
690 }
691 if (ex instanceof UnresolvableObjectException) {
692 return new HibernateObjectRetrievalFailureException((UnresolvableObjectException) ex);
693 }
694 if (ex instanceof WrongClassException) {
695 return new HibernateObjectRetrievalFailureException((WrongClassException) ex);
696 }
697 if (ex instanceof StaleObjectStateException) {
698 return new HibernateOptimisticLockingFailureException((StaleObjectStateException) ex);
699 }
700 if (ex instanceof StaleStateException) {
701 return new HibernateOptimisticLockingFailureException((StaleStateException) ex);
702 }
703 if (ex instanceof OptimisticLockException) {
704 return new HibernateOptimisticLockingFailureException((OptimisticLockException) ex);
705 }
706
707
708 return new HibernateSystemException(ex);
709 }
710
711
712
713
714
715
716
717
718 public static boolean isDeferredCloseActive(SessionFactory sessionFactory) {
719 Assert.notNull(sessionFactory, "No SessionFactory specified");
720 Map<SessionFactory, Set<Session>> holderMap = deferredCloseHolder.get();
721 return (holderMap != null && holderMap.containsKey(sessionFactory));
722 }
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737 public static void initDeferredClose(SessionFactory sessionFactory) {
738 Assert.notNull(sessionFactory, "No SessionFactory specified");
739 logger.debug("Initializing deferred close of Hibernate Sessions");
740 Map<SessionFactory, Set<Session>> holderMap = deferredCloseHolder.get();
741 if (holderMap == null) {
742 holderMap = new HashMap<SessionFactory, Set<Session>>();
743 deferredCloseHolder.set(holderMap);
744 }
745 holderMap.put(sessionFactory, new LinkedHashSet<Session>(4));
746 }
747
748
749
750
751
752
753
754
755 public static void processDeferredClose(SessionFactory sessionFactory) {
756 Assert.notNull(sessionFactory, "No SessionFactory specified");
757 Map<SessionFactory, Set<Session>> holderMap = deferredCloseHolder.get();
758 if (holderMap == null || !holderMap.containsKey(sessionFactory)) {
759 throw new IllegalStateException("Deferred close not active for SessionFactory [" + sessionFactory + "]");
760 }
761 logger.debug("Processing deferred close of Hibernate Sessions");
762 Set<Session> sessions = holderMap.remove(sessionFactory);
763 for (Session session : sessions) {
764 closeSession(session);
765 }
766 if (holderMap.isEmpty()) {
767 deferredCloseHolder.remove();
768 }
769 }
770
771
772
773
774
775
776
777
778 public static void releaseSession(Session session, SessionFactory sessionFactory) {
779 if (session == null) {
780 return;
781 }
782
783 if (!isSessionTransactional(session, sessionFactory)) {
784 closeSessionOrRegisterDeferredClose(session, sessionFactory);
785 }
786 }
787
788
789
790
791
792
793
794
795
796 static void closeSessionOrRegisterDeferredClose(Session session, SessionFactory sessionFactory) {
797 Map<SessionFactory, Set<Session>> holderMap = deferredCloseHolder.get();
798 if (holderMap != null && sessionFactory != null && holderMap.containsKey(sessionFactory)) {
799 logger.debug("Registering Hibernate Session for deferred close");
800
801 session.setFlushMode(FlushMode.MANUAL);
802 Set<Session> sessions = holderMap.get(sessionFactory);
803 sessions.add(session);
804 }
805 else {
806 closeSession(session);
807 }
808 }
809
810
811
812
813
814
815
816 public static void closeSession(Session session) {
817 if (session != null) {
818 logger.debug("Closing Hibernate Session");
819 try {
820 session.close();
821 }
822 catch (HibernateException ex) {
823 logger.debug("Could not close Hibernate Session", ex);
824 }
825 catch (Throwable ex) {
826 logger.debug("Unexpected exception on closing Hibernate Session", ex);
827 }
828 }
829 }
830
831 }