KickJava   Java API By Example, From Geeks To Geeks.

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

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

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

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

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

106     public static final int FLUSH_COMMIT = 3;
107
108
109     /** Constants instance for HibernateAccessor */
110     private static final Constants constants = new Constants(HibernateAccessor.class);
111
112     /** Logger available to subclasses */
113     protected final Log logger = LogFactory.getLog(getClass());
114
115     private SessionFactory sessionFactory;
116
117     private Object JavaDoc entityInterceptor;
118
119     private SQLExceptionTranslator jdbcExceptionTranslator;
120
121     private int flushMode = FLUSH_AUTO;
122
123     /**
124      * Just needed for entityInterceptorBeanName.
125      * @see #setEntityInterceptorBeanName
126      */

127     private BeanFactory beanFactory;
128
129
130     /**
131      * Set the Hibernate SessionFactory that should be used to create
132      * Hibernate Sessions.
133      */

134     public void setSessionFactory(SessionFactory sessionFactory) {
135         this.sessionFactory = sessionFactory;
136     }
137
138     /**
139      * Return the Hibernate SessionFactory that should be used to create
140      * Hibernate Sessions.
141      */

142     public SessionFactory getSessionFactory() {
143         return this.sessionFactory;
144     }
145
146     /**
147      * Set the bean name of a Hibernate entity interceptor that allows to inspect
148      * and change property values before writing to and reading from the database.
149      * Will get applied to any new Session created by this transaction manager.
150      * <p>Requires the bean factory to be known, to be able to resolve the bean
151      * name to an interceptor instance on session creation. Typically used for
152      * prototype interceptors, i.e. a new interceptor instance per session.
153      * <p>Can also be used for shared interceptor instances, but it is recommended
154      * to set the interceptor reference directly in such a scenario.
155      * @param entityInterceptorBeanName the name of the entity interceptor in
156      * the bean factory
157      * @see #setBeanFactory
158      * @see #setEntityInterceptor
159      */

160     public void setEntityInterceptorBeanName(String JavaDoc entityInterceptorBeanName) {
161         this.entityInterceptor = entityInterceptorBeanName;
162     }
163
164     /**
165      * Set a Hibernate entity interceptor that allows to inspect and change
166      * property values before writing to and reading from the database.
167      * Will get applied to any <b>new</b> Session created by this object.
168      * <p>Such an interceptor can either be set at the SessionFactory level,
169      * i.e. on LocalSessionFactoryBean, or at the Session level, i.e. on
170      * HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager.
171      * It's preferable to set it on LocalSessionFactoryBean or HibernateTransactionManager
172      * to avoid repeated configuration and guarantee consistent behavior in transactions.
173      * @see #setEntityInterceptorBeanName
174      * @see LocalSessionFactoryBean#setEntityInterceptor
175      * @see HibernateTransactionManager#setEntityInterceptor
176      */

177     public void setEntityInterceptor(Interceptor entityInterceptor) {
178         this.entityInterceptor = entityInterceptor;
179     }
180
181     /**
182      * Return the current Hibernate entity interceptor, or <code>null</code> if none.
183      * Resolves an entity interceptor bean name via the bean factory,
184      * if necessary.
185      * @throws IllegalStateException if bean name specified but no bean factory set
186      * @throws org.springframework.beans.BeansException if bean name resolution via the bean factory failed
187      * @see #setEntityInterceptor
188      * @see #setEntityInterceptorBeanName
189      * @see #setBeanFactory
190      */

191     public Interceptor getEntityInterceptor() throws IllegalStateException JavaDoc, BeansException {
192         if (this.entityInterceptor instanceof String JavaDoc) {
193             if (this.beanFactory == null) {
194                 throw new IllegalStateException JavaDoc("Cannot get entity interceptor via bean name if no bean factory set");
195             }
196             return (Interceptor) this.beanFactory.getBean((String JavaDoc) this.entityInterceptor, Interceptor.class);
197         }
198         return (Interceptor) this.entityInterceptor;
199     }
200
201     /**
202      * Set the JDBC exception translator for this instance.
203      * Applied to SQLExceptions thrown by callback code, be it direct
204      * SQLExceptions or wrapped Hibernate JDBCExceptions.
205      * <p>The default exception translator is either a SQLErrorCodeSQLExceptionTranslator
206      * if a DataSource is available, or a SQLStateSQLExceptionTranslator else.
207      * @param jdbcExceptionTranslator the exception translator
208      * @see java.sql.SQLException
209      * @see net.sf.hibernate.JDBCException
210      * @see SessionFactoryUtils#newJdbcExceptionTranslator
211      * @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
212      * @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator
213      */

214     public void setJdbcExceptionTranslator(SQLExceptionTranslator jdbcExceptionTranslator) {
215         this.jdbcExceptionTranslator = jdbcExceptionTranslator;
216     }
217
218     /**
219      * Return the JDBC exception translator for this instance.
220      * <p>Creates a default SQLErrorCodeSQLExceptionTranslator or SQLStateSQLExceptionTranslator
221      * for the specified SessionFactory, if no exception translator explicitly specified.
222      */

223     public synchronized SQLExceptionTranslator getJdbcExceptionTranslator() {
224         if (this.jdbcExceptionTranslator == null) {
225             this.jdbcExceptionTranslator = SessionFactoryUtils.newJdbcExceptionTranslator(getSessionFactory());
226         }
227         return this.jdbcExceptionTranslator;
228     }
229
230     /**
231      * Set the flush behavior by the name of the respective constant
232      * in this class, e.g. "FLUSH_AUTO". Default is "FLUSH_AUTO".
233      * @param constantName name of the constant
234      * @see #setFlushMode
235      * @see #FLUSH_AUTO
236      */

237     public void setFlushModeName(String JavaDoc constantName) {
238         setFlushMode(constants.asNumber(constantName).intValue());
239     }
240
241     /**
242      * Set the flush behavior to one of the constants in this class.
243      * Default is FLUSH_AUTO.
244      * @see #setFlushModeName
245      * @see #FLUSH_AUTO
246      */

247     public void setFlushMode(int flushMode) {
248         this.flushMode = flushMode;
249     }
250
251     /**
252      * Return if a flush should be forced after executing the callback code.
253      */

254     public int getFlushMode() {
255         return this.flushMode;
256     }
257
258     /**
259      * The bean factory just needs to be known for resolving entity interceptor
260      * bean names. It does not need to be set for any other mode of operation.
261      * @see #setEntityInterceptorBeanName
262      */

263     public void setBeanFactory(BeanFactory beanFactory) {
264         this.beanFactory = beanFactory;
265     }
266
267     public void afterPropertiesSet() {
268         if (getSessionFactory() == null) {
269             throw new IllegalArgumentException JavaDoc("Property 'sessionFactory' is required");
270         }
271     }
272
273
274     /**
275      * Apply the flush mode that's been specified for this accessor
276      * to the given Session.
277      * @param session the current Hibernate Session
278      * @param existingTransaction if executing within an existing transaction
279      * @return the previous flush mode to restore after the operation,
280      * or <code>null</code> if none
281      * @see #setFlushMode
282      * @see net.sf.hibernate.Session#setFlushMode
283      */

284     protected FlushMode applyFlushMode(Session session, boolean existingTransaction) {
285         if (getFlushMode() == FLUSH_NEVER) {
286             if (existingTransaction) {
287                 FlushMode previousFlushMode = session.getFlushMode();
288                 if (!previousFlushMode.equals(FlushMode.NEVER)) {
289                     session.setFlushMode(FlushMode.NEVER);
290                     return previousFlushMode;
291                 }
292             }
293             else {
294                 session.setFlushMode(FlushMode.NEVER);
295             }
296         }
297         else if (getFlushMode() == FLUSH_EAGER) {
298             if (existingTransaction) {
299                 FlushMode previousFlushMode = session.getFlushMode();
300                 if (!previousFlushMode.equals(FlushMode.AUTO)) {
301                     session.setFlushMode(FlushMode.AUTO);
302                     return previousFlushMode;
303                 }
304             }
305             else {
306                 // rely on default FlushMode.AUTO
307
}
308         }
309         else if (getFlushMode() == FLUSH_COMMIT) {
310             if (existingTransaction) {
311                 FlushMode previousFlushMode = session.getFlushMode();
312                 if (previousFlushMode.equals(FlushMode.AUTO)) {
313                     session.setFlushMode(FlushMode.COMMIT);
314                     return previousFlushMode;
315                 }
316             }
317             else {
318                 session.setFlushMode(FlushMode.COMMIT);
319             }
320         }
321         return null;
322     }
323
324     /**
325      * Flush the given Hibernate Session if necessary.
326      * @param session the current Hibernate Session
327      * @param existingTransaction if executing within an existing transaction
328      * @throws HibernateException in case of Hibernate flushing errors
329      */

330     protected void flushIfNecessary(Session session, boolean existingTransaction) throws HibernateException {
331         if (getFlushMode() == FLUSH_EAGER || (!existingTransaction && getFlushMode() != FLUSH_NEVER)) {
332             logger.debug("Eagerly flushing Hibernate session");
333             session.flush();
334         }
335     }
336
337
338     /**
339      * Convert the given HibernateException to an appropriate exception from the
340      * <code>org.springframework.dao</code> hierarchy. Will automatically detect
341      * wrapped SQLExceptions and convert them accordingly.
342      * <p>The default implementation delegates to SessionFactoryUtils
343      * and {@link #convertJdbcAccessException}. Can be overridden in subclasses.
344      * @param ex HibernateException that occured
345      * @return the corresponding DataAccessException instance
346      * @see #convertJdbcAccessException(net.sf.hibernate.JDBCException)
347      * @see SessionFactoryUtils#convertHibernateAccessException
348      */

349     public DataAccessException convertHibernateAccessException(HibernateException ex) {
350         if (ex instanceof JDBCException) {
351             return convertJdbcAccessException((JDBCException) ex);
352         }
353         return SessionFactoryUtils.convertHibernateAccessException(ex);
354     }
355
356     /**
357      * Convert the given JDBCException to an appropriate exception from the
358      * <code>org.springframework.dao</code> hierarchy. Can be overridden in subclasses.
359      * @param ex JDBCException that occured, wrapping a SQLException
360      * @return the corresponding DataAccessException instance
361      * @see #setJdbcExceptionTranslator
362      */

363     protected DataAccessException convertJdbcAccessException(JDBCException ex) {
364         return getJdbcExceptionTranslator().translate(
365                 "Hibernate operation: " + ex.getMessage(), null, ex.getSQLException());
366     }
367
368     /**
369      * Convert the given SQLException to an appropriate exception from the
370      * <code>org.springframework.dao</code> hierarchy. Can be overridden in subclasses.
371      * <p>Note that a direct SQLException can just occur when callback code
372      * performs direct JDBC access via <code>Session.connection()</code>.
373      * @param ex the SQLException
374      * @return the corresponding DataAccessException instance
375      * @see #setJdbcExceptionTranslator
376      * @see net.sf.hibernate.Session#connection()
377      */

378     protected DataAccessException convertJdbcAccessException(SQLException JavaDoc ex) {
379         return getJdbcExceptionTranslator().translate("Hibernate operation", null, ex);
380     }
381
382 }
383
Popular Tags