KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > orm > hibernate > 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.hibernate;
18
19 import javax.transaction.SystemException JavaDoc;
20 import javax.transaction.Transaction JavaDoc;
21 import javax.transaction.TransactionManager JavaDoc;
22
23 import net.sf.hibernate.FlushMode;
24 import net.sf.hibernate.HibernateException;
25 import net.sf.hibernate.JDBCException;
26 import net.sf.hibernate.Session;
27 import net.sf.hibernate.SessionFactory;
28 import net.sf.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  * @see SessionFactoryUtils
43  * @see org.springframework.transaction.jta.JtaTransactionManager
44  */

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

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