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