KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > orm > jpa > JpaTransactionManager


1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.orm.jpa;
18
19 import java.util.HashMap JavaDoc;
20 import java.util.Map JavaDoc;
21 import java.util.Properties JavaDoc;
22
23 import javax.persistence.EntityManager;
24 import javax.persistence.EntityManagerFactory;
25 import javax.persistence.EntityTransaction;
26 import javax.persistence.PersistenceException;
27 import javax.persistence.RollbackException;
28 import javax.sql.DataSource JavaDoc;
29
30 import org.springframework.beans.factory.InitializingBean;
31 import org.springframework.dao.support.DataAccessUtils;
32 import org.springframework.jdbc.datasource.ConnectionHandle;
33 import org.springframework.jdbc.datasource.ConnectionHolder;
34 import org.springframework.jdbc.datasource.JdbcTransactionObjectSupport;
35 import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
36 import org.springframework.transaction.CannotCreateTransactionException;
37 import org.springframework.transaction.IllegalTransactionStateException;
38 import org.springframework.transaction.TransactionDefinition;
39 import org.springframework.transaction.TransactionException;
40 import org.springframework.transaction.TransactionSystemException;
41 import org.springframework.transaction.UnexpectedRollbackException;
42 import org.springframework.transaction.support.AbstractPlatformTransactionManager;
43 import org.springframework.transaction.support.DefaultTransactionStatus;
44 import org.springframework.transaction.support.TransactionSynchronizationManager;
45 import org.springframework.transaction.support.ResourceTransactionManager;
46 import org.springframework.util.CollectionUtils;
47
48 /**
49  * {@link org.springframework.transaction.PlatformTransactionManager} implementation
50  * for a single JPA {@link javax.persistence.EntityManagerFactory}. Binds a JPA
51  * EntityManager from the specified factory to the thread, potentially allowing for
52  * one thread-bound EntityManager per factory. {@link SharedEntityManagerCreator}
53  * and {@link JpaTemplate} are aware of thread-bound entity managers and participate
54  * in such transactions automatically. Using either is required for JPA access code
55  * supporting this transaction management mechanism.
56  *
57  * <p>This transaction manager is appropriate for applications that use a single
58  * JPA EntityManagerFactory for transactional data access. JTA (usually through
59  * {@link org.springframework.transaction.jta.JtaTransactionManager}) is necessary
60  * for accessing multiple transactional resources within the same transaction.
61  * Note that you need to configure your JPA provider accordingly in order to make
62  * it participate in JTA transactions.
63  *
64  * <p>This transaction manager also supports direct DataSource access within a
65  * transaction (i.e. plain JDBC code working with the same DataSource).
66  * This allows for mixing services which access JPA and services which use plain
67  * JDBC (without being aware of JPA)! Application code needs to stick to the
68  * same simple Connection lookup pattern as with
69  * {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}
70  * (i.e. {@link org.springframework.jdbc.datasource.DataSourceUtils#getConnection}
71  * or going through a
72  * {@link org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy}).
73  * Note that this requires a vendor-specific {@link JpaDialect} to be configured.
74  *
75  * <p>Note: To be able to register a DataSource's Connection for plain JDBC code,
76  * this instance needs to be aware of the DataSource ({@link #setDataSource}).
77  * The given DataSource should obviously match the one used by the given
78  * EntityManagerFactory. This transaction manager will autodetect the DataSource
79  * used as known connection factory of the EntityManagerFactory, so you usually
80  * don't need to explicitly specify the "dataSource" property.
81  *
82  * <p>On JDBC 3.0, this transaction manager supports nested transactions via JDBC 3.0
83  * Savepoints. The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"}
84  * flag defaults to "false", though, as nested transactions will just apply to the
85  * JDBC Connection, not to the JPA EntityManager and its cached objects.
86  * You can manually set the flag to "true" if you want to use nested transactions
87  * for JDBC access code which participates in JPA transactions (provided that your
88  * JDBC driver supports Savepoints). <i>Note that JPA itself does not support
89  * nested transactions! Hence, do not expect JPA access code to semantically
90  * participate in a nested transaction.</i>
91  *
92  * @author Juergen Hoeller
93  * @since 2.0
94  * @see #setEntityManagerFactory
95  * @see #setDataSource
96  * @see LocalEntityManagerFactoryBean
97  * @see JpaTemplate#execute
98  * @see org.springframework.orm.jpa.support.SharedEntityManagerBean
99  * @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection
100  * @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
101  * @see org.springframework.jdbc.core.JdbcTemplate
102  * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
103  * @see org.springframework.transaction.jta.JtaTransactionManager
104  */

105 public class JpaTransactionManager extends AbstractPlatformTransactionManager
106         implements ResourceTransactionManager, InitializingBean {
107
108     private EntityManagerFactory entityManagerFactory;
109
110     private final Map JavaDoc jpaPropertyMap = new HashMap JavaDoc();
111
112     private DataSource JavaDoc dataSource;
113
114     private JpaDialect jpaDialect = new DefaultJpaDialect();
115
116
117     /**
118      * Create a new JpaTransactionManager instance.
119      * A EntityManagerFactory has to be set to be able to use it.
120      * @see #setEntityManagerFactory
121      */

122     public JpaTransactionManager() {
123     }
124
125     /**
126      * Create a new JpaTransactionManager instance.
127      * @param emf EntityManagerFactory to manage transactions for
128      */

129     public JpaTransactionManager(EntityManagerFactory emf) {
130         this.entityManagerFactory = emf;
131         afterPropertiesSet();
132     }
133
134     /**
135      * Set the EntityManagerFactory that this instance should manage transactions for.
136      */

137     public void setEntityManagerFactory(EntityManagerFactory emf) {
138         this.entityManagerFactory = emf;
139     }
140
141     /**
142      * Return the EntityManagerFactory that this instance should manage transactions for.
143      */

144     public EntityManagerFactory getEntityManagerFactory() {
145         return this.entityManagerFactory;
146     }
147
148     /**
149      * Specify JPA properties, to be passed into
150      * <code>EntityManagerFactory.createEntityManager(Map)</code> (if any).
151      * <p>Can be populated with a String "value" (parsed via PropertiesEditor)
152      * or a "props" element in XML bean definitions.
153      * @see javax.persistence.EntityManagerFactory#createEntityManager(java.util.Map)
154      */

155     public void setJpaProperties(Properties JavaDoc jpaProperties) {
156         CollectionUtils.mergePropertiesIntoMap(jpaProperties, this.jpaPropertyMap);
157     }
158
159     /**
160      * Specify JPA properties as a Map, to be passed into
161      * <code>EntityManagerFactory.createEntityManager(Map)</code> (if any).
162      * <p>Can be populated with a "map" or "props" element in XML bean definitions.
163      * @see javax.persistence.EntityManagerFactory#createEntityManager(java.util.Map)
164      */

165     public void setJpaPropertyMap(Map JavaDoc jpaProperties) {
166         if (jpaProperties != null) {
167             this.jpaPropertyMap.putAll(jpaProperties);
168         }
169     }
170
171     /**
172      * Allow Map access to the JPA properties to be passed to the persistence
173      * provider, with the option to add or override specific entries.
174      * <p>Useful for specifying entries directly, for example via "jpaPropertyMap[myKey]".
175      */

176     public Map JavaDoc getJpaPropertyMap() {
177         return this.jpaPropertyMap;
178     }
179
180     /**
181      * Set the JDBC DataSource that this instance should manage transactions for.
182    * The DataSource should match the one used by the JPA EntityManagerFactory:
183      * for example, you could specify the same JNDI DataSource for both.
184      * <p>If the EntityManagerFactory uses a known DataSource as connection factory,
185      * the DataSource will be autodetected: You can still explictly specify the
186      * DataSource, but you don't need to in this case.
187      * <p>A transactional JDBC Connection for this DataSource will be provided to
188      * application code accessing this DataSource directly via DataSourceUtils
189      * or JdbcTemplate. The Connection will be taken from the JPA EntityManager.
190      * <p>Note that you need to use a JPA dialect for a specific JPA implementation
191      * to allow for exposing JPA transactions as JDBC transactions.
192      * <p>The DataSource specified here should be the target DataSource to manage
193      * transactions for, not a TransactionAwareDataSourceProxy. Only data access
194      * code may work with TransactionAwareDataSourceProxy, while the transaction
195      * manager needs to work on the underlying target DataSource. If there's
196      * nevertheless a TransactionAwareDataSourceProxy passed in, it will be
197      * unwrapped to extract its target DataSource.
198      * @see EntityManagerFactoryInfo#getDataSource()
199      * @see #setJpaDialect
200      * @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
201      * @see org.springframework.jdbc.datasource.DataSourceUtils
202      * @see org.springframework.jdbc.core.JdbcTemplate
203      */

204     public void setDataSource(DataSource JavaDoc dataSource) {
205         if (dataSource instanceof TransactionAwareDataSourceProxy) {
206             // If we got a TransactionAwareDataSourceProxy, we need to perform transactions
207
// for its underlying target DataSource, else data access code won't see
208
// properly exposed transactions (i.e. transactions for the target DataSource).
209
this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
210         }
211         else {
212             this.dataSource = dataSource;
213         }
214     }
215
216     /**
217      * Return the JDBC DataSource that this instance manages transactions for.
218      */

219     public DataSource JavaDoc getDataSource() {
220         return this.dataSource;
221     }
222
223     /**
224      * Set the JPA dialect to use for this transaction manager.
225      * Used for vendor-specific transaction management and JDBC connection exposure.
226      * <p>If the EntityManagerFactory uses a known JpaDialect, it will be autodetected:
227      * You can still explictly specify the DataSource, but you don't need to in this case.
228      * <p>The dialect object can be used to retrieve the underlying JDBC connection
229      * and thus allows for exposing JPA transactions as JDBC transactions.
230      * @see EntityManagerFactoryInfo#getJpaDialect()
231      * @see JpaDialect#beginTransaction
232      * @see JpaDialect#getJdbcConnection
233      */

234     public void setJpaDialect(JpaDialect jpaDialect) {
235         this.jpaDialect = (jpaDialect != null ? jpaDialect : new DefaultJpaDialect());
236     }
237
238     /**
239      * Return the JPA dialect to use for this transaction manager.
240      */

241     public JpaDialect getJpaDialect() {
242         return this.jpaDialect;
243     }
244
245     /**
246      * Eagerly initialize the JPA dialect, creating a default one
247      * for the specified EntityManagerFactory if none set.
248      * Auto-detect the EntityManagerFactory's DataSource, if any.
249      */

250     public void afterPropertiesSet() {
251         if (getEntityManagerFactory() == null) {
252             throw new IllegalArgumentException JavaDoc("entityManagerFactory is required");
253         }
254         if (getEntityManagerFactory() instanceof EntityManagerFactoryInfo) {
255             EntityManagerFactoryInfo emfInfo = (EntityManagerFactoryInfo) getEntityManagerFactory();
256             DataSource JavaDoc dataSource = emfInfo.getDataSource();
257             if (dataSource != null) {
258                 setDataSource(dataSource);
259             }
260             JpaDialect jpaDialect = emfInfo.getJpaDialect();
261             if (jpaDialect != null) {
262                 setJpaDialect(jpaDialect);
263             }
264         }
265     }
266
267
268     public Object JavaDoc getResourceFactory() {
269         return getEntityManagerFactory();
270     }
271
272     protected Object JavaDoc doGetTransaction() {
273         JpaTransactionObject txObject = new JpaTransactionObject();
274         txObject.setSavepointAllowed(isNestedTransactionAllowed());
275
276         EntityManagerHolder emHolder = (EntityManagerHolder)
277                 TransactionSynchronizationManager.getResource(getEntityManagerFactory());
278         if (emHolder != null) {
279             if (logger.isDebugEnabled()) {
280                 logger.debug("Found thread-bound EntityManager [" +
281                         emHolder.getEntityManager() + "] for JPA transaction");
282             }
283             txObject.setEntityManagerHolder(emHolder, false);
284         }
285
286         if (getDataSource() != null) {
287             ConnectionHolder conHolder = (ConnectionHolder)
288                     TransactionSynchronizationManager.getResource(getDataSource());
289             txObject.setConnectionHolder(conHolder);
290         }
291
292         return txObject;
293     }
294
295     protected boolean isExistingTransaction(Object JavaDoc transaction) {
296         return ((JpaTransactionObject) transaction).hasTransaction();
297     }
298
299     protected void doBegin(Object JavaDoc transaction, TransactionDefinition definition) {
300         JpaTransactionObject txObject = (JpaTransactionObject) transaction;
301
302         if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
303             throw new IllegalTransactionStateException(
304                     "Pre-bound JDBC Connection found! JpaTransactionManager does not support " +
305                     "running within DataSourceTransactionManager if told to manage the DataSource itself. " +
306                     "It is recommended to use a single JpaTransactionManager for all transactions " +
307                     "on a single DataSource, no matter whether JPA or JDBC access.");
308         }
309
310         EntityManager em = null;
311
312         try {
313             if (txObject.getEntityManagerHolder() == null ||
314                     txObject.getEntityManagerHolder().isSynchronizedWithTransaction()) {
315                 EntityManager newEm = createEntityManagerForTransaction();
316                 if (logger.isDebugEnabled()) {
317                     logger.debug("Opened new EntityManager [" + newEm + "] for JPA transaction");
318                 }
319                 txObject.setEntityManagerHolder(new EntityManagerHolder(newEm), true);
320             }
321
322             txObject.getEntityManagerHolder().setSynchronizedWithTransaction(true);
323             em = txObject.getEntityManagerHolder().getEntityManager();
324
325             // Delegate to JpaDialect for actual transaction begin.
326
Object JavaDoc transactionData = getJpaDialect().beginTransaction(em, definition);
327             txObject.setTransactionData(transactionData);
328
329             // Register transaction timeout.
330
int timeout = determineTimeout(definition);
331             if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
332                 txObject.getEntityManagerHolder().setTimeoutInSeconds(timeout);
333             }
334
335             // Register the JPA EntityManager's JDBC Connection for the DataSource, if set.
336
if (getDataSource() != null) {
337                 ConnectionHandle conHandle = getJpaDialect().getJdbcConnection(em, definition.isReadOnly());
338                 if (conHandle != null) {
339                     ConnectionHolder conHolder = new ConnectionHolder(conHandle);
340                     if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
341                         conHolder.setTimeoutInSeconds(timeout);
342                     }
343                     if (logger.isDebugEnabled()) {
344                         logger.debug("Exposing JPA transaction as JDBC transaction [" + conHolder.getConnectionHandle() + "]");
345                     }
346                     TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
347                     txObject.setConnectionHolder(conHolder);
348                 }
349                 else {
350                     if (logger.isDebugEnabled()) {
351                         logger.debug("Not exposing JPA transaction [" + em + "] as JDBC transaction because JpaDialect [" +
352                                 getJpaDialect() + "] does not support JDBC Connection retrieval");
353                     }
354                 }
355             }
356
357             // Bind the entity manager holder to the thread.
358
if (txObject.isNewEntityManagerHolder()) {
359                 TransactionSynchronizationManager.bindResource(
360                         getEntityManagerFactory(), txObject.getEntityManagerHolder());
361             }
362         }
363
364         catch (TransactionException ex) {
365             if (em != null) {
366                 em.close();
367             }
368             throw ex;
369         }
370         catch (Exception JavaDoc ex) {
371             if (em != null) {
372                 em.close();
373             }
374             throw new CannotCreateTransactionException("Could not open JPA EntityManager for transaction", ex);
375         }
376     }
377
378     /**
379      * Create a JPA EntityManager to be used for a transaction.
380      * <p>The default implementation checks whether the EntityManagerFactory
381      * is a Spring proxy and unwraps it first.
382      * @see javax.persistence.EntityManagerFactory#createEntityManager()
383      * @see EntityManagerFactoryInfo#getNativeEntityManagerFactory()
384      */

385     protected EntityManager createEntityManagerForTransaction() {
386         EntityManagerFactory emf = getEntityManagerFactory();
387         if (emf instanceof EntityManagerFactoryInfo) {
388             emf = ((EntityManagerFactoryInfo) emf).getNativeEntityManagerFactory();
389         }
390         Map JavaDoc properties = getJpaPropertyMap();
391         return (!CollectionUtils.isEmpty(properties) ?
392                 emf.createEntityManager(properties) : emf.createEntityManager());
393     }
394
395     protected Object JavaDoc doSuspend(Object JavaDoc transaction) {
396         JpaTransactionObject txObject = (JpaTransactionObject) transaction;
397         txObject.setEntityManagerHolder(null, false);
398         EntityManagerHolder entityManagerHolder = (EntityManagerHolder)
399                 TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
400         txObject.setConnectionHolder(null);
401         ConnectionHolder connectionHolder = null;
402         if (getDataSource() != null) {
403             connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.unbindResource(getDataSource());
404         }
405         return new SuspendedResourcesHolder(entityManagerHolder, connectionHolder);
406     }
407
408     protected void doResume(Object JavaDoc transaction, Object JavaDoc suspendedResources) {
409         SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources;
410         TransactionSynchronizationManager.bindResource(
411                 getEntityManagerFactory(), resourcesHolder.getEntityManagerHolder());
412         if (getDataSource() != null) {
413             TransactionSynchronizationManager.bindResource(getDataSource(), resourcesHolder.getConnectionHolder());
414         }
415     }
416
417     /**
418      * This implementation returns "true": a JPA commit will properly handle
419      * transactions that have been marked rollback-only at a global level.
420      */

421     protected boolean shouldCommitOnGlobalRollbackOnly() {
422         return true;
423     }
424
425     protected void doCommit(DefaultTransactionStatus status) {
426         JpaTransactionObject txObject = (JpaTransactionObject) status.getTransaction();
427         if (status.isDebug()) {
428             logger.debug("Committing JPA transaction on EntityManager [" +
429                     txObject.getEntityManagerHolder().getEntityManager() + "]");
430         }
431         try {
432             EntityTransaction tx = txObject.getEntityManagerHolder().getEntityManager().getTransaction();
433             tx.commit();
434         }
435         catch (RollbackException ex) {
436             throw new UnexpectedRollbackException(
437                     "JPA transaction unexpectedly rolled back (maybe marked rollback-only after a failed operation)", ex);
438         }
439         catch (RuntimeException JavaDoc rawException) {
440             // Assumably failed to flush changes to database.
441
throw DataAccessUtils.translateIfNecessary(rawException, getJpaDialect());
442         }
443     }
444
445     protected void doRollback(DefaultTransactionStatus status) {
446         JpaTransactionObject txObject = (JpaTransactionObject) status.getTransaction();
447         if (status.isDebug()) {
448             logger.debug("Rolling back JPA transaction on EntityManager [" +
449                     txObject.getEntityManagerHolder().getEntityManager() + "]");
450         }
451         try {
452             EntityTransaction tx = txObject.getEntityManagerHolder().getEntityManager().getTransaction();
453             if (tx.isActive()) {
454                 tx.rollback();
455             }
456         }
457         catch (PersistenceException ex) {
458             throw new TransactionSystemException("Could not roll back JPA transaction", ex);
459         }
460         finally {
461             if (!txObject.isNewEntityManagerHolder()) {
462                 // Clear all pending inserts/updates/deletes in the EntityManager.
463
// Necessary for pre-bound EntityManagers, to avoid inconsistent state.
464
txObject.getEntityManagerHolder().getEntityManager().clear();
465             }
466         }
467     }
468
469     protected void doSetRollbackOnly(DefaultTransactionStatus status) {
470         JpaTransactionObject txObject = (JpaTransactionObject) status.getTransaction();
471         if (status.isDebug()) {
472             logger.debug("Setting JPA transaction on EntityManager [" +
473                     txObject.getEntityManagerHolder().getEntityManager() + "] rollback-only");
474         }
475         txObject.setRollbackOnly();
476     }
477
478     protected void doCleanupAfterCompletion(Object JavaDoc transaction) {
479         JpaTransactionObject txObject = (JpaTransactionObject) transaction;
480
481         // Remove the entity manager holder from the thread.
482
if (txObject.isNewEntityManagerHolder()) {
483             TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
484         }
485         txObject.getEntityManagerHolder().clear();
486
487         // Remove the JDBC connection holder from the thread, if exposed.
488
if (txObject.hasConnectionHolder()) {
489             TransactionSynchronizationManager.unbindResource(getDataSource());
490             try {
491                 getJpaDialect().releaseJdbcConnection(txObject.getConnectionHolder().getConnectionHandle(),
492                         txObject.getEntityManagerHolder().getEntityManager());
493             }
494             catch (Exception JavaDoc ex) {
495                 // Just log it, to keep a transaction-related exception.
496
logger.error("Could not close JDBC connection after transaction", ex);
497             }
498         }
499
500         getJpaDialect().cleanupTransaction(txObject.getTransactionData());
501
502         // Remove the entity manager holder from the thread.
503
if (txObject.isNewEntityManagerHolder()) {
504             EntityManager em = txObject.getEntityManagerHolder().getEntityManager();
505             if (logger.isDebugEnabled()) {
506                 logger.debug("Closing JPA EntityManager [" + em + "] after transaction");
507             }
508             em.close();
509         }
510         else {
511             logger.debug("Not closing pre-bound JPA EntityManager after transaction");
512         }
513     }
514
515
516     /**
517      * JPA transaction object, representing a EntityManagerHolder.
518      * Used as transaction object by JpaTransactionManager.
519      *
520      * <p>Derives from JdbcTransactionObjectSupport in order to inherit the
521      * capability to manage JDBC 3.0 Savepoints for underlying JDBC Connections.
522      *
523      * @see EntityManagerHolder
524      */

525     private static class JpaTransactionObject extends JdbcTransactionObjectSupport {
526
527         private EntityManagerHolder entityManagerHolder;
528
529         private boolean newEntityManagerHolder;
530
531         private Object JavaDoc transactionData;
532
533         public void setEntityManagerHolder(
534                 EntityManagerHolder entityManagerHolder, boolean newEntityManagerHolder) {
535             this.entityManagerHolder = entityManagerHolder;
536             this.newEntityManagerHolder = newEntityManagerHolder;
537         }
538
539         public EntityManagerHolder getEntityManagerHolder() {
540             return this.entityManagerHolder;
541         }
542
543         public boolean isNewEntityManagerHolder() {
544             return this.newEntityManagerHolder;
545         }
546
547         public boolean hasTransaction() {
548             return (this.entityManagerHolder != null &&
549                     this.entityManagerHolder.getEntityManager() != null &&
550                     this.entityManagerHolder.getEntityManager().getTransaction().isActive());
551         }
552
553         public void setTransactionData(Object JavaDoc transactionData) {
554             this.transactionData = transactionData;
555         }
556
557         public Object JavaDoc getTransactionData() {
558             return this.transactionData;
559         }
560
561         public void setRollbackOnly() {
562             EntityTransaction tx = this.entityManagerHolder.getEntityManager().getTransaction();
563             if (tx.isActive()) {
564                 tx.setRollbackOnly();
565             }
566             if (hasConnectionHolder()) {
567                 getConnectionHolder().setRollbackOnly();
568             }
569         }
570
571         public boolean isRollbackOnly() {
572             EntityTransaction tx = this.entityManagerHolder.getEntityManager().getTransaction();
573             return tx.getRollbackOnly();
574         }
575     }
576
577
578     /**
579      * Holder for suspended resources.
580      * Used internally by <code>doSuspend</code> and <code>doResume</code>.
581      */

582     private static class SuspendedResourcesHolder {
583
584         private final EntityManagerHolder entityManagerHolder;
585
586         private final ConnectionHolder connectionHolder;
587
588         private SuspendedResourcesHolder(EntityManagerHolder emHolder, ConnectionHolder conHolder) {
589             this.entityManagerHolder = emHolder;
590             this.connectionHolder = conHolder;
591         }
592
593         private EntityManagerHolder getEntityManagerHolder() {
594             return this.entityManagerHolder;
595         }
596
597         private ConnectionHolder getConnectionHolder() {
598             return this.connectionHolder;
599         }
600     }
601
602 }
603
Popular Tags