1 /*
2 * Copyright 2002-2008 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.sql.DataSource;
20
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23 import org.hibernate.HibernateException;
24 import org.hibernate.JDBCException;
25 import org.hibernate.SessionFactory;
26
27 import org.springframework.beans.factory.DisposableBean;
28 import org.springframework.beans.factory.FactoryBean;
29 import org.springframework.beans.factory.InitializingBean;
30 import org.springframework.dao.DataAccessException;
31 import org.springframework.dao.support.PersistenceExceptionTranslator;
32 import org.springframework.jdbc.support.SQLExceptionTranslator;
33
34 /**
35 * Abstract {@link org.springframework.beans.factory.FactoryBean} that creates
36 * a Hibernate {@link org.hibernate.SessionFactory} within a Spring application
37 * context, providing general infrastructure not related to Hibernate's
38 * specific configuration API.
39 *
40 * <p>This class implements the
41 * {@link org.springframework.dao.support.PersistenceExceptionTranslator}
42 * interface, as autodetected by Spring's
43 * {@link org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor},
44 * for AOP-based translation of native exceptions to Spring DataAccessExceptions.
45 * Hence, the presence of e.g. LocalSessionFactoryBean automatically enables
46 * a PersistenceExceptionTranslationPostProcessor to translate Hibernate exceptions.
47 *
48 * <p>This class mainly serves as common base class for {@link LocalSessionFactoryBean}.
49 * For details on typical SessionFactory setup, see the LocalSessionFactoryBean javadoc.
50 *
51 * @author Juergen Hoeller
52 * @since 2.0
53 * @see #setExposeTransactionAwareSessionFactory
54 * @see org.hibernate.SessionFactory#getCurrentSession()
55 * @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor
56 */
57 public abstract class AbstractSessionFactoryBean
58 implements FactoryBean, InitializingBean, DisposableBean, PersistenceExceptionTranslator {
59
60 /** Logger available to subclasses */
61 protected final Log logger = LogFactory.getLog(getClass());
62
63 private DataSource dataSource;
64
65 private boolean useTransactionAwareDataSource = false;
66
67 private boolean exposeTransactionAwareSessionFactory = true;
68
69 private SQLExceptionTranslator jdbcExceptionTranslator;
70
71 private SessionFactory sessionFactory;
72
73
74 /**
75 * Set the DataSource to be used by the SessionFactory.
76 * If set, this will override corresponding settings in Hibernate properties.
77 * <p>If this is set, the Hibernate settings should not define
78 * a connection provider to avoid meaningless double configuration.
79 * <p>If using HibernateTransactionManager as transaction strategy, consider
80 * proxying your target DataSource with a LazyConnectionDataSourceProxy.
81 * This defers fetching of an actual JDBC Connection until the first JDBC
82 * Statement gets executed, even within JDBC transactions (as performed by
83 * HibernateTransactionManager). Such lazy fetching is particularly beneficial
84 * for read-only operations, in particular if the chances of resolving the
85 * result in the second-level cache are high.
86 * <p>As JTA and transactional JNDI DataSources already provide lazy enlistment
87 * of JDBC Connections, LazyConnectionDataSourceProxy does not add value with
88 * JTA (i.e. Spring's JtaTransactionManager) as transaction strategy.
89 * @see #setUseTransactionAwareDataSource
90 * @see HibernateTransactionManager
91 * @see org.springframework.transaction.jta.JtaTransactionManager
92 * @see org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy
93 */
94 public void setDataSource(DataSource dataSource) {
95 this.dataSource = dataSource;
96 }
97
98 /**
99 * Return the DataSource to be used by the SessionFactory.
100 */
101 public DataSource getDataSource() {
102 return this.dataSource;
103 }
104
105 /**
106 * Set whether to use a transaction-aware DataSource for the SessionFactory,
107 * i.e. whether to automatically wrap the passed-in DataSource with Spring's
108 * TransactionAwareDataSourceProxy.
109 * <p>Default is "false": LocalSessionFactoryBean is usually used with Spring's
110 * HibernateTransactionManager or JtaTransactionManager, both of which work nicely
111 * on a plain JDBC DataSource. Hibernate Sessions and their JDBC Connections are
112 * fully managed by the Hibernate/JTA transaction infrastructure in such a scenario.
113 * <p>If you switch this flag to "true", Spring's Hibernate access will be able to
114 * <i>participate in JDBC-based transactions managed outside of Hibernate</i>
115 * (for example, by Spring's DataSourceTransactionManager). This can be convenient
116 * if you need a different local transaction strategy for another O/R mapping tool,
117 * for example, but still want Hibernate access to join into those transactions.
118 * <p>A further benefit of this option is that <i>plain Sessions opened directly
119 * via the SessionFactory</i>, outside of Spring's Hibernate support, will still
120 * participate in active Spring-managed transactions. However, consider using
121 * Hibernate's <code>getCurrentSession()</code> method instead (see javadoc of
122 * "exposeTransactionAwareSessionFactory" property).
123 * <p><b>WARNING:</b> When using a transaction-aware JDBC DataSource in combination
124 * with OpenSessionInViewFilter/Interceptor, whether participating in JTA or
125 * external JDBC-based transactions, it is strongly recommended to set Hibernate's
126 * Connection release mode to "after_transaction" or "after_statement", which
127 * guarantees proper Connection handling in such a scenario. In contrast to that,
128 * HibernateTransactionManager generally requires release mode "on_close".
129 * <p>Note: If you want to use Hibernate's Connection release mode "after_statement"
130 * with a DataSource specified on this LocalSessionFactoryBean (for example, a
131 * JTA-aware DataSource fetched from JNDI), switch this setting to "true".
132 * Otherwise, the ConnectionProvider used underneath will vote against aggressive
133 * release and thus silently switch to release mode "after_transaction".
134 * @see #setDataSource
135 * @see #setExposeTransactionAwareSessionFactory
136 * @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
137 * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
138 * @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
139 * @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor
140 * @see HibernateTransactionManager
141 * @see org.springframework.transaction.jta.JtaTransactionManager
142 */
143 public void setUseTransactionAwareDataSource(boolean useTransactionAwareDataSource) {
144 this.useTransactionAwareDataSource = useTransactionAwareDataSource;
145 }
146
147 /**
148 * Return whether to use a transaction-aware DataSource for the SessionFactory.
149 */
150 protected boolean isUseTransactionAwareDataSource() {
151 return this.useTransactionAwareDataSource;
152 }
153
154 /**
155 * Set whether to expose a transaction-aware current Session from the
156 * SessionFactory's <code>getCurrentSession()</code> method, returning the
157 * Session that's associated with the current Spring-managed transaction, if any.
158 * <p>Default is "true", letting data access code work with the plain
159 * Hibernate SessionFactory and its <code>getCurrentSession()</code> method,
160 * while still being able to participate in current Spring-managed transactions:
161 * with any transaction management strategy, either local or JTA / EJB CMT,
162 * and any transaction synchronization mechanism, either Spring or JTA.
163 * Furthermore, <code>getCurrentSession()</code> will also seamlessly work with
164 * a request-scoped Session managed by OpenSessionInViewFilter/Interceptor.
165 * <p>Turn this flag off to expose the plain Hibernate SessionFactory with
166 * Hibernate's default <code>getCurrentSession()</code> behavior, supporting
167 * plain JTA synchronization only. Alternatively, simply override the
168 * corresponding Hibernate property "hibernate.current_session_context_class".
169 * @see SpringSessionContext
170 * @see org.hibernate.SessionFactory#getCurrentSession()
171 * @see org.springframework.transaction.jta.JtaTransactionManager
172 * @see HibernateTransactionManager
173 * @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
174 * @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor
175 */
176 public void setExposeTransactionAwareSessionFactory(boolean exposeTransactionAwareSessionFactory) {
177 this.exposeTransactionAwareSessionFactory = exposeTransactionAwareSessionFactory;
178 }
179
180 /**
181 * Return whether to expose a transaction-aware proxy for the SessionFactory.
182 */
183 protected boolean isExposeTransactionAwareSessionFactory() {
184 return this.exposeTransactionAwareSessionFactory;
185 }
186
187 /**
188 * Set the JDBC exception translator for the SessionFactory,
189 * exposed via the PersistenceExceptionTranslator interface.
190 * <p>Applied to any SQLException root cause of a Hibernate JDBCException,
191 * overriding Hibernate's default SQLException translation (which is
192 * based on Hibernate's Dialect for a specific target database).
193 * @param jdbcExceptionTranslator the exception translator
194 * @see java.sql.SQLException
195 * @see org.hibernate.JDBCException
196 * @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
197 * @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator
198 * @see org.springframework.dao.support.PersistenceExceptionTranslator
199 */
200 public void setJdbcExceptionTranslator(SQLExceptionTranslator jdbcExceptionTranslator) {
201 this.jdbcExceptionTranslator = jdbcExceptionTranslator;
202 }
203
204
205 /**
206 * Build and expose the SessionFactory.
207 * @see #buildSessionFactory()
208 * @see #wrapSessionFactoryIfNecessary
209 */
210 public void afterPropertiesSet() throws Exception {
211 SessionFactory rawSf = buildSessionFactory();
212 this.sessionFactory = wrapSessionFactoryIfNecessary(rawSf);
213 afterSessionFactoryCreation();
214 }
215
216 /**
217 * Wrap the given SessionFactory with a proxy, if demanded.
218 * <p>The default implementation simply returns the given SessionFactory as-is.
219 * Subclasses may override this to implement transaction awareness through
220 * a SessionFactory proxy, for example.
221 * @param rawSf the raw SessionFactory as built by {@link #buildSessionFactory()}
222 * @return the SessionFactory reference to expose
223 * @see #buildSessionFactory()
224 */
225 protected SessionFactory wrapSessionFactoryIfNecessary(SessionFactory rawSf) {
226 return rawSf;
227 }
228
229 /**
230 * Return the exposed SessionFactory.
231 * Will throw an exception if not initialized yet.
232 * @return the SessionFactory (never <code>null</code>)
233 * @throws IllegalStateException if the SessionFactory has not been initialized yet
234 */
235 protected final SessionFactory getSessionFactory() {
236 if (this.sessionFactory == null) {
237 throw new IllegalStateException("SessionFactory not initialized yet");
238 }
239 return this.sessionFactory;
240 }
241
242 /**
243 * Close the SessionFactory on bean factory shutdown.
244 */
245 public void destroy() throws HibernateException {
246 logger.info("Closing Hibernate SessionFactory");
247 try {
248 beforeSessionFactoryDestruction();
249 }
250 finally {
251 this.sessionFactory.close();
252 }
253 }
254
255
256 /**
257 * Return the singleton SessionFactory.
258 */
259 public Object getObject() {
260 return this.sessionFactory;
261 }
262
263 public Class getObjectType() {
264 return (this.sessionFactory != null) ? this.sessionFactory.getClass() : SessionFactory.class;
265 }
266
267 public boolean isSingleton() {
268 return true;
269 }
270
271
272 /**
273 * Implementation of the PersistenceExceptionTranslator interface,
274 * as autodetected by Spring's PersistenceExceptionTranslationPostProcessor.
275 * <p>Converts the exception if it is a HibernateException;
276 * else returns <code>null</code> to indicate an unknown exception.
277 * @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor
278 * @see #convertHibernateAccessException
279 */
280 public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
281 if (ex instanceof HibernateException) {
282 return convertHibernateAccessException((HibernateException) ex);
283 }
284 return null;
285 }
286
287 /**
288 * Convert the given HibernateException to an appropriate exception from the
289 * <code>org.springframework.dao</code> hierarchy.
290 * <p>Will automatically apply a specified SQLExceptionTranslator to a
291 * Hibernate JDBCException, else rely on Hibernate's default translation.
292 * @param ex HibernateException that occured
293 * @return a corresponding DataAccessException
294 * @see SessionFactoryUtils#convertHibernateAccessException
295 * @see #setJdbcExceptionTranslator
296 */
297 protected DataAccessException convertHibernateAccessException(HibernateException ex) {
298 if (this.jdbcExceptionTranslator != null && ex instanceof JDBCException) {
299 JDBCException jdbcEx = (JDBCException) ex;
300 return this.jdbcExceptionTranslator.translate(
301 "Hibernate operation: " + jdbcEx.getMessage(), jdbcEx.getSQL(), jdbcEx.getSQLException());
302 }
303 return SessionFactoryUtils.convertHibernateAccessException(ex);
304 }
305
306
307 /**
308 * Build the underlying Hibernate SessionFactory.
309 * @return the raw SessionFactory (potentially to be wrapped with a
310 * transaction-aware proxy before it is exposed to the application)
311 * @throws Exception in case of initialization failure
312 */
313 protected abstract SessionFactory buildSessionFactory() throws Exception;
314
315 /**
316 * Hook that allows post-processing after the SessionFactory has been
317 * successfully created. The SessionFactory is already available through
318 * <code>getSessionFactory()</code> at this point.
319 * <p>This implementation is empty.
320 * @throws Exception in case of initialization failure
321 * @see #getSessionFactory()
322 */
323 protected void afterSessionFactoryCreation() throws Exception {
324 }
325
326 /**
327 * Hook that allows shutdown processing before the SessionFactory
328 * will be closed. The SessionFactory is still available through
329 * <code>getSessionFactory()</code> at this point.
330 * <p>This implementation is empty.
331 * @see #getSessionFactory()
332 */
333 protected void beforeSessionFactoryDestruction() {
334 }
335
336 }