Source Code Cross Referenced for JpaTransactionManager.java in  » J2EE » spring-framework-2.5 » org » springframework » orm » jpa » 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.jpa 
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.jpa;
018:
019:        import java.util.HashMap;
020:        import java.util.Map;
021:        import java.util.Properties;
022:
023:        import javax.persistence.EntityManager;
024:        import javax.persistence.EntityManagerFactory;
025:        import javax.persistence.EntityTransaction;
026:        import javax.persistence.PersistenceException;
027:        import javax.persistence.RollbackException;
028:        import javax.sql.DataSource;
029:
030:        import org.springframework.beans.factory.InitializingBean;
031:        import org.springframework.dao.DataAccessException;
032:        import org.springframework.dao.support.DataAccessUtils;
033:        import org.springframework.jdbc.datasource.ConnectionHandle;
034:        import org.springframework.jdbc.datasource.ConnectionHolder;
035:        import org.springframework.jdbc.datasource.JdbcTransactionObjectSupport;
036:        import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
037:        import org.springframework.transaction.CannotCreateTransactionException;
038:        import org.springframework.transaction.IllegalTransactionStateException;
039:        import org.springframework.transaction.NestedTransactionNotSupportedException;
040:        import org.springframework.transaction.SavepointManager;
041:        import org.springframework.transaction.TransactionDefinition;
042:        import org.springframework.transaction.TransactionException;
043:        import org.springframework.transaction.TransactionSystemException;
044:        import org.springframework.transaction.support.AbstractPlatformTransactionManager;
045:        import org.springframework.transaction.support.DefaultTransactionStatus;
046:        import org.springframework.transaction.support.ResourceTransactionManager;
047:        import org.springframework.transaction.support.TransactionSynchronizationManager;
048:        import org.springframework.util.CollectionUtils;
049:
050:        /**
051:         * {@link org.springframework.transaction.PlatformTransactionManager} implementation
052:         * for a single JPA {@link javax.persistence.EntityManagerFactory}. Binds a JPA
053:         * EntityManager from the specified factory to the thread, potentially allowing for
054:         * one thread-bound EntityManager per factory. {@link SharedEntityManagerCreator}
055:         * and {@link JpaTemplate} are aware of thread-bound entity managers and participate
056:         * in such transactions automatically. Using either is required for JPA access code
057:         * supporting this transaction management mechanism.
058:         *
059:         * <p>This transaction manager is appropriate for applications that use a single
060:         * JPA EntityManagerFactory for  transactional data access. JTA (usually through
061:         * {@link org.springframework.transaction.jta.JtaTransactionManager}) is necessary
062:         * for accessing multiple transactional resources within the same transaction.
063:         * Note that you need to configure your JPA provider accordingly in order to make
064:         * it participate in JTA transactions.
065:         *
066:         * <p>This transaction manager also supports direct DataSource access within a
067:         * transaction (i.e. plain JDBC code working with the same DataSource).
068:         * This allows for mixing services which access JPA and services which use plain
069:         * JDBC (without being aware of JPA)! Application code needs to stick to the
070:         * same simple Connection lookup pattern as with
071:         * {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}
072:         * (i.e. {@link org.springframework.jdbc.datasource.DataSourceUtils#getConnection}
073:         * or going through a
074:         * {@link org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy}).
075:         * Note that this requires a vendor-specific {@link JpaDialect} to be configured.
076:         *
077:         * <p>Note: To be able to register a DataSource's Connection for plain JDBC code,
078:         * this instance needs to be aware of the DataSource ({@link #setDataSource}).
079:         * The given DataSource should obviously match the one used by the given
080:         * EntityManagerFactory. This transaction manager will autodetect the DataSource
081:         * used as known connection factory of the EntityManagerFactory, so you usually
082:         * don't need to explicitly specify the "dataSource" property.
083:         *
084:         * <p>On JDBC 3.0, this transaction manager supports nested transactions via JDBC 3.0
085:         * Savepoints. The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"}
086:         * flag defaults to "false", though, as nested transactions will just apply to the
087:         * JDBC Connection, not to the JPA EntityManager and its cached objects.
088:         * You can manually set the flag to "true" if you want to use nested transactions
089:         * for JDBC access code which participates in JPA transactions (provided that your
090:         * JDBC driver supports Savepoints). <i>Note that JPA itself does not support
091:         * nested transactions! Hence, do not expect JPA access code to semantically
092:         * participate in a nested transaction.</i>
093:         *
094:         * @author Juergen Hoeller
095:         * @since 2.0
096:         * @see #setEntityManagerFactory
097:         * @see #setDataSource
098:         * @see LocalEntityManagerFactoryBean
099:         * @see JpaTemplate#execute
100:         * @see org.springframework.orm.jpa.support.SharedEntityManagerBean
101:         * @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection
102:         * @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
103:         * @see org.springframework.jdbc.core.JdbcTemplate
104:         * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
105:         * @see org.springframework.transaction.jta.JtaTransactionManager
106:         */
107:        public class JpaTransactionManager extends
108:                AbstractPlatformTransactionManager implements 
109:                ResourceTransactionManager, InitializingBean {
110:
111:            private EntityManagerFactory entityManagerFactory;
112:
113:            private final Map jpaPropertyMap = new HashMap();
114:
115:            private DataSource dataSource;
116:
117:            private JpaDialect jpaDialect = new DefaultJpaDialect();
118:
119:            /**
120:             * Create a new JpaTransactionManager instance.
121:             * A EntityManagerFactory has to be set to be able to use it.
122:             * @see #setEntityManagerFactory
123:             */
124:            public JpaTransactionManager() {
125:                setNestedTransactionAllowed(true);
126:            }
127:
128:            /**
129:             * Create a new JpaTransactionManager instance.
130:             * @param emf EntityManagerFactory to manage transactions for
131:             */
132:            public JpaTransactionManager(EntityManagerFactory emf) {
133:                this ();
134:                this .entityManagerFactory = emf;
135:                afterPropertiesSet();
136:            }
137:
138:            /**
139:             * Set the EntityManagerFactory that this instance should manage transactions for.
140:             */
141:            public void setEntityManagerFactory(EntityManagerFactory emf) {
142:                this .entityManagerFactory = emf;
143:            }
144:
145:            /**
146:             * Return the EntityManagerFactory that this instance should manage transactions for.
147:             */
148:            public EntityManagerFactory getEntityManagerFactory() {
149:                return this .entityManagerFactory;
150:            }
151:
152:            /**
153:             * Specify JPA properties, to be passed into
154:             * <code>EntityManagerFactory.createEntityManager(Map)</code> (if any).
155:             * <p>Can be populated with a String "value" (parsed via PropertiesEditor)
156:             * or a "props" element in XML bean definitions.
157:             * @see javax.persistence.EntityManagerFactory#createEntityManager(java.util.Map)
158:             */
159:            public void setJpaProperties(Properties jpaProperties) {
160:                CollectionUtils.mergePropertiesIntoMap(jpaProperties,
161:                        this .jpaPropertyMap);
162:            }
163:
164:            /**
165:             * Specify JPA properties as a Map, to be passed into
166:             * <code>EntityManagerFactory.createEntityManager(Map)</code> (if any).
167:             * <p>Can be populated with a "map" or "props" element in XML bean definitions.
168:             * @see javax.persistence.EntityManagerFactory#createEntityManager(java.util.Map)
169:             */
170:            public void setJpaPropertyMap(Map jpaProperties) {
171:                if (jpaProperties != null) {
172:                    this .jpaPropertyMap.putAll(jpaProperties);
173:                }
174:            }
175:
176:            /**
177:             * Allow Map access to the JPA properties to be passed to the persistence
178:             * provider, with the option to add or override specific entries.
179:             * <p>Useful for specifying entries directly, for example via "jpaPropertyMap[myKey]".
180:             */
181:            public Map getJpaPropertyMap() {
182:                return this .jpaPropertyMap;
183:            }
184:
185:            /**
186:             * Set the JDBC DataSource that this instance should manage transactions for.
187:             * The DataSource should match the one used by the JPA EntityManagerFactory:
188:             * for example, you could specify the same JNDI DataSource for both.
189:             * <p>If the EntityManagerFactory uses a known DataSource as connection factory,
190:             * the DataSource will be autodetected: You can still explictly specify the
191:             * DataSource, but you don't need to in this case.
192:             * <p>A transactional JDBC Connection for this DataSource will be provided to
193:             * application code accessing this DataSource directly via DataSourceUtils
194:             * or JdbcTemplate. The Connection will be taken from the JPA EntityManager.
195:             * <p>Note that you need to use a JPA dialect for a specific JPA implementation
196:             * to allow for exposing JPA transactions as JDBC transactions.
197:             * <p>The DataSource specified here should be the target DataSource to manage
198:             * transactions for, not a TransactionAwareDataSourceProxy. Only data access
199:             * code may work with TransactionAwareDataSourceProxy, while the transaction
200:             * manager needs to work on the underlying target DataSource. If there's
201:             * nevertheless a TransactionAwareDataSourceProxy passed in, it will be
202:             * unwrapped to extract its target DataSource.
203:             * @see EntityManagerFactoryInfo#getDataSource()
204:             * @see #setJpaDialect
205:             * @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
206:             * @see org.springframework.jdbc.datasource.DataSourceUtils
207:             * @see org.springframework.jdbc.core.JdbcTemplate
208:             */
209:            public void setDataSource(DataSource dataSource) {
210:                if (dataSource instanceof  TransactionAwareDataSourceProxy) {
211:                    // If we got a TransactionAwareDataSourceProxy, we need to perform transactions
212:                    // for its underlying target DataSource, else data access code won't see
213:                    // properly exposed transactions (i.e. transactions for the target DataSource).
214:                    this .dataSource = ((TransactionAwareDataSourceProxy) dataSource)
215:                            .getTargetDataSource();
216:                } else {
217:                    this .dataSource = dataSource;
218:                }
219:            }
220:
221:            /**
222:             * Return the JDBC DataSource that this instance manages transactions for.
223:             */
224:            public DataSource getDataSource() {
225:                return this .dataSource;
226:            }
227:
228:            /**
229:             * Set the JPA dialect to use for this transaction manager.
230:             * Used for vendor-specific transaction management and JDBC connection exposure.
231:             * <p>If the EntityManagerFactory uses a known JpaDialect, it will be autodetected:
232:             * You can still explictly specify the DataSource, but you don't need to in this case.
233:             * <p>The dialect object can be used to retrieve the underlying JDBC connection
234:             * and thus allows for exposing JPA transactions as JDBC transactions.
235:             * @see EntityManagerFactoryInfo#getJpaDialect()
236:             * @see JpaDialect#beginTransaction
237:             * @see JpaDialect#getJdbcConnection
238:             */
239:            public void setJpaDialect(JpaDialect jpaDialect) {
240:                this .jpaDialect = (jpaDialect != null ? jpaDialect
241:                        : new DefaultJpaDialect());
242:            }
243:
244:            /**
245:             * Return the JPA dialect to use for this transaction manager.
246:             */
247:            public JpaDialect getJpaDialect() {
248:                return this .jpaDialect;
249:            }
250:
251:            /**
252:             * Eagerly initialize the JPA dialect, creating a default one
253:             * for the specified EntityManagerFactory if none set.
254:             * Auto-detect the EntityManagerFactory's DataSource, if any.
255:             */
256:            public void afterPropertiesSet() {
257:                if (getEntityManagerFactory() == null) {
258:                    throw new IllegalArgumentException(
259:                            "Property 'entityManagerFactory' is required");
260:                }
261:                if (getEntityManagerFactory() instanceof  EntityManagerFactoryInfo) {
262:                    EntityManagerFactoryInfo emfInfo = (EntityManagerFactoryInfo) getEntityManagerFactory();
263:                    DataSource dataSource = emfInfo.getDataSource();
264:                    if (dataSource != null) {
265:                        setDataSource(dataSource);
266:                    }
267:                    JpaDialect jpaDialect = emfInfo.getJpaDialect();
268:                    if (jpaDialect != null) {
269:                        setJpaDialect(jpaDialect);
270:                    }
271:                }
272:            }
273:
274:            public Object getResourceFactory() {
275:                return getEntityManagerFactory();
276:            }
277:
278:            protected Object doGetTransaction() {
279:                JpaTransactionObject txObject = new JpaTransactionObject();
280:                txObject.setSavepointAllowed(isNestedTransactionAllowed());
281:
282:                EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager
283:                        .getResource(getEntityManagerFactory());
284:                if (emHolder != null) {
285:                    if (logger.isDebugEnabled()) {
286:                        logger.debug("Found thread-bound EntityManager ["
287:                                + emHolder.getEntityManager()
288:                                + "] for JPA transaction");
289:                    }
290:                    txObject.setEntityManagerHolder(emHolder, false);
291:                }
292:
293:                if (getDataSource() != null) {
294:                    ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager
295:                            .getResource(getDataSource());
296:                    txObject.setConnectionHolder(conHolder);
297:                }
298:
299:                return txObject;
300:            }
301:
302:            protected boolean isExistingTransaction(Object transaction) {
303:                return ((JpaTransactionObject) transaction).hasTransaction();
304:            }
305:
306:            protected void doBegin(Object transaction,
307:                    TransactionDefinition definition) {
308:                JpaTransactionObject txObject = (JpaTransactionObject) transaction;
309:
310:                if (txObject.hasConnectionHolder()
311:                        && !txObject.getConnectionHolder()
312:                                .isSynchronizedWithTransaction()) {
313:                    throw new IllegalTransactionStateException(
314:                            "Pre-bound JDBC Connection found! JpaTransactionManager does not support "
315:                                    + "running within DataSourceTransactionManager if told to manage the DataSource itself. "
316:                                    + "It is recommended to use a single JpaTransactionManager for all transactions "
317:                                    + "on a single DataSource, no matter whether JPA or JDBC access.");
318:                }
319:
320:                EntityManager em = null;
321:
322:                try {
323:                    if (txObject.getEntityManagerHolder() == null
324:                            || txObject.getEntityManagerHolder()
325:                                    .isSynchronizedWithTransaction()) {
326:                        EntityManager newEm = createEntityManagerForTransaction();
327:                        if (logger.isDebugEnabled()) {
328:                            logger.debug("Opened new EntityManager [" + newEm
329:                                    + "] for JPA transaction");
330:                        }
331:                        txObject.setEntityManagerHolder(
332:                                new EntityManagerHolder(newEm), true);
333:                    }
334:
335:                    em = txObject.getEntityManagerHolder().getEntityManager();
336:
337:                    // Delegate to JpaDialect for actual transaction begin.
338:                    Object transactionData = getJpaDialect().beginTransaction(
339:                            em, definition);
340:                    txObject.setTransactionData(transactionData);
341:
342:                    // Register transaction timeout.
343:                    int timeout = determineTimeout(definition);
344:                    if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
345:                        txObject.getEntityManagerHolder().setTimeoutInSeconds(
346:                                timeout);
347:                    }
348:
349:                    // Register the JPA EntityManager's JDBC Connection for the DataSource, if set.
350:                    if (getDataSource() != null) {
351:                        ConnectionHandle conHandle = getJpaDialect()
352:                                .getJdbcConnection(em, definition.isReadOnly());
353:                        if (conHandle != null) {
354:                            ConnectionHolder conHolder = new ConnectionHolder(
355:                                    conHandle);
356:                            if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
357:                                conHolder.setTimeoutInSeconds(timeout);
358:                            }
359:                            if (logger.isDebugEnabled()) {
360:                                logger
361:                                        .debug("Exposing JPA transaction as JDBC transaction ["
362:                                                + conHolder
363:                                                        .getConnectionHandle()
364:                                                + "]");
365:                            }
366:                            TransactionSynchronizationManager.bindResource(
367:                                    getDataSource(), conHolder);
368:                            txObject.setConnectionHolder(conHolder);
369:                        } else {
370:                            if (logger.isDebugEnabled()) {
371:                                logger
372:                                        .debug("Not exposing JPA transaction ["
373:                                                + em
374:                                                + "] as JDBC transaction because JpaDialect ["
375:                                                + getJpaDialect()
376:                                                + "] does not support JDBC Connection retrieval");
377:                            }
378:                        }
379:                    }
380:
381:                    // Bind the entity manager holder to the thread.
382:                    if (txObject.isNewEntityManagerHolder()) {
383:                        TransactionSynchronizationManager.bindResource(
384:                                getEntityManagerFactory(), txObject
385:                                        .getEntityManagerHolder());
386:                    }
387:                    txObject.getEntityManagerHolder()
388:                            .setSynchronizedWithTransaction(true);
389:                }
390:
391:                catch (TransactionException ex) {
392:                    if (txObject.isNewEntityManagerHolder()) {
393:                        em.close();
394:                    }
395:                    throw ex;
396:                } catch (Exception ex) {
397:                    if (txObject.isNewEntityManagerHolder()) {
398:                        em.close();
399:                    }
400:                    throw new CannotCreateTransactionException(
401:                            "Could not open JPA EntityManager for transaction",
402:                            ex);
403:                }
404:            }
405:
406:            /**
407:             * Create a JPA EntityManager to be used for a transaction.
408:             * <p>The default implementation checks whether the EntityManagerFactory
409:             * is a Spring proxy and unwraps it first.
410:             * @see javax.persistence.EntityManagerFactory#createEntityManager()
411:             * @see EntityManagerFactoryInfo#getNativeEntityManagerFactory()
412:             */
413:            protected EntityManager createEntityManagerForTransaction() {
414:                EntityManagerFactory emf = getEntityManagerFactory();
415:                if (emf instanceof  EntityManagerFactoryInfo) {
416:                    emf = ((EntityManagerFactoryInfo) emf)
417:                            .getNativeEntityManagerFactory();
418:                }
419:                Map properties = getJpaPropertyMap();
420:                return (!CollectionUtils.isEmpty(properties) ? emf
421:                        .createEntityManager(properties) : emf
422:                        .createEntityManager());
423:            }
424:
425:            protected Object doSuspend(Object transaction) {
426:                JpaTransactionObject txObject = (JpaTransactionObject) transaction;
427:                txObject.setEntityManagerHolder(null, false);
428:                EntityManagerHolder entityManagerHolder = (EntityManagerHolder) TransactionSynchronizationManager
429:                        .unbindResource(getEntityManagerFactory());
430:                txObject.setConnectionHolder(null);
431:                ConnectionHolder connectionHolder = null;
432:                if (getDataSource() != null
433:                        && TransactionSynchronizationManager
434:                                .hasResource(getDataSource())) {
435:                    connectionHolder = (ConnectionHolder) TransactionSynchronizationManager
436:                            .unbindResource(getDataSource());
437:                }
438:                return new SuspendedResourcesHolder(entityManagerHolder,
439:                        connectionHolder);
440:            }
441:
442:            protected void doResume(Object transaction,
443:                    Object suspendedResources) {
444:                SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources;
445:                TransactionSynchronizationManager.bindResource(
446:                        getEntityManagerFactory(), resourcesHolder
447:                                .getEntityManagerHolder());
448:                if (getDataSource() != null
449:                        && resourcesHolder.getConnectionHolder() != null) {
450:                    TransactionSynchronizationManager.bindResource(
451:                            getDataSource(), resourcesHolder
452:                                    .getConnectionHolder());
453:                }
454:            }
455:
456:            /**
457:             * This implementation returns "true": a JPA commit will properly handle
458:             * transactions that have been marked rollback-only at a global level.
459:             */
460:            protected boolean shouldCommitOnGlobalRollbackOnly() {
461:                return true;
462:            }
463:
464:            protected void doCommit(DefaultTransactionStatus status) {
465:                JpaTransactionObject txObject = (JpaTransactionObject) status
466:                        .getTransaction();
467:                if (status.isDebug()) {
468:                    logger
469:                            .debug("Committing JPA transaction on EntityManager ["
470:                                    + txObject.getEntityManagerHolder()
471:                                            .getEntityManager() + "]");
472:                }
473:                try {
474:                    EntityTransaction tx = txObject.getEntityManagerHolder()
475:                            .getEntityManager().getTransaction();
476:                    tx.commit();
477:                } catch (RollbackException ex) {
478:                    if (ex.getCause() instanceof  RuntimeException) {
479:                        DataAccessException dex = getJpaDialect()
480:                                .translateExceptionIfPossible(
481:                                        (RuntimeException) ex.getCause());
482:                        if (dex != null) {
483:                            throw dex;
484:                        }
485:                    }
486:                    throw new TransactionSystemException(
487:                            "Could not commit JPA transaction", ex);
488:                } catch (RuntimeException ex) {
489:                    // Assumably failed to flush changes to database.
490:                    throw DataAccessUtils.translateIfNecessary(ex,
491:                            getJpaDialect());
492:                }
493:            }
494:
495:            protected void doRollback(DefaultTransactionStatus status) {
496:                JpaTransactionObject txObject = (JpaTransactionObject) status
497:                        .getTransaction();
498:                if (status.isDebug()) {
499:                    logger
500:                            .debug("Rolling back JPA transaction on EntityManager ["
501:                                    + txObject.getEntityManagerHolder()
502:                                            .getEntityManager() + "]");
503:                }
504:                try {
505:                    EntityTransaction tx = txObject.getEntityManagerHolder()
506:                            .getEntityManager().getTransaction();
507:                    if (tx.isActive()) {
508:                        tx.rollback();
509:                    }
510:                } catch (PersistenceException ex) {
511:                    throw new TransactionSystemException(
512:                            "Could not roll back JPA transaction", ex);
513:                } finally {
514:                    if (!txObject.isNewEntityManagerHolder()) {
515:                        // Clear all pending inserts/updates/deletes in the EntityManager.
516:                        // Necessary for pre-bound EntityManagers, to avoid inconsistent state.
517:                        txObject.getEntityManagerHolder().getEntityManager()
518:                                .clear();
519:                    }
520:                }
521:            }
522:
523:            protected void doSetRollbackOnly(DefaultTransactionStatus status) {
524:                JpaTransactionObject txObject = (JpaTransactionObject) status
525:                        .getTransaction();
526:                if (status.isDebug()) {
527:                    logger.debug("Setting JPA transaction on EntityManager ["
528:                            + txObject.getEntityManagerHolder()
529:                                    .getEntityManager() + "] rollback-only");
530:                }
531:                txObject.setRollbackOnly();
532:            }
533:
534:            protected void doCleanupAfterCompletion(Object transaction) {
535:                JpaTransactionObject txObject = (JpaTransactionObject) transaction;
536:
537:                // Remove the entity manager holder from the thread.
538:                if (txObject.isNewEntityManagerHolder()) {
539:                    TransactionSynchronizationManager
540:                            .unbindResource(getEntityManagerFactory());
541:                }
542:                txObject.getEntityManagerHolder().clear();
543:
544:                // Remove the JDBC connection holder from the thread, if exposed.
545:                if (txObject.hasConnectionHolder()) {
546:                    TransactionSynchronizationManager
547:                            .unbindResource(getDataSource());
548:                    try {
549:                        getJpaDialect().releaseJdbcConnection(
550:                                txObject.getConnectionHolder()
551:                                        .getConnectionHandle(),
552:                                txObject.getEntityManagerHolder()
553:                                        .getEntityManager());
554:                    } catch (Exception ex) {
555:                        // Just log it, to keep a transaction-related exception.
556:                        logger
557:                                .error(
558:                                        "Could not close JDBC connection after transaction",
559:                                        ex);
560:                    }
561:                }
562:
563:                getJpaDialect().cleanupTransaction(
564:                        txObject.getTransactionData());
565:
566:                // Remove the entity manager holder from the thread.
567:                if (txObject.isNewEntityManagerHolder()) {
568:                    EntityManager em = txObject.getEntityManagerHolder()
569:                            .getEntityManager();
570:                    if (logger.isDebugEnabled()) {
571:                        logger.debug("Closing JPA EntityManager [" + em
572:                                + "] after transaction");
573:                    }
574:                    em.close();
575:                } else {
576:                    logger
577:                            .debug("Not closing pre-bound JPA EntityManager after transaction");
578:                }
579:            }
580:
581:            /**
582:             * JPA transaction object, representing a EntityManagerHolder.
583:             * Used as transaction object by JpaTransactionManager.
584:             */
585:            private static class JpaTransactionObject extends
586:                    JdbcTransactionObjectSupport {
587:
588:                private EntityManagerHolder entityManagerHolder;
589:
590:                private boolean newEntityManagerHolder;
591:
592:                private Object transactionData;
593:
594:                public void setEntityManagerHolder(
595:                        EntityManagerHolder entityManagerHolder,
596:                        boolean newEntityManagerHolder) {
597:                    this .entityManagerHolder = entityManagerHolder;
598:                    this .newEntityManagerHolder = newEntityManagerHolder;
599:                }
600:
601:                public EntityManagerHolder getEntityManagerHolder() {
602:                    return this .entityManagerHolder;
603:                }
604:
605:                public boolean isNewEntityManagerHolder() {
606:                    return this .newEntityManagerHolder;
607:                }
608:
609:                public boolean hasTransaction() {
610:                    return (this .entityManagerHolder != null
611:                            && this .entityManagerHolder.getEntityManager() != null && (this .entityManagerHolder
612:                            .getEntityManager().getTransaction().isActive() || this .entityManagerHolder
613:                            .isSynchronizedWithTransaction()));
614:                }
615:
616:                public void setTransactionData(Object transactionData) {
617:                    this .transactionData = transactionData;
618:                    if (transactionData instanceof  SavepointManager) {
619:                        this .entityManagerHolder
620:                                .setSavepointManager((SavepointManager) transactionData);
621:                    }
622:                }
623:
624:                public Object getTransactionData() {
625:                    return this .transactionData;
626:                }
627:
628:                public void setRollbackOnly() {
629:                    EntityTransaction tx = this .entityManagerHolder
630:                            .getEntityManager().getTransaction();
631:                    if (tx.isActive()) {
632:                        tx.setRollbackOnly();
633:                    }
634:                    if (hasConnectionHolder()) {
635:                        getConnectionHolder().setRollbackOnly();
636:                    }
637:                }
638:
639:                public boolean isRollbackOnly() {
640:                    EntityTransaction tx = this .entityManagerHolder
641:                            .getEntityManager().getTransaction();
642:                    return tx.getRollbackOnly();
643:                }
644:
645:                public Object createSavepoint() throws TransactionException {
646:                    return getSavepointManager().createSavepoint();
647:                }
648:
649:                public void rollbackToSavepoint(Object savepoint)
650:                        throws TransactionException {
651:                    getSavepointManager().rollbackToSavepoint(savepoint);
652:                }
653:
654:                public void releaseSavepoint(Object savepoint)
655:                        throws TransactionException {
656:                    getSavepointManager().releaseSavepoint(savepoint);
657:                }
658:
659:                private SavepointManager getSavepointManager() {
660:                    if (!isSavepointAllowed()) {
661:                        throw new NestedTransactionNotSupportedException(
662:                                "Transaction manager does not allow nested transactions");
663:                    }
664:                    SavepointManager savepointManager = getEntityManagerHolder()
665:                            .getSavepointManager();
666:                    if (savepointManager == null) {
667:                        throw new NestedTransactionNotSupportedException(
668:                                "JpaDialect does not support savepoints - check your JPA provider's capabilities");
669:                    }
670:                    return savepointManager;
671:                }
672:            }
673:
674:            /**
675:             * Holder for suspended resources.
676:             * Used internally by <code>doSuspend</code> and <code>doResume</code>.
677:             */
678:            private static class SuspendedResourcesHolder {
679:
680:                private final EntityManagerHolder entityManagerHolder;
681:
682:                private final ConnectionHolder connectionHolder;
683:
684:                private SuspendedResourcesHolder(EntityManagerHolder emHolder,
685:                        ConnectionHolder conHolder) {
686:                    this .entityManagerHolder = emHolder;
687:                    this .connectionHolder = conHolder;
688:                }
689:
690:                private EntityManagerHolder getEntityManagerHolder() {
691:                    return this .entityManagerHolder;
692:                }
693:
694:                private ConnectionHolder getConnectionHolder() {
695:                    return this.connectionHolder;
696:                }
697:            }
698:
699:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.