KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > orm > hibernate3 > SpringSessionSynchronization


1 /*
2  * Copyright 2002-2006 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 javax.transaction.SystemException JavaDoc;
20 import javax.transaction.Transaction JavaDoc;
21 import javax.transaction.TransactionManager JavaDoc;
22
23 import org.hibernate.FlushMode;
24 import org.hibernate.HibernateException;
25 import org.hibernate.JDBCException;
26 import org.hibernate.Session;
27 import org.hibernate.SessionFactory;
28 import org.hibernate.engine.SessionImplementor;
29
30 import org.springframework.core.Ordered;
31 import org.springframework.dao.DataAccessException;
32 import org.springframework.dao.DataAccessResourceFailureException;
33 import org.springframework.jdbc.support.SQLExceptionTranslator;
34 import org.springframework.transaction.support.TransactionSynchronizationAdapter;
35 import org.springframework.transaction.support.TransactionSynchronizationManager;
36
37 /**
38  * Callback for resource cleanup at the end of a Spring-managed JTA transaction,
39  * that is, when participating in a JtaTransactionManager transaction.
40  *
41  * @author Juergen Hoeller
42  * @since 1.2
43  * @see SessionFactoryUtils
44  * @see org.springframework.transaction.jta.JtaTransactionManager
45  */

46 class SpringSessionSynchronization extends TransactionSynchronizationAdapter implements Ordered {
47
48     private final SessionHolder sessionHolder;
49
50     private final SessionFactory sessionFactory;
51
52     private final SQLExceptionTranslator jdbcExceptionTranslator;
53
54     private final boolean newSession;
55
56     /**
57      * Whether Hibernate has a looked-up JTA TransactionManager that it will
58      * automatically register CacheSynchronizations with on Session connect.
59      */

60     private boolean hibernateTransactionCompletion = false;
61
62     private Transaction JavaDoc jtaTransaction;
63
64     private boolean holderActive = true;
65
66
67     public SpringSessionSynchronization(
68             SessionHolder sessionHolder, SessionFactory sessionFactory,
69             SQLExceptionTranslator jdbcExceptionTranslator, boolean newSession) {
70
71         this.sessionHolder = sessionHolder;
72         this.sessionFactory = sessionFactory;
73         this.jdbcExceptionTranslator = jdbcExceptionTranslator;
74         this.newSession = newSession;
75
76         // Check whether the SessionFactory has a JTA TransactionManager.
77
TransactionManager JavaDoc jtaTm =
78                 SessionFactoryUtils.getJtaTransactionManager(sessionFactory, sessionHolder.getAnySession());
79         if (jtaTm != null) {
80             this.hibernateTransactionCompletion = true;
81             // Fetch current JTA Transaction object
82
// (just necessary for JTA transaction suspension, with an individual
83
// Hibernate Session per currently active/suspended transaction).
84
try {
85                 this.jtaTransaction = jtaTm.getTransaction();
86             }
87             catch (SystemException JavaDoc ex) {
88                 throw new DataAccessResourceFailureException("Could not access JTA transaction", ex);
89             }
90         }
91     }
92
93
94     public int getOrder() {
95         return SessionFactoryUtils.SESSION_SYNCHRONIZATION_ORDER;
96     }
97
98     public void suspend() {
99         if (this.holderActive) {
100             TransactionSynchronizationManager.unbindResource(this.sessionFactory);
101         }
102     }
103
104     public void resume() {
105         if (this.holderActive) {
106             TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder);
107         }
108     }
109
110     public void beforeCommit(boolean readOnly) throws DataAccessException {
111         if (!readOnly) {
112             Session session = null;
113             // Check whether there is a Hibernate Session for the current JTA
114
// transaction. Else, fall back to the default thread-bound Session.
115
if (this.jtaTransaction != null) {
116                 session = this.sessionHolder.getSession(this.jtaTransaction);
117             }
118             if (session == null) {
119                 session = this.sessionHolder.getSession();
120             }
121             // Read-write transaction -> flush the Hibernate Session.
122
// Further check: only flush when not FlushMode.NEVER/MANUAL.
123
if (!session.getFlushMode().lessThan(FlushMode.COMMIT)) {
124                 try {
125                     SessionFactoryUtils.logger.debug("Flushing Hibernate Session on transaction synchronization");
126                     session.flush();
127                 }
128                 catch (HibernateException ex) {
129                     if (this.jdbcExceptionTranslator != null && ex instanceof JDBCException) {
130                         JDBCException jdbcEx = (JDBCException) ex;
131                         throw this.jdbcExceptionTranslator.translate(
132                                 "Hibernate flushing: " + jdbcEx.getMessage(), jdbcEx.getSQL(), jdbcEx.getSQLException());
133                     }
134                     throw SessionFactoryUtils.convertHibernateAccessException(ex);
135                 }
136             }
137         }
138     }
139
140     public void beforeCompletion() {
141         if (this.jtaTransaction != null) {
142             // Typically in case of a suspended JTA transaction:
143
// Remove the Session for the current JTA transaction, but keep the holder.
144
Session session = this.sessionHolder.removeSession(this.jtaTransaction);
145             if (session != null) {
146                 if (this.sessionHolder.isEmpty()) {
147                     // No Sessions for JTA transactions bound anymore -> could remove it.
148
if (TransactionSynchronizationManager.hasResource(this.sessionFactory)) {
149                         // Explicit check necessary because of remote transaction propagation:
150
// The synchronization callbacks will execute in a different thread
151
// in such a scenario, as they're triggered by a remote server.
152
// The best we can do is to leave the SessionHolder bound to the
153
// thread that originally performed the data access. It will be
154
// reused when a new data access operation starts on that thread.
155
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
156                     }
157                     this.holderActive = false;
158                 }
159                 // Do not close a pre-bound Session. In that case, we'll find the
160
// transaction-specific Session the same as the default Session.
161
if (session != this.sessionHolder.getSession()) {
162                     SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, this.sessionFactory);
163                 }
164                 else if (this.sessionHolder.getPreviousFlushMode() != null) {
165                     // In case of pre-bound Session, restore previous flush mode.
166
session.setFlushMode(this.sessionHolder.getPreviousFlushMode());
167                 }
168                 return;
169             }
170         }
171         // We'll only get here if there was no specific JTA transaction to handle.
172
if (this.newSession) {
173             // Default behavior: unbind and close the thread-bound Hibernate Session.
174
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
175             this.holderActive = false;
176             if (this.hibernateTransactionCompletion) {
177                 // Close the Hibernate Session here in case of a Hibernate TransactionManagerLookup:
178
// Hibernate will automatically defer the actual closing until JTA transaction completion.
179
// Else, the Session will be closed in the afterCompletion method, to provide the
180
// correct transaction status for releasing the Session's cache locks.
181
SessionFactoryUtils.closeSessionOrRegisterDeferredClose(this.sessionHolder.getSession(), this.sessionFactory);
182             }
183         }
184         else if (this.sessionHolder.getPreviousFlushMode() != null) {
185             // In case of pre-bound Session, restore previous flush mode.
186
this.sessionHolder.getSession().setFlushMode(this.sessionHolder.getPreviousFlushMode());
187         }
188     }
189
190     public void afterCompletion(int status) {
191         if (!this.hibernateTransactionCompletion || !this.newSession) {
192             // No Hibernate TransactionManagerLookup: apply afterTransactionCompletion callback.
193
// Always perform explicit afterTransactionCompletion callback for pre-bound Session,
194
// even with Hibernate TransactionManagerLookup (which only applies to new Sessions).
195
Session session = this.sessionHolder.getSession();
196             // Provide correct transaction status for releasing the Session's cache locks,
197
// if possible. Else, closing will release all cache locks assuming a rollback.
198
if (session instanceof SessionImplementor) {
199                 ((SessionImplementor) session).afterTransactionCompletion(status == STATUS_COMMITTED, null);
200             }
201             // Close the Hibernate Session here if necessary
202
// (closed in beforeCompletion in case of TransactionManagerLookup).
203
if (this.newSession) {
204                 SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, this.sessionFactory);
205             }
206         }
207         if (!this.newSession && status != STATUS_COMMITTED) {
208             // Clear all pending inserts/updates/deletes in the Session.
209
// Necessary for pre-bound Sessions, to avoid inconsistent state.
210
this.sessionHolder.getSession().clear();
211         }
212         if (this.sessionHolder.doesNotHoldNonDefaultSession()) {
213             this.sessionHolder.setSynchronizedWithTransaction(false);
214         }
215     }
216
217 }
218
Popular Tags