KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * Copyright 2002-2007 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 java.sql.SQLException JavaDoc;
20
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23 import org.hibernate.FlushMode;
24 import org.hibernate.HibernateException;
25 import org.hibernate.Interceptor;
26 import org.hibernate.JDBCException;
27 import org.hibernate.Session;
28 import org.hibernate.SessionFactory;
29 import org.hibernate.exception.GenericJDBCException;
30
31 import org.springframework.beans.BeansException;
32 import org.springframework.beans.factory.BeanFactory;
33 import org.springframework.beans.factory.BeanFactoryAware;
34 import org.springframework.beans.factory.InitializingBean;
35 import org.springframework.core.Constants;
36 import org.springframework.dao.DataAccessException;
37 import org.springframework.jdbc.support.SQLExceptionTranslator;
38
39 /**
40  * Base class for {@link HibernateTemplate} and {@link HibernateInterceptor},
41  * defining common properties such as SessionFactory and flushing behavior.
42  *
43  * <p>Not intended to be used directly.
44  * See {@link HibernateTemplate} and {@link HibernateInterceptor}.
45  *
46  * @author Juergen Hoeller
47  * @since 1.2
48  * @see HibernateTemplate
49  * @see HibernateInterceptor
50  * @see #setFlushMode
51  */

52 public abstract class HibernateAccessor implements InitializingBean, BeanFactoryAware {
53
54     /**
55      * Never flush is a good strategy for read-only units of work.
56      * Hibernate will not track and look for changes in this case,
57      * avoiding any overhead of modification detection.
58      * <p>In case of an existing Session, FLUSH_NEVER will turn the flush mode
59      * to NEVER for the scope of the current operation, resetting the previous
60      * flush mode afterwards.
61      * @see #setFlushMode
62      */

63     public static final int FLUSH_NEVER = 0;
64
65     /**
66      * Automatic flushing is the default mode for a Hibernate Session.
67      * A session will get flushed on transaction commit, and on certain find
68      * operations that might involve already modified instances, but not
69      * after each unit of work like with eager flushing.
70      * <p>In case of an existing Session, FLUSH_AUTO will participate in the
71      * existing flush mode, not modifying it for the current operation.
72      * This in particular means that this setting will not modify an existing
73      * flush mode NEVER, in contrast to FLUSH_EAGER.
74      * @see #setFlushMode
75      */

76     public static final int FLUSH_AUTO = 1;
77
78     /**
79      * Eager flushing leads to immediate synchronization with the database,
80      * even if in a transaction. This causes inconsistencies to show up and throw
81      * a respective exception immediately, and JDBC access code that participates
82      * in the same transaction will see the changes as the database is already
83      * aware of them then. But the drawbacks are:
84      * <ul>
85      * <li>additional communication roundtrips with the database, instead of a
86      * single batch at transaction commit;
87      * <li>the fact that an actual database rollback is needed if the Hibernate
88      * transaction rolls back (due to already submitted SQL statements).
89      * </ul>
90      * <p>In case of an existing Session, FLUSH_EAGER will turn the flush mode
91      * to AUTO for the scope of the current operation and issue a flush at the
92      * end, resetting the previous flush mode afterwards.
93      * @see #setFlushMode
94      */

95     public static final int FLUSH_EAGER = 2;
96
97     /**
98      * Flushing at commit only is intended for units of work where no
99      * intermediate flushing is desired, not even for find operations
100      * that might involve already modified instances.
101      * <p>In case of an existing Session, FLUSH_COMMIT will turn the flush mode
102      * to COMMIT for the scope of the current operation, resetting the previous
103      * flush mode afterwards. The only exception is an existing flush mode
104      * NEVER, which will not be modified through this setting.
105      * @see #setFlushMode
106      */

107     public static final int FLUSH_COMMIT = 3;
108
109     /**
110      * Flushing before every query statement is rarely necessary.
111      * It is only available for special needs.
112      * <p>In case of an existing Session, FLUSH_ALWAYS will turn the flush mode
113      * to ALWAYS for the scope of the current operation, resetting the previous
114      * flush mode afterwards.
115      * @see #setFlushMode
116      */

117     public static final int FLUSH_ALWAYS = 4;
118
119
120     /** Constants instance for HibernateAccessor */
121     private static final Constants constants = new Constants(HibernateAccessor.class);
122
123     /** Logger available to subclasses */
124     protected final Log logger = LogFactory.getLog(getClass());
125
126     private SessionFactory sessionFactory;
127
128     private Object JavaDoc entityInterceptor;
129
130     private SQLExceptionTranslator jdbcExceptionTranslator;
131
132     private SQLExceptionTranslator defaultJdbcExceptionTranslator;
133
134     private int flushMode = FLUSH_AUTO;
135
136     private String JavaDoc[] filterNames;
137
138     /**
139      * Just needed for entityInterceptorBeanName.
140      * @see #setEntityInterceptorBeanName
141      */

142     private BeanFactory beanFactory;
143
144
145     /**
146      * Set the Hibernate SessionFactory that should be used to create
147      * Hibernate Sessions.
148      */

149     public void setSessionFactory(SessionFactory sessionFactory) {
150         this.sessionFactory = sessionFactory;
151     }
152
153     /**
154      * Return the Hibernate SessionFactory that should be used to create
155      * Hibernate Sessions.
156      */

157     public SessionFactory getSessionFactory() {
158         return this.sessionFactory;
159     }
160
161     /**
162      * Set the bean name of a Hibernate entity interceptor that allows to inspect
163      * and change property values before writing to and reading from the database.
164      * Will get applied to any new Session created by this transaction manager.
165      * <p>Requires the bean factory to be known, to be able to resolve the bean
166      * name to an interceptor instance on session creation. Typically used for
167      * prototype interceptors, i.e. a new interceptor instance per session.
168      * <p>Can also be used for shared interceptor instances, but it is recommended
169      * to set the interceptor reference directly in such a scenario.
170      * @param entityInterceptorBeanName the name of the entity interceptor in
171      * the bean factory
172      * @see #setBeanFactory
173      * @see #setEntityInterceptor
174      */

175     public void setEntityInterceptorBeanName(String JavaDoc entityInterceptorBeanName) {
176         this.entityInterceptor = entityInterceptorBeanName;
177     }
178
179     /**
180      * Set a Hibernate entity interceptor that allows to inspect and change
181      * property values before writing to and reading from the database.
182      * Will get applied to any <b>new</b> Session created by this object.
183      * <p>Such an interceptor can either be set at the SessionFactory level,
184      * i.e. on LocalSessionFactoryBean, or at the Session level, i.e. on
185      * HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager.
186      * It's preferable to set it on LocalSessionFactoryBean or HibernateTransactionManager
187      * to avoid repeated configuration and guarantee consistent behavior in transactions.
188      * @see #setEntityInterceptorBeanName
189      * @see LocalSessionFactoryBean#setEntityInterceptor
190      * @see HibernateTransactionManager#setEntityInterceptor
191      */

192     public void setEntityInterceptor(Interceptor entityInterceptor) {
193         this.entityInterceptor = entityInterceptor;
194     }
195
196     /**
197      * Return the current Hibernate entity interceptor, or <code>null</code> if none.
198      * Resolves an entity interceptor bean name via the bean factory,
199      * if necessary.
200      * @throws IllegalStateException if bean name specified but no bean factory set
201      * @throws org.springframework.beans.BeansException if bean name resolution via the bean factory failed
202      * @see #setEntityInterceptor
203      * @see #setEntityInterceptorBeanName
204      * @see #setBeanFactory
205      */

206     public Interceptor getEntityInterceptor() throws IllegalStateException JavaDoc, BeansException {
207         if (this.entityInterceptor instanceof String JavaDoc) {
208             if (this.beanFactory == null) {
209                 throw new IllegalStateException JavaDoc("Cannot get entity interceptor via bean name if no bean factory set");
210             }
211             return (Interceptor) this.beanFactory.getBean((String JavaDoc) this.entityInterceptor, Interceptor.class);
212         }
213         return (Interceptor) this.entityInterceptor;
214     }
215
216     /**
217      * Set the JDBC exception translator for this instance.
218      * <p>Applied to any SQLException root cause of a Hibernate JDBCException,
219      * overriding Hibernate's default SQLException translation (which is
220      * based on Hibernate's Dialect for a specific target database).
221      * @param jdbcExceptionTranslator the exception translator
222      * @see java.sql.SQLException
223      * @see org.hibernate.JDBCException
224      * @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
225      * @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator
226      */

227     public void setJdbcExceptionTranslator(SQLExceptionTranslator jdbcExceptionTranslator) {
228         this.jdbcExceptionTranslator = jdbcExceptionTranslator;
229     }
230
231     /**
232      * Return the JDBC exception translator for this instance, if any.
233      */

234     public SQLExceptionTranslator getJdbcExceptionTranslator() {
235         return this.jdbcExceptionTranslator;
236     }
237
238     /**
239      * Set the flush behavior by the name of the respective constant
240      * in this class, e.g. "FLUSH_AUTO". Default is "FLUSH_AUTO".
241      * @param constantName name of the constant
242      * @see #setFlushMode
243      * @see #FLUSH_AUTO
244      */

245     public void setFlushModeName(String JavaDoc constantName) {
246         setFlushMode(constants.asNumber(constantName).intValue());
247     }
248
249     /**
250      * Set the flush behavior to one of the constants in this class.
251      * Default is FLUSH_AUTO.
252      * @see #setFlushModeName
253      * @see #FLUSH_AUTO
254      */

255     public void setFlushMode(int flushMode) {
256         this.flushMode = flushMode;
257     }
258
259     /**
260      * Return if a flush should be forced after executing the callback code.
261      */

262     public int getFlushMode() {
263         return this.flushMode;
264     }
265
266     /**
267      * Set the name of a Hibernate filter to be activated for all
268      * Sessions that this accessor works with.
269      * <p>This filter will be enabled at the beginning of each operation
270      * and correspondingly disabled at the end of the operation.
271      * This will work for newly opened Sessions as well as for existing
272      * Sessions (for example, within a transaction).
273      * @see #enableFilters(org.hibernate.Session)
274      * @see org.hibernate.Session#enableFilter(String)
275      * @see LocalSessionFactoryBean#setFilterDefinitions
276      */

277     public void setFilterName(String JavaDoc filter) {
278         this.filterNames = new String JavaDoc[] {filter};
279     }
280
281     /**
282      * Set one or more names of Hibernate filters to be activated for all
283      * Sessions that this accessor works with.
284      * <p>Each of those filters will be enabled at the beginning of each
285      * operation and correspondingly disabled at the end of the operation.
286      * This will work for newly opened Sessions as well as for existing
287      * Sessions (for example, within a transaction).
288      * @see #enableFilters(org.hibernate.Session)
289      * @see org.hibernate.Session#enableFilter(String)
290      * @see LocalSessionFactoryBean#setFilterDefinitions
291      */

292     public void setFilterNames(String JavaDoc[] filterNames) {
293         this.filterNames = filterNames;
294     }
295
296     /**
297      * Return the names of Hibernate filters to be activated, if any.
298      */

299     public String JavaDoc[] getFilterNames() {
300         return this.filterNames;
301     }
302
303     /**
304      * The bean factory just needs to be known for resolving entity interceptor
305      * bean names. It does not need to be set for any other mode of operation.
306      * @see #setEntityInterceptorBeanName
307      */

308     public void setBeanFactory(BeanFactory beanFactory) {
309         this.beanFactory = beanFactory;
310     }
311
312     public void afterPropertiesSet() {
313         if (getSessionFactory() == null) {
314             throw new IllegalArgumentException JavaDoc("Property 'sessionFactory' is required");
315         }
316     }
317
318
319     /**
320      * Apply the flush mode that's been specified for this accessor
321      * to the given Session.
322      * @param session the current Hibernate Session
323      * @param existingTransaction if executing within an existing transaction
324      * @return the previous flush mode to restore after the operation,
325      * or <code>null</code> if none
326      * @see #setFlushMode
327      * @see org.hibernate.Session#setFlushMode
328      */

329     protected FlushMode applyFlushMode(Session session, boolean existingTransaction) {
330         if (getFlushMode() == FLUSH_NEVER) {
331             if (existingTransaction) {
332                 FlushMode previousFlushMode = session.getFlushMode();
333                 if (!previousFlushMode.lessThan(FlushMode.COMMIT)) {
334                     session.setFlushMode(FlushMode.NEVER);
335                     return previousFlushMode;
336                 }
337             }
338             else {
339                 session.setFlushMode(FlushMode.NEVER);
340             }
341         }
342         else if (getFlushMode() == FLUSH_EAGER) {
343             if (existingTransaction) {
344                 FlushMode previousFlushMode = session.getFlushMode();
345                 if (!previousFlushMode.equals(FlushMode.AUTO)) {
346                     session.setFlushMode(FlushMode.AUTO);
347                     return previousFlushMode;
348                 }
349             }
350             else {
351                 // rely on default FlushMode.AUTO
352
}
353         }
354         else if (getFlushMode() == FLUSH_COMMIT) {
355             if (existingTransaction) {
356                 FlushMode previousFlushMode = session.getFlushMode();
357                 if (previousFlushMode.equals(FlushMode.AUTO) || previousFlushMode.equals(FlushMode.ALWAYS)) {
358                     session.setFlushMode(FlushMode.COMMIT);
359                     return previousFlushMode;
360                 }
361             }
362             else {
363                 session.setFlushMode(FlushMode.COMMIT);
364             }
365         }
366         else if (getFlushMode() == FLUSH_ALWAYS) {
367             if (existingTransaction) {
368                 FlushMode previousFlushMode = session.getFlushMode();
369                 if (!previousFlushMode.equals(FlushMode.ALWAYS)) {
370                     session.setFlushMode(FlushMode.ALWAYS);
371                     return previousFlushMode;
372                 }
373             }
374             else {
375                 session.setFlushMode(FlushMode.ALWAYS);
376             }
377         }
378         return null;
379     }
380
381     /**
382      * Flush the given Hibernate Session if necessary.
383      * @param session the current Hibernate Session
384      * @param existingTransaction if executing within an existing transaction
385      * @throws HibernateException in case of Hibernate flushing errors
386      */

387     protected void flushIfNecessary(Session session, boolean existingTransaction) throws HibernateException {
388         if (getFlushMode() == FLUSH_EAGER || (!existingTransaction && getFlushMode() != FLUSH_NEVER)) {
389             logger.debug("Eagerly flushing Hibernate session");
390             session.flush();
391         }
392     }
393
394
395     /**
396      * Convert the given HibernateException to an appropriate exception
397      * from the <code>org.springframework.dao</code> hierarchy.
398      * <p>Will automatically apply a specified SQLExceptionTranslator to a
399      * Hibernate JDBCException, else rely on Hibernate's default translation.
400      * @param ex HibernateException that occured
401      * @return a corresponding DataAccessException
402      * @see SessionFactoryUtils#convertHibernateAccessException
403      * @see #setJdbcExceptionTranslator
404      */

405     public DataAccessException convertHibernateAccessException(HibernateException ex) {
406         if (getJdbcExceptionTranslator() != null && ex instanceof JDBCException) {
407             return convertJdbcAccessException((JDBCException) ex, getJdbcExceptionTranslator());
408         }
409         else if (GenericJDBCException.class.equals(ex.getClass())) {
410             return convertJdbcAccessException((GenericJDBCException) ex, getDefaultJdbcExceptionTranslator());
411         }
412         return SessionFactoryUtils.convertHibernateAccessException(ex);
413     }
414
415     /**
416      * Convert the given Hibernate JDBCException to an appropriate exception
417      * from the <code>org.springframework.dao</code> hierarchy, using the
418      * given SQLExceptionTranslator.
419      * @param ex Hibernate JDBCException that occured
420      * @param translator the SQLExceptionTranslator to use
421      * @return a corresponding DataAccessException
422      */

423     protected DataAccessException convertJdbcAccessException(JDBCException ex, SQLExceptionTranslator translator) {
424         return translator.translate("Hibernate operation: " + ex.getMessage(), ex.getSQL(), ex.getSQLException());
425     }
426
427     /**
428      * Convert the given SQLException to an appropriate exception from the
429      * <code>org.springframework.dao</code> hierarchy. Can be overridden in subclasses.
430      * <p>Note that a direct SQLException can just occur when callback code
431      * performs direct JDBC access via <code>Session.connection()</code>.
432      * @param ex the SQLException
433      * @return the corresponding DataAccessException instance
434      * @see #setJdbcExceptionTranslator
435      * @see org.hibernate.Session#connection()
436      */

437     protected DataAccessException convertJdbcAccessException(SQLException JavaDoc ex) {
438         SQLExceptionTranslator translator = getJdbcExceptionTranslator();
439         if (translator == null) {
440             translator = getDefaultJdbcExceptionTranslator();
441         }
442         return translator.translate("Hibernate-related JDBC operation", null, ex);
443     }
444
445     /**
446      * Obtain a default SQLExceptionTranslator, lazily creating it if necessary.
447      * <p>Creates a default
448      * {@link org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator}
449      * for the SessionFactory's underlying DataSource.
450      */

451     protected synchronized SQLExceptionTranslator getDefaultJdbcExceptionTranslator() {
452         if (this.defaultJdbcExceptionTranslator == null) {
453             this.defaultJdbcExceptionTranslator = SessionFactoryUtils.newJdbcExceptionTranslator(getSessionFactory());
454         }
455         return this.defaultJdbcExceptionTranslator;
456     }
457
458
459     /**
460      * Enable the specified filters on the given Session.
461      * @param session the current Hibernate Session
462      * @see #setFilterNames
463      * @see org.hibernate.Session#enableFilter(String)
464      */

465     protected void enableFilters(Session session) {
466         String JavaDoc[] filterNames = getFilterNames();
467         if (filterNames != null) {
468             for (int i = 0; i < filterNames.length; i++) {
469                 session.enableFilter(filterNames[i]);
470             }
471         }
472     }
473
474     /**
475      * Disable the specified filters on the given Session.
476      * @param session the current Hibernate Session
477      * @see #setFilterNames
478      * @see org.hibernate.Session#disableFilter(String)
479      */

480     protected void disableFilters(Session session) {
481         String JavaDoc[] filterNames = getFilterNames();
482         if (filterNames != null) {
483             for (int i = 0; i < filterNames.length; i++) {
484                 session.disableFilter(filterNames[i]);
485             }
486         }
487     }
488
489 }
490
Popular Tags