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