Source Code Cross Referenced for HibernateTransactionManager.java in  » J2EE » spring-framework-2.0.6 » org » springframework » orm » hibernate3 » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » J2EE » spring framework 2.0.6 » org.springframework.orm.hibernate3 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * Copyright 2002-2007 the original author or authors.
003:         *
004:         * Licensed under the Apache License, Version 2.0 (the "License");
005:         * you may not use this file except in compliance with the License.
006:         * You may obtain a copy of the License at
007:         *
008:         *      http://www.apache.org/licenses/LICENSE-2.0
009:         *
010:         * Unless required by applicable law or agreed to in writing, software
011:         * distributed under the License is distributed on an "AS IS" BASIS,
012:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013:         * See the License for the specific language governing permissions and
014:         * limitations under the License.
015:         */
016:
017:        package org.springframework.orm.hibernate3;
018:
019:        import java.sql.Connection;
020:
021:        import javax.sql.DataSource;
022:
023:        import org.hibernate.ConnectionReleaseMode;
024:        import org.hibernate.FlushMode;
025:        import org.hibernate.HibernateException;
026:        import org.hibernate.Interceptor;
027:        import org.hibernate.JDBCException;
028:        import org.hibernate.Session;
029:        import org.hibernate.SessionFactory;
030:        import org.hibernate.Transaction;
031:        import org.hibernate.exception.GenericJDBCException;
032:        import org.hibernate.impl.SessionImpl;
033:
034:        import org.springframework.beans.BeansException;
035:        import org.springframework.beans.factory.BeanFactory;
036:        import org.springframework.beans.factory.BeanFactoryAware;
037:        import org.springframework.beans.factory.InitializingBean;
038:        import org.springframework.dao.DataAccessException;
039:        import org.springframework.jdbc.datasource.ConnectionHolder;
040:        import org.springframework.jdbc.datasource.DataSourceUtils;
041:        import org.springframework.jdbc.datasource.JdbcTransactionObjectSupport;
042:        import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
043:        import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
044:        import org.springframework.jdbc.support.SQLExceptionTranslator;
045:        import org.springframework.transaction.CannotCreateTransactionException;
046:        import org.springframework.transaction.IllegalTransactionStateException;
047:        import org.springframework.transaction.InvalidIsolationLevelException;
048:        import org.springframework.transaction.TransactionDefinition;
049:        import org.springframework.transaction.TransactionSystemException;
050:        import org.springframework.transaction.support.AbstractPlatformTransactionManager;
051:        import org.springframework.transaction.support.DefaultTransactionStatus;
052:        import org.springframework.transaction.support.TransactionSynchronizationManager;
053:        import org.springframework.transaction.support.ResourceTransactionManager;
054:        import org.springframework.util.ClassUtils;
055:
056:        /**
057:         * {@link org.springframework.transaction.PlatformTransactionManager}
058:         * implementation for a single Hibernate {@link org.hibernate.SessionFactory}.
059:         * Binds a Hibernate Session from the specified factory to the thread, potentially
060:         * allowing for one thread-bound Session per factory. {@link SessionFactoryUtils}
061:         * and {@link HibernateTemplate} are aware of thread-bound Sessions and participate
062:         * in such transactions automatically. Using either of those or going through
063:         * <code>SessionFactory.getCurrentSession()</code> is required for Hibernate
064:         * access code that needs to support this transaction handling mechanism.
065:         *
066:         * <p>Supports custom isolation levels, and timeouts that get applied as
067:         * Hibernate transaction timeouts (on Hibernate 3.1+) or as appropriate
068:         * query timeouts (on Hibernate 3.0, when using {@link HibernateTemplate}).
069:         *
070:         * <p>This transaction manager is appropriate for applications that use a single
071:         * Hibernate SessionFactory for transactional data access, but it also supports
072:         * direct DataSource access within a transaction (i.e. plain JDBC code working
073:         * with the same DataSource). This allows for mixing services which access Hibernate
074:         * and services which use plain JDBC (without being aware of Hibernate)!
075:         * Application code needs to stick to the same simple Connection lookup pattern as
076:         * with {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}
077:         * (i.e. {@link org.springframework.jdbc.datasource.DataSourceUtils#getConnection}
078:         * or going through a
079:         * {@link org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy}).
080:         *
081:         * <p>Note: To be able to register a DataSource's Connection for plain JDBC code,
082:         * this instance needs to be aware of the DataSource ({@link #setDataSource}).
083:         * The given DataSource should obviously match the one used by the given
084:         * SessionFactory. To achieve this, configure both to the same JNDI DataSource,
085:         * or preferably create the SessionFactory with {@link LocalSessionFactoryBean} and
086:         * a local DataSource (which will be autodetected by this transaction manager).
087:         *
088:         * <p>JTA (usually through {@link org.springframework.transaction.jta.JtaTransactionManager})
089:         * is necessary for accessing multiple transactional resources within the same
090:         * transaction. The DataSource that Hibernate uses needs to be JTA-enabled in
091:         * such a scenario (see container setup). Normally, JTA setup for Hibernate is
092:         * somewhat container-specific due to the JTA TransactionManager lookup, required
093:         * for proper transactional handling of the SessionFactory-level read-write cache.
094:         *
095:         * <p>Fortunately, there is an easier way with Spring: {@link SessionFactoryUtils}
096:         * (and thus {@link HibernateTemplate}) registers synchronizations with Spring's
097:         * {@link org.springframework.transaction.support.TransactionSynchronizationManager}
098:         * (as used by {@link org.springframework.transaction.jta.JtaTransactionManager}),
099:         * for proper after-completion callbacks. Therefore, as long as Spring's
100:         * JtaTransactionManager drives the JTA transactions, Hibernate does not require
101:         * any special configuration for proper JTA participation. Note that there are
102:         * special restrictions with EJB CMT and restrictive JTA subsystems: See
103:         * {@link org.springframework.transaction.jta.JtaTransactionManager}'s javadoc for details.
104:         *
105:         * <p>On JDBC 3.0, this transaction manager supports nested transactions via JDBC 3.0
106:         * Savepoints. The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"}
107:         * flag defaults to "false", though, as nested transactions will just apply to the
108:         * JDBC Connection, not to the Hibernate Session and its cached objects. You can
109:         * manually set the flag to "true" if you want to use nested transactions for
110:         * JDBC access code which participates in Hibernate transactions (provided that
111:         * your JDBC driver supports Savepoints). <i>Note that Hibernate itself does not
112:         * support nested transactions! Hence, do not expect Hibernate access code to
113:         * semantically participate in a nested transaction.</i>
114:         *
115:         * <p>Requires Hibernate 3.0.3 or later. As of Spring 2.0, this transaction manager
116:         * autodetects Hibernate 3.1 and uses its advanced timeout functionality, while
117:         * remaining compatible with Hibernate 3.0 as well. Running against Hibernate 3.1.3+
118:         * is recommended, unless you need to remain compatible with JDK 1.3. (Note that
119:         * Hibernate 3.1+ only runs on JDK 1.4+!)
120:         *
121:         * @author Juergen Hoeller
122:         * @since 1.2
123:         * @see #setSessionFactory
124:         * @see #setDataSource
125:         * @see LocalSessionFactoryBean
126:         * @see SessionFactoryUtils#getSession
127:         * @see SessionFactoryUtils#applyTransactionTimeout
128:         * @see SessionFactoryUtils#releaseSession
129:         * @see HibernateTemplate
130:         * @see org.hibernate.SessionFactory#getCurrentSession()
131:         * @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection
132:         * @see org.springframework.jdbc.datasource.DataSourceUtils#applyTransactionTimeout
133:         * @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
134:         * @see org.springframework.jdbc.core.JdbcTemplate
135:         * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
136:         * @see org.springframework.transaction.jta.JtaTransactionManager
137:         */
138:        public class HibernateTransactionManager extends
139:                AbstractPlatformTransactionManager implements 
140:                ResourceTransactionManager, BeanFactoryAware, InitializingBean {
141:
142:            // Determine whether the Hibernate 3.1 Transaction.setTimeout(int) method
143:            // is available, for use in this HibernateTransactionManager's doBegin.
144:            private final static boolean hibernateSetTimeoutAvailable = ClassUtils
145:                    .hasMethod(Transaction.class, "setTimeout",
146:                            new Class[] { int.class });
147:
148:            private SessionFactory sessionFactory;
149:
150:            private DataSource dataSource;
151:
152:            private boolean autodetectDataSource = true;
153:
154:            private boolean prepareConnection = true;
155:
156:            private Object entityInterceptor;
157:
158:            private SQLExceptionTranslator jdbcExceptionTranslator;
159:
160:            private SQLExceptionTranslator defaultJdbcExceptionTranslator;
161:
162:            /**
163:             * Just needed for entityInterceptorBeanName.
164:             * @see #setEntityInterceptorBeanName
165:             */
166:            private BeanFactory beanFactory;
167:
168:            /**
169:             * Create a new HibernateTransactionManager instance.
170:             * A SessionFactory has to be set to be able to use it.
171:             * @see #setSessionFactory
172:             */
173:            public HibernateTransactionManager() {
174:            }
175:
176:            /**
177:             * Create a new HibernateTransactionManager instance.
178:             * @param sessionFactory SessionFactory to manage transactions for
179:             */
180:            public HibernateTransactionManager(SessionFactory sessionFactory) {
181:                this .sessionFactory = sessionFactory;
182:                afterPropertiesSet();
183:            }
184:
185:            /**
186:             * Set the SessionFactory that this instance should manage transactions for.
187:             */
188:            public void setSessionFactory(SessionFactory sessionFactory) {
189:                this .sessionFactory = sessionFactory;
190:            }
191:
192:            /**
193:             * Return the SessionFactory that this instance should manage transactions for.
194:             */
195:            public SessionFactory getSessionFactory() {
196:                return this .sessionFactory;
197:            }
198:
199:            /**
200:             * Set the JDBC DataSource that this instance should manage transactions for.
201:             * The DataSource should match the one used by the Hibernate SessionFactory:
202:             * for example, you could specify the same JNDI DataSource for both.
203:             * <p>If the SessionFactory was configured with LocalDataSourceConnectionProvider,
204:             * i.e. by Spring's LocalSessionFactoryBean with a specified "dataSource",
205:             * the DataSource will be auto-detected: You can still explictly specify the
206:             * DataSource, but you don't need to in this case.
207:             * <p>A transactional JDBC Connection for this DataSource will be provided to
208:             * application code accessing this DataSource directly via DataSourceUtils
209:             * or JdbcTemplate. The Connection will be taken from the Hibernate Session.
210:             * <p>The DataSource specified here should be the target DataSource to manage
211:             * transactions for, not a TransactionAwareDataSourceProxy. Only data access
212:             * code may work with TransactionAwareDataSourceProxy, while the transaction
213:             * manager needs to work on the underlying target DataSource. If there's
214:             * nevertheless a TransactionAwareDataSourceProxy passed in, it will be
215:             * unwrapped to extract its target DataSource.
216:             * @see #setAutodetectDataSource
217:             * @see LocalDataSourceConnectionProvider
218:             * @see LocalSessionFactoryBean#setDataSource
219:             * @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
220:             * @see org.springframework.jdbc.datasource.DataSourceUtils
221:             * @see org.springframework.jdbc.core.JdbcTemplate
222:             */
223:            public void setDataSource(DataSource dataSource) {
224:                if (dataSource instanceof  TransactionAwareDataSourceProxy) {
225:                    // If we got a TransactionAwareDataSourceProxy, we need to perform transactions
226:                    // for its underlying target DataSource, else data access code won't see
227:                    // properly exposed transactions (i.e. transactions for the target DataSource).
228:                    this .dataSource = ((TransactionAwareDataSourceProxy) dataSource)
229:                            .getTargetDataSource();
230:                } else {
231:                    this .dataSource = dataSource;
232:                }
233:            }
234:
235:            /**
236:             * Return the JDBC DataSource that this instance manages transactions for.
237:             */
238:            public DataSource getDataSource() {
239:                return this .dataSource;
240:            }
241:
242:            /**
243:             * Set whether to autodetect a JDBC DataSource used by the Hibernate SessionFactory,
244:             * if set via LocalSessionFactoryBean's <code>setDataSource</code>. Default is "true".
245:             * <p>Can be turned off to deliberately ignore an available DataSource,
246:             * to not expose Hibernate transactions as JDBC transactions for that DataSource.
247:             * @see #setDataSource
248:             * @see LocalSessionFactoryBean#setDataSource
249:             */
250:            public void setAutodetectDataSource(boolean autodetectDataSource) {
251:                this .autodetectDataSource = autodetectDataSource;
252:            }
253:
254:            /**
255:             * Set whether to prepare the underlying JDBC Connection of a transactional
256:             * Hibernate Session, that is, whether to apply a transaction-specific
257:             * isolation level and/or the transaction's read-only flag to the underlying
258:             * JDBC Connection.
259:             * <p>Default is "true". If you turn this flag off, the transaction manager
260:             * will not support per-transaction isolation levels anymore. It will not
261:             * call <code>Connection.setReadOnly(true)</code> for read-only transactions
262:             * anymore either. If this flag is turned off, no cleanup of a JDBC Connection
263:             * is required after a transaction, since no Connection settings will get modified.
264:             * <p>It is recommended to turn this flag off if running against Hibernate 3.1
265:             * and a connection pool that does not reset connection settings (for example,
266:             * Jakarta Commons DBCP). To keep this flag turned on, you can set the
267:             * "hibernate.connection.release_mode" property to "on_close" instead,
268:             * or consider using a smarter connection pool (for example, C3P0).
269:             * @see java.sql.Connection#setTransactionIsolation
270:             * @see java.sql.Connection#setReadOnly
271:             */
272:            public void setPrepareConnection(boolean prepareConnection) {
273:                this .prepareConnection = prepareConnection;
274:            }
275:
276:            /**
277:             * Set the bean name of a Hibernate entity interceptor that allows to inspect
278:             * and change property values before writing to and reading from the database.
279:             * Will get applied to any new Session created by this transaction manager.
280:             * <p>Requires the bean factory to be known, to be able to resolve the bean
281:             * name to an interceptor instance on session creation. Typically used for
282:             * prototype interceptors, i.e. a new interceptor instance per session.
283:             * <p>Can also be used for shared interceptor instances, but it is recommended
284:             * to set the interceptor reference directly in such a scenario.
285:             * @param entityInterceptorBeanName the name of the entity interceptor in
286:             * the bean factory
287:             * @see #setBeanFactory
288:             * @see #setEntityInterceptor
289:             */
290:            public void setEntityInterceptorBeanName(
291:                    String entityInterceptorBeanName) {
292:                this .entityInterceptor = entityInterceptorBeanName;
293:            }
294:
295:            /**
296:             * Set a Hibernate entity interceptor that allows to inspect and change
297:             * property values before writing to and reading from the database.
298:             * Will get applied to any new Session created by this transaction manager.
299:             * <p>Such an interceptor can either be set at the SessionFactory level,
300:             * i.e. on LocalSessionFactoryBean, or at the Session level, i.e. on
301:             * HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager.
302:             * It's preferable to set it on LocalSessionFactoryBean or HibernateTransactionManager
303:             * to avoid repeated configuration and guarantee consistent behavior in transactions.
304:             * @see LocalSessionFactoryBean#setEntityInterceptor
305:             * @see HibernateTemplate#setEntityInterceptor
306:             * @see HibernateInterceptor#setEntityInterceptor
307:             */
308:            public void setEntityInterceptor(Interceptor entityInterceptor) {
309:                this .entityInterceptor = entityInterceptor;
310:            }
311:
312:            /**
313:             * Return the current Hibernate entity interceptor, or <code>null</code> if none.
314:             * Resolves an entity interceptor bean name via the bean factory,
315:             * if necessary.
316:             * @throws IllegalStateException if bean name specified but no bean factory set
317:             * @throws BeansException if bean name resolution via the bean factory failed
318:             * @see #setEntityInterceptor
319:             * @see #setEntityInterceptorBeanName
320:             * @see #setBeanFactory
321:             */
322:            public Interceptor getEntityInterceptor()
323:                    throws IllegalStateException, BeansException {
324:                if (this .entityInterceptor instanceof  Interceptor) {
325:                    return (Interceptor) entityInterceptor;
326:                } else if (this .entityInterceptor instanceof  String) {
327:                    if (this .beanFactory == null) {
328:                        throw new IllegalStateException(
329:                                "Cannot get entity interceptor via bean name if no bean factory set");
330:                    }
331:                    String beanName = (String) this .entityInterceptor;
332:                    return (Interceptor) this .beanFactory.getBean(beanName,
333:                            Interceptor.class);
334:                } else {
335:                    return null;
336:                }
337:            }
338:
339:            /**
340:             * Set the JDBC exception translator for this transaction manager.
341:             * <p>Applied to any SQLException root cause of a Hibernate JDBCException that
342:             * is thrown on flush, overriding Hibernate's default SQLException translation
343:             * (which is based on Hibernate's Dialect for a specific target database).
344:             * @param jdbcExceptionTranslator the exception translator
345:             * @see java.sql.SQLException
346:             * @see org.hibernate.JDBCException
347:             * @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
348:             * @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator
349:             */
350:            public void setJdbcExceptionTranslator(
351:                    SQLExceptionTranslator jdbcExceptionTranslator) {
352:                this .jdbcExceptionTranslator = jdbcExceptionTranslator;
353:            }
354:
355:            /**
356:             * Return the JDBC exception translator for this transaction manager, if any.
357:             */
358:            public SQLExceptionTranslator getJdbcExceptionTranslator() {
359:                return this .jdbcExceptionTranslator;
360:            }
361:
362:            /**
363:             * The bean factory just needs to be known for resolving entity interceptor
364:             * bean names. It does not need to be set for any other mode of operation.
365:             * @see #setEntityInterceptorBeanName
366:             */
367:            public void setBeanFactory(BeanFactory beanFactory) {
368:                this .beanFactory = beanFactory;
369:            }
370:
371:            public void afterPropertiesSet() {
372:                if (getSessionFactory() == null) {
373:                    throw new IllegalArgumentException(
374:                            "Property 'sessionFactory' is required");
375:                }
376:                if (this .entityInterceptor instanceof  String
377:                        && this .beanFactory == null) {
378:                    throw new IllegalArgumentException(
379:                            "Property 'beanFactory' is required for 'entityInterceptorBeanName'");
380:                }
381:
382:                // Check for SessionFactory's DataSource.
383:                if (this .autodetectDataSource && getDataSource() == null) {
384:                    DataSource sfds = SessionFactoryUtils
385:                            .getDataSource(getSessionFactory());
386:                    if (sfds != null) {
387:                        // Use the SessionFactory's DataSource for exposing transactions to JDBC code.
388:                        if (logger.isInfoEnabled()) {
389:                            logger
390:                                    .info("Using DataSource ["
391:                                            + sfds
392:                                            + "] of Hibernate SessionFactory for HibernateTransactionManager");
393:                        }
394:                        setDataSource(sfds);
395:                    }
396:                }
397:            }
398:
399:            public Object getResourceFactory() {
400:                return getSessionFactory();
401:            }
402:
403:            protected Object doGetTransaction() {
404:                HibernateTransactionObject txObject = new HibernateTransactionObject();
405:                txObject.setSavepointAllowed(isNestedTransactionAllowed());
406:
407:                SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager
408:                        .getResource(getSessionFactory());
409:                if (sessionHolder != null) {
410:                    if (logger.isDebugEnabled()) {
411:                        logger.debug("Found thread-bound Session ["
412:                                + SessionFactoryUtils.toString(sessionHolder
413:                                        .getSession())
414:                                + "] for Hibernate transaction");
415:                    }
416:                    txObject.setSessionHolder(sessionHolder, false);
417:                }
418:
419:                if (getDataSource() != null) {
420:                    ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager
421:                            .getResource(getDataSource());
422:                    txObject.setConnectionHolder(conHolder);
423:                }
424:
425:                return txObject;
426:            }
427:
428:            protected boolean isExistingTransaction(Object transaction) {
429:                return ((HibernateTransactionObject) transaction)
430:                        .hasTransaction();
431:            }
432:
433:            protected void doBegin(Object transaction,
434:                    TransactionDefinition definition) {
435:                HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
436:
437:                if (txObject.hasConnectionHolder()
438:                        && !txObject.getConnectionHolder()
439:                                .isSynchronizedWithTransaction()) {
440:                    throw new IllegalTransactionStateException(
441:                            "Pre-bound JDBC Connection found! HibernateTransactionManager does not support "
442:                                    + "running within DataSourceTransactionManager if told to manage the DataSource itself. "
443:                                    + "It is recommended to use a single HibernateTransactionManager for all transactions "
444:                                    + "on a single DataSource, no matter whether Hibernate or JDBC access.");
445:                }
446:
447:                Session session = null;
448:
449:                try {
450:                    if (txObject.getSessionHolder() == null
451:                            || txObject.getSessionHolder()
452:                                    .isSynchronizedWithTransaction()) {
453:                        Interceptor entityInterceptor = getEntityInterceptor();
454:                        Session newSession = (entityInterceptor != null ? getSessionFactory()
455:                                .openSession(entityInterceptor)
456:                                : getSessionFactory().openSession());
457:                        if (logger.isDebugEnabled()) {
458:                            logger.debug("Opened new Session ["
459:                                    + SessionFactoryUtils.toString(newSession)
460:                                    + "] for Hibernate transaction");
461:                        }
462:                        txObject.setSessionHolder(
463:                                new SessionHolder(newSession), true);
464:                    }
465:
466:                    txObject.getSessionHolder().setSynchronizedWithTransaction(
467:                            true);
468:                    session = txObject.getSessionHolder().getSession();
469:
470:                    if (this .prepareConnection
471:                            && isSameConnectionForEntireSession(session)) {
472:                        // We're allowed to change the transaction settings of the JDBC Connection.
473:                        if (logger.isDebugEnabled()) {
474:                            logger
475:                                    .debug("Preparing JDBC Connection of Hibernate Session ["
476:                                            + SessionFactoryUtils
477:                                                    .toString(session) + "]");
478:                        }
479:                        Connection con = session.connection();
480:                        Integer previousIsolationLevel = DataSourceUtils
481:                                .prepareConnectionForTransaction(con,
482:                                        definition);
483:                        txObject
484:                                .setPreviousIsolationLevel(previousIsolationLevel);
485:                    } else {
486:                        // Not allowed to change the transaction settings of the JDBC Connection.
487:                        if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
488:                            // We should set a specific isolation level but are not allowed to...
489:                            throw new InvalidIsolationLevelException(
490:                                    "HibernateTransactionManager is not allowed to support custom isolation levels: "
491:                                            + "make sure that its 'prepareConnection' flag is on (the default) and that the "
492:                                            + "Hibernate connection release mode is set to 'on_close' (LocalSessionFactoryBean's default)");
493:                        }
494:                        if (logger.isDebugEnabled()) {
495:                            logger
496:                                    .debug("Not preparing JDBC Connection of Hibernate Session ["
497:                                            + SessionFactoryUtils
498:                                                    .toString(session) + "]");
499:                        }
500:                    }
501:
502:                    if (definition.isReadOnly()
503:                            && txObject.isNewSessionHolder()) {
504:                        // Just set to NEVER in case of a new Session for this transaction.
505:                        session.setFlushMode(FlushMode.NEVER);
506:                    }
507:
508:                    if (!definition.isReadOnly()
509:                            && !txObject.isNewSessionHolder()) {
510:                        // We need AUTO or COMMIT for a non-read-only transaction.
511:                        FlushMode flushMode = session.getFlushMode();
512:                        if (flushMode.lessThan(FlushMode.COMMIT)) {
513:                            session.setFlushMode(FlushMode.AUTO);
514:                            txObject.getSessionHolder().setPreviousFlushMode(
515:                                    flushMode);
516:                        }
517:                    }
518:
519:                    Transaction hibTx = null;
520:
521:                    // Register transaction timeout.
522:                    int timeout = determineTimeout(definition);
523:                    if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
524:                        if (hibernateSetTimeoutAvailable) {
525:                            // Use Hibernate's own transaction timeout mechanism on Hibernate 3.1
526:                            // Applies to all statements, also to inserts, updates and deletes!
527:                            hibTx = session.getTransaction();
528:                            hibTx.setTimeout(timeout);
529:                            hibTx.begin();
530:                        } else {
531:                            // Use Spring query timeouts driven by SessionHolder on Hibernate 3.0
532:                            // Only applies to Hibernate queries, not to insert/update/delete statements.
533:                            hibTx = session.beginTransaction();
534:                            txObject.getSessionHolder().setTimeoutInSeconds(
535:                                    timeout);
536:                        }
537:                    } else {
538:                        // Open a plain Hibernate transaction without specified timeout.
539:                        hibTx = session.beginTransaction();
540:                    }
541:
542:                    // Add the Hibernate transaction to the session holder.
543:                    txObject.getSessionHolder().setTransaction(hibTx);
544:
545:                    // Register the Hibernate Session's JDBC Connection for the DataSource, if set.
546:                    if (getDataSource() != null) {
547:                        Connection con = session.connection();
548:                        ConnectionHolder conHolder = new ConnectionHolder(con);
549:                        if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
550:                            conHolder.setTimeoutInSeconds(timeout);
551:                        }
552:                        if (logger.isDebugEnabled()) {
553:                            logger
554:                                    .debug("Exposing Hibernate transaction as JDBC transaction ["
555:                                            + con + "]");
556:                        }
557:                        TransactionSynchronizationManager.bindResource(
558:                                getDataSource(), conHolder);
559:                        txObject.setConnectionHolder(conHolder);
560:                    }
561:
562:                    // Bind the session holder to the thread.
563:                    if (txObject.isNewSessionHolder()) {
564:                        TransactionSynchronizationManager.bindResource(
565:                                getSessionFactory(), txObject
566:                                        .getSessionHolder());
567:                    }
568:                }
569:
570:                catch (Exception ex) {
571:                    SessionFactoryUtils.closeSession(session);
572:                    throw new CannotCreateTransactionException(
573:                            "Could not open Hibernate Session for transaction",
574:                            ex);
575:                }
576:            }
577:
578:            protected Object doSuspend(Object transaction) {
579:                HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
580:                txObject.setSessionHolder(null, false);
581:                SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager
582:                        .unbindResource(getSessionFactory());
583:                txObject.setConnectionHolder(null);
584:                ConnectionHolder connectionHolder = null;
585:                if (getDataSource() != null) {
586:                    connectionHolder = (ConnectionHolder) TransactionSynchronizationManager
587:                            .unbindResource(getDataSource());
588:                }
589:                return new SuspendedResourcesHolder(sessionHolder,
590:                        connectionHolder);
591:            }
592:
593:            protected void doResume(Object transaction,
594:                    Object suspendedResources) {
595:                SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources;
596:                if (TransactionSynchronizationManager
597:                        .hasResource(getSessionFactory())) {
598:                    // From non-transactional code running in active transaction synchronization
599:                    // -> can be safely removed, will be closed on transaction completion.
600:                    TransactionSynchronizationManager
601:                            .unbindResource(getSessionFactory());
602:                }
603:                TransactionSynchronizationManager
604:                        .bindResource(getSessionFactory(), resourcesHolder
605:                                .getSessionHolder());
606:                if (getDataSource() != null) {
607:                    TransactionSynchronizationManager.bindResource(
608:                            getDataSource(), resourcesHolder
609:                                    .getConnectionHolder());
610:                }
611:            }
612:
613:            protected void doCommit(DefaultTransactionStatus status) {
614:                HibernateTransactionObject txObject = (HibernateTransactionObject) status
615:                        .getTransaction();
616:                if (status.isDebug()) {
617:                    logger
618:                            .debug("Committing Hibernate transaction on Session ["
619:                                    + SessionFactoryUtils.toString(txObject
620:                                            .getSessionHolder().getSession())
621:                                    + "]");
622:                }
623:                try {
624:                    txObject.getSessionHolder().getTransaction().commit();
625:                } catch (org.hibernate.TransactionException ex) {
626:                    // assumably from commit call to the underlying JDBC connection
627:                    throw new TransactionSystemException(
628:                            "Could not commit Hibernate transaction", ex);
629:                } catch (HibernateException ex) {
630:                    // assumably failed to flush changes to database
631:                    throw convertHibernateAccessException(ex);
632:                }
633:            }
634:
635:            protected void doRollback(DefaultTransactionStatus status) {
636:                HibernateTransactionObject txObject = (HibernateTransactionObject) status
637:                        .getTransaction();
638:                if (status.isDebug()) {
639:                    logger
640:                            .debug("Rolling back Hibernate transaction on Session ["
641:                                    + SessionFactoryUtils.toString(txObject
642:                                            .getSessionHolder().getSession())
643:                                    + "]");
644:                }
645:                try {
646:                    txObject.getSessionHolder().getTransaction().rollback();
647:                } catch (org.hibernate.TransactionException ex) {
648:                    throw new TransactionSystemException(
649:                            "Could not roll back Hibernate transaction", ex);
650:                } catch (HibernateException ex) {
651:                    // Shouldn't really happen, as a rollback doesn't cause a flush.
652:                    throw convertHibernateAccessException(ex);
653:                } finally {
654:                    if (!txObject.isNewSessionHolder()) {
655:                        // Clear all pending inserts/updates/deletes in the Session.
656:                        // Necessary for pre-bound Sessions, to avoid inconsistent state.
657:                        txObject.getSessionHolder().getSession().clear();
658:                    }
659:                }
660:            }
661:
662:            protected void doSetRollbackOnly(DefaultTransactionStatus status) {
663:                HibernateTransactionObject txObject = (HibernateTransactionObject) status
664:                        .getTransaction();
665:                if (status.isDebug()) {
666:                    logger.debug("Setting Hibernate transaction on Session ["
667:                            + SessionFactoryUtils.toString(txObject
668:                                    .getSessionHolder().getSession())
669:                            + "] rollback-only");
670:                }
671:                txObject.setRollbackOnly();
672:            }
673:
674:            protected void doCleanupAfterCompletion(Object transaction) {
675:                HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
676:
677:                // Remove the session holder from the thread.
678:                if (txObject.isNewSessionHolder()) {
679:                    TransactionSynchronizationManager
680:                            .unbindResource(getSessionFactory());
681:                }
682:
683:                // Remove the JDBC connection holder from the thread, if exposed.
684:                if (getDataSource() != null) {
685:                    TransactionSynchronizationManager
686:                            .unbindResource(getDataSource());
687:                }
688:
689:                Session session = txObject.getSessionHolder().getSession();
690:                if (this .prepareConnection && session.isConnected()
691:                        && isSameConnectionForEntireSession(session)) {
692:                    // We're running with connection release mode "on_close": We're able to reset
693:                    // the isolation level and/or read-only flag of the JDBC Connection here.
694:                    // Else, we need to rely on the connection pool to perform proper cleanup.
695:                    try {
696:                        Connection con = session.connection();
697:                        DataSourceUtils.resetConnectionAfterTransaction(con,
698:                                txObject.getPreviousIsolationLevel());
699:                    } catch (HibernateException ex) {
700:                        logger
701:                                .debug(
702:                                        "Could not access JDBC Connection of Hibernate Session",
703:                                        ex);
704:                    }
705:                }
706:
707:                if (txObject.isNewSessionHolder()) {
708:                    if (logger.isDebugEnabled()) {
709:                        logger.debug("Closing Hibernate Session ["
710:                                + SessionFactoryUtils.toString(session)
711:                                + "] after transaction");
712:                    }
713:                    SessionFactoryUtils.closeSessionOrRegisterDeferredClose(
714:                            session, getSessionFactory());
715:                } else {
716:                    if (logger.isDebugEnabled()) {
717:                        logger
718:                                .debug("Not closing pre-bound Hibernate Session ["
719:                                        + SessionFactoryUtils.toString(session)
720:                                        + "] after transaction");
721:                    }
722:                    if (txObject.getSessionHolder().getPreviousFlushMode() != null) {
723:                        session.setFlushMode(txObject.getSessionHolder()
724:                                .getPreviousFlushMode());
725:                    }
726:                    if (hibernateSetTimeoutAvailable) {
727:                        // Running against Hibernate 3.1+...
728:                        // Let's explicitly disconnect the Session to provide efficient Connection handling
729:                        // even with connection release mode "on_close". The Session will automatically
730:                        // obtain a new Connection in case of further database access.
731:                        // Couldn't do this on Hibernate 3.0, where disconnect required a manual reconnect.
732:                        session.disconnect();
733:                    }
734:                }
735:                txObject.getSessionHolder().clear();
736:            }
737:
738:            /**
739:             * Return whether the given Hibernate Session will always hold the same
740:             * JDBC Connection. This is used to check whether the transaction manager
741:             * can safely prepare and clean up the JDBC Connection used for a transaction.
742:             * <p>Default implementation checks the Session's connection release mode
743:             * to be "on_close". Unfortunately, this requires casting to SessionImpl,
744:             * as of Hibernate 3.0/3.1. If that cast doesn't work, we'll simply assume
745:             * we're safe and return <code>true</code>.
746:             * @param session the Hibernate Session to check
747:             * @see org.hibernate.impl.SessionImpl#getConnectionReleaseMode()
748:             * @see org.hibernate.ConnectionReleaseMode#ON_CLOSE
749:             */
750:            protected boolean isSameConnectionForEntireSession(Session session) {
751:                if (!(session instanceof  SessionImpl)) {
752:                    // The best we can do is to assume we're safe.
753:                    return true;
754:                }
755:                ConnectionReleaseMode releaseMode = ((SessionImpl) session)
756:                        .getConnectionReleaseMode();
757:                return ConnectionReleaseMode.ON_CLOSE.equals(releaseMode);
758:            }
759:
760:            /**
761:             * Convert the given HibernateException to an appropriate exception
762:             * from the <code>org.springframework.dao</code> hierarchy.
763:             * <p>Will automatically apply a specified SQLExceptionTranslator to a
764:             * Hibernate JDBCException, else rely on Hibernate's default translation.
765:             * @param ex HibernateException that occured
766:             * @return a corresponding DataAccessException
767:             * @see SessionFactoryUtils#convertHibernateAccessException
768:             * @see #setJdbcExceptionTranslator
769:             */
770:            protected DataAccessException convertHibernateAccessException(
771:                    HibernateException ex) {
772:                if (getJdbcExceptionTranslator() != null
773:                        && ex instanceof  JDBCException) {
774:                    return convertJdbcAccessException((JDBCException) ex,
775:                            getJdbcExceptionTranslator());
776:                } else if (GenericJDBCException.class.equals(ex.getClass())) {
777:                    return convertJdbcAccessException(
778:                            (GenericJDBCException) ex,
779:                            getDefaultJdbcExceptionTranslator());
780:                }
781:                return SessionFactoryUtils.convertHibernateAccessException(ex);
782:            }
783:
784:            /**
785:             * Convert the given Hibernate JDBCException to an appropriate exception
786:             * from the <code>org.springframework.dao</code> hierarchy, using the
787:             * given SQLExceptionTranslator.
788:             * @param ex Hibernate JDBCException that occured
789:             * @param translator the SQLExceptionTranslator to use
790:             * @return a corresponding DataAccessException
791:             */
792:            protected DataAccessException convertJdbcAccessException(
793:                    JDBCException ex, SQLExceptionTranslator translator) {
794:                return translator.translate("Hibernate flushing: "
795:                        + ex.getMessage(), ex.getSQL(), ex.getSQLException());
796:            }
797:
798:            /**
799:             * Obtain a default SQLExceptionTranslator, lazily creating it if necessary.
800:             * <p>Creates a default
801:             * {@link org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator}
802:             * for the SessionFactory's underlying DataSource.
803:             */
804:            protected synchronized SQLExceptionTranslator getDefaultJdbcExceptionTranslator() {
805:                if (this .defaultJdbcExceptionTranslator == null) {
806:                    if (getDataSource() != null) {
807:                        this .defaultJdbcExceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(
808:                                getDataSource());
809:                    } else {
810:                        this .defaultJdbcExceptionTranslator = SessionFactoryUtils
811:                                .newJdbcExceptionTranslator(getSessionFactory());
812:                    }
813:                }
814:                return this .defaultJdbcExceptionTranslator;
815:            }
816:
817:            /**
818:             * Hibernate transaction object, representing a SessionHolder.
819:             * Used as transaction object by HibernateTransactionManager.
820:             *
821:             * <p>Derives from JdbcTransactionObjectSupport in order to inherit the
822:             * capability to manage JDBC 3.0 Savepoints for underlying JDBC Connections.
823:             *
824:             * @see SessionHolder
825:             */
826:            private static class HibernateTransactionObject extends
827:                    JdbcTransactionObjectSupport {
828:
829:                private SessionHolder sessionHolder;
830:
831:                private boolean newSessionHolder;
832:
833:                public void setSessionHolder(SessionHolder sessionHolder,
834:                        boolean newSessionHolder) {
835:                    this .sessionHolder = sessionHolder;
836:                    this .newSessionHolder = newSessionHolder;
837:                }
838:
839:                public SessionHolder getSessionHolder() {
840:                    return this .sessionHolder;
841:                }
842:
843:                public boolean isNewSessionHolder() {
844:                    return this .newSessionHolder;
845:                }
846:
847:                public boolean hasTransaction() {
848:                    return (this .sessionHolder != null && this .sessionHolder
849:                            .getTransaction() != null);
850:                }
851:
852:                public void setRollbackOnly() {
853:                    getSessionHolder().setRollbackOnly();
854:                    if (hasConnectionHolder()) {
855:                        getConnectionHolder().setRollbackOnly();
856:                    }
857:                }
858:
859:                public boolean isRollbackOnly() {
860:                    return getSessionHolder().isRollbackOnly()
861:                            || (hasConnectionHolder() && getConnectionHolder()
862:                                    .isRollbackOnly());
863:                }
864:            }
865:
866:            /**
867:             * Holder for suspended resources.
868:             * Used internally by <code>doSuspend</code> and <code>doResume</code>.
869:             */
870:            private static class SuspendedResourcesHolder {
871:
872:                private final SessionHolder sessionHolder;
873:
874:                private final ConnectionHolder connectionHolder;
875:
876:                private SuspendedResourcesHolder(SessionHolder sessionHolder,
877:                        ConnectionHolder conHolder) {
878:                    this .sessionHolder = sessionHolder;
879:                    this .connectionHolder = conHolder;
880:                }
881:
882:                private SessionHolder getSessionHolder() {
883:                    return this .sessionHolder;
884:                }
885:
886:                private ConnectionHolder getConnectionHolder() {
887:                    return this.connectionHolder;
888:                }
889:            }
890:
891:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.