001/* 002 * Copyright 2002-2014 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 * https://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 017package org.springframework.orm.jdo; 018 019import javax.jdo.JDOException; 020import javax.jdo.PersistenceManager; 021import javax.jdo.PersistenceManagerFactory; 022import javax.jdo.Transaction; 023import javax.sql.DataSource; 024 025import org.springframework.beans.factory.InitializingBean; 026import org.springframework.dao.DataAccessException; 027import org.springframework.jdbc.datasource.ConnectionHandle; 028import org.springframework.jdbc.datasource.ConnectionHolder; 029import org.springframework.jdbc.datasource.JdbcTransactionObjectSupport; 030import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; 031import org.springframework.transaction.CannotCreateTransactionException; 032import org.springframework.transaction.IllegalTransactionStateException; 033import org.springframework.transaction.TransactionDefinition; 034import org.springframework.transaction.TransactionException; 035import org.springframework.transaction.TransactionSystemException; 036import org.springframework.transaction.support.AbstractPlatformTransactionManager; 037import org.springframework.transaction.support.DefaultTransactionStatus; 038import org.springframework.transaction.support.DelegatingTransactionDefinition; 039import org.springframework.transaction.support.ResourceTransactionManager; 040import org.springframework.transaction.support.TransactionSynchronizationManager; 041 042/** 043 * {@link org.springframework.transaction.PlatformTransactionManager} implementation for a 044 * single JDO {@link javax.jdo.PersistenceManagerFactory}. Binds a JDO PersistenceManager 045 * from the specified factory to the thread, potentially allowing for one thread-bound 046 * PersistenceManager per factory. {@link PersistenceManagerFactoryUtils} and 047 * {@link org.springframework.orm.jdo.support.SpringPersistenceManagerProxyBean} are aware 048 * of thread-bound persistence managers and participate in such transactions automatically. 049 * Using either of those (or going through a {@link TransactionAwarePersistenceManagerFactoryProxy} 050 * is required for JDO access code supporting this transaction management mechanism. 051 * 052 * <p>This transaction manager is appropriate for applications that use a single 053 * JDO PersistenceManagerFactory for transactional data access. JTA (usually through 054 * {@link org.springframework.transaction.jta.JtaTransactionManager}) is necessary 055 * for accessing multiple transactional resources within the same transaction. 056 * Note that you need to configure your JDO provider accordingly in order to make 057 * it participate in JTA transactions. 058 * 059 * <p>This transaction manager also supports direct DataSource access within a 060 * transaction (i.e. plain JDBC code working with the same DataSource). 061 * This allows for mixing services which access JDO and services which use plain 062 * JDBC (without being aware of JDO)! Application code needs to stick to the 063 * same simple Connection lookup pattern as with 064 * {@link org.springframework.jdbc.datasource.DataSourceTransactionManager} 065 * (i.e. {@link org.springframework.jdbc.datasource.DataSourceUtils#getConnection} 066 * or going through a 067 * {@link org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy}). 068 * 069 * <p>Note: To be able to register a DataSource's Connection for plain JDBC code, 070 * this instance needs to be aware of the DataSource ({@link #setDataSource}). 071 * The given DataSource should obviously match the one used by the given 072 * PersistenceManagerFactory. This transaction manager will autodetect the DataSource 073 * that acts as "connectionFactory" of the PersistenceManagerFactory, so you usually 074 * don't need to explicitly specify the "dataSource" property. 075 * 076 * <p>This transaction manager supports nested transactions via JDBC 3.0 Savepoints. 077 * The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"} flag defaults 078 * to "false", though, as nested transactions will just apply to the JDBC Connection, 079 * not to the JDO PersistenceManager and its cached entity objects and related context. 080 * You can manually set the flag to "true" if you want to use nested transactions 081 * for JDBC access code which participates in JDO transactions (provided that your 082 * JDBC driver supports Savepoints). <i>Note that JDO itself does not support 083 * nested transactions! Hence, do not expect JDO access code to semantically 084 * participate in a nested transaction.</i> 085 * 086 * @author Juergen Hoeller 087 * @since 03.06.2003 088 * @see #setPersistenceManagerFactory 089 * @see #setDataSource 090 * @see javax.jdo.PersistenceManagerFactory#getConnectionFactory 091 * @see LocalPersistenceManagerFactoryBean 092 * @see PersistenceManagerFactoryUtils#getPersistenceManager 093 * @see PersistenceManagerFactoryUtils#releasePersistenceManager 094 * @see TransactionAwarePersistenceManagerFactoryProxy 095 * @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection 096 * @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection 097 * @see org.springframework.jdbc.core.JdbcTemplate 098 * @see org.springframework.jdbc.datasource.DataSourceTransactionManager 099 * @see org.springframework.transaction.jta.JtaTransactionManager 100 */ 101@SuppressWarnings("serial") 102public class JdoTransactionManager extends AbstractPlatformTransactionManager 103 implements ResourceTransactionManager, InitializingBean { 104 105 private PersistenceManagerFactory persistenceManagerFactory; 106 107 private DataSource dataSource; 108 109 private boolean autodetectDataSource = true; 110 111 private JdoDialect jdoDialect; 112 113 114 /** 115 * Create a new JdoTransactionManager instance. 116 * A PersistenceManagerFactory has to be set to be able to use it. 117 * @see #setPersistenceManagerFactory 118 */ 119 public JdoTransactionManager() { 120 } 121 122 /** 123 * Create a new JdoTransactionManager instance. 124 * @param pmf PersistenceManagerFactory to manage transactions for 125 */ 126 public JdoTransactionManager(PersistenceManagerFactory pmf) { 127 this.persistenceManagerFactory = pmf; 128 afterPropertiesSet(); 129 } 130 131 132 /** 133 * Set the PersistenceManagerFactory that this instance should manage transactions for. 134 * <p>The PersistenceManagerFactory specified here should be the target 135 * PersistenceManagerFactory to manage transactions for, not a 136 * TransactionAwarePersistenceManagerFactoryProxy. Only data access 137 * code may work with TransactionAwarePersistenceManagerFactoryProxy, while the 138 * transaction manager needs to work on the underlying target PersistenceManagerFactory. 139 * @see TransactionAwarePersistenceManagerFactoryProxy 140 */ 141 public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) { 142 this.persistenceManagerFactory = pmf; 143 } 144 145 /** 146 * Return the PersistenceManagerFactory that this instance should manage transactions for. 147 */ 148 public PersistenceManagerFactory getPersistenceManagerFactory() { 149 return this.persistenceManagerFactory; 150 } 151 152 /** 153 * Set the JDBC DataSource that this instance should manage transactions for. 154 * The DataSource should match the one used by the JDO PersistenceManagerFactory: 155 * for example, you could specify the same JNDI DataSource for both. 156 * <p>If the PersistenceManagerFactory uses a DataSource as connection factory, 157 * the DataSource will be autodetected: You can still explicitly specify the 158 * DataSource, but you don't need to in this case. 159 * <p>A transactional JDBC Connection for this DataSource will be provided to 160 * application code accessing this DataSource directly via DataSourceUtils 161 * or JdbcTemplate. The Connection will be taken from the JDO PersistenceManager. 162 * <p>Note that you need to use a JDO dialect for a specific JDO provider to 163 * allow for exposing JDO transactions as JDBC transactions. 164 * <p>The DataSource specified here should be the target DataSource to manage 165 * transactions for, not a TransactionAwareDataSourceProxy. Only data access 166 * code may work with TransactionAwareDataSourceProxy, while the transaction 167 * manager needs to work on the underlying target DataSource. If there's 168 * nevertheless a TransactionAwareDataSourceProxy passed in, it will be 169 * unwrapped to extract its target DataSource. 170 * @see #setAutodetectDataSource 171 * @see #setJdoDialect 172 * @see javax.jdo.PersistenceManagerFactory#getConnectionFactory 173 * @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy 174 * @see org.springframework.jdbc.datasource.DataSourceUtils 175 * @see org.springframework.jdbc.core.JdbcTemplate 176 */ 177 public void setDataSource(DataSource dataSource) { 178 if (dataSource instanceof TransactionAwareDataSourceProxy) { 179 // If we got a TransactionAwareDataSourceProxy, we need to perform transactions 180 // for its underlying target DataSource, else data access code won't see 181 // properly exposed transactions (i.e. transactions for the target DataSource). 182 this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource(); 183 } 184 else { 185 this.dataSource = dataSource; 186 } 187 } 188 189 /** 190 * Return the JDBC DataSource that this instance manages transactions for. 191 */ 192 public DataSource getDataSource() { 193 return this.dataSource; 194 } 195 196 /** 197 * Set whether to autodetect a JDBC DataSource used by the JDO PersistenceManagerFactory, 198 * as returned by the {@code getConnectionFactory()} method. Default is "true". 199 * <p>Can be turned off to deliberately ignore an available DataSource, 200 * to not expose JDO transactions as JDBC transactions for that DataSource. 201 * @see #setDataSource 202 * @see javax.jdo.PersistenceManagerFactory#getConnectionFactory 203 */ 204 public void setAutodetectDataSource(boolean autodetectDataSource) { 205 this.autodetectDataSource = autodetectDataSource; 206 } 207 208 /** 209 * Set the JDO dialect to use for this transaction manager. 210 * <p>The dialect object can be used to retrieve the underlying JDBC connection 211 * and thus allows for exposing JDO transactions as JDBC transactions. 212 * @see JdoDialect#getJdbcConnection 213 */ 214 public void setJdoDialect(JdoDialect jdoDialect) { 215 this.jdoDialect = jdoDialect; 216 } 217 218 /** 219 * Return the JDO dialect to use for this transaction manager. 220 * <p>Creates a default one for the specified PersistenceManagerFactory if none set. 221 */ 222 public JdoDialect getJdoDialect() { 223 if (this.jdoDialect == null) { 224 this.jdoDialect = new DefaultJdoDialect(); 225 } 226 return this.jdoDialect; 227 } 228 229 /** 230 * Eagerly initialize the JDO dialect, creating a default one 231 * for the specified PersistenceManagerFactory if none set. 232 * Auto-detect the PersistenceManagerFactory's DataSource, if any. 233 */ 234 @Override 235 public void afterPropertiesSet() { 236 if (getPersistenceManagerFactory() == null) { 237 throw new IllegalArgumentException("Property 'persistenceManagerFactory' is required"); 238 } 239 // Build default JdoDialect if none explicitly specified. 240 if (this.jdoDialect == null) { 241 this.jdoDialect = new DefaultJdoDialect(getPersistenceManagerFactory().getConnectionFactory()); 242 } 243 244 // Check for DataSource as connection factory. 245 if (this.autodetectDataSource && getDataSource() == null) { 246 Object pmfcf = getPersistenceManagerFactory().getConnectionFactory(); 247 if (pmfcf instanceof DataSource) { 248 // Use the PersistenceManagerFactory's DataSource for exposing transactions to JDBC code. 249 this.dataSource = (DataSource) pmfcf; 250 if (logger.isInfoEnabled()) { 251 logger.info("Using DataSource [" + this.dataSource + 252 "] of JDO PersistenceManagerFactory for JdoTransactionManager"); 253 } 254 } 255 } 256 } 257 258 259 @Override 260 public Object getResourceFactory() { 261 return getPersistenceManagerFactory(); 262 } 263 264 @Override 265 protected Object doGetTransaction() { 266 JdoTransactionObject txObject = new JdoTransactionObject(); 267 txObject.setSavepointAllowed(isNestedTransactionAllowed()); 268 269 PersistenceManagerHolder pmHolder = (PersistenceManagerHolder) 270 TransactionSynchronizationManager.getResource(getPersistenceManagerFactory()); 271 if (pmHolder != null) { 272 if (logger.isDebugEnabled()) { 273 logger.debug("Found thread-bound PersistenceManager [" + 274 pmHolder.getPersistenceManager() + "] for JDO transaction"); 275 } 276 txObject.setPersistenceManagerHolder(pmHolder, false); 277 } 278 279 if (getDataSource() != null) { 280 ConnectionHolder conHolder = (ConnectionHolder) 281 TransactionSynchronizationManager.getResource(getDataSource()); 282 txObject.setConnectionHolder(conHolder); 283 } 284 285 return txObject; 286 } 287 288 @Override 289 protected boolean isExistingTransaction(Object transaction) { 290 return ((JdoTransactionObject) transaction).hasTransaction(); 291 } 292 293 @Override 294 protected void doBegin(Object transaction, TransactionDefinition definition) { 295 JdoTransactionObject txObject = (JdoTransactionObject) transaction; 296 297 if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) { 298 throw new IllegalTransactionStateException( 299 "Pre-bound JDBC Connection found! JdoTransactionManager does not support " + 300 "running within DataSourceTransactionManager if told to manage the DataSource itself. " + 301 "It is recommended to use a single JdoTransactionManager for all transactions " + 302 "on a single DataSource, no matter whether JDO or JDBC access."); 303 } 304 305 PersistenceManager pm; 306 307 try { 308 if (txObject.getPersistenceManagerHolder() == null || 309 txObject.getPersistenceManagerHolder().isSynchronizedWithTransaction()) { 310 PersistenceManager newPm = getPersistenceManagerFactory().getPersistenceManager(); 311 if (logger.isDebugEnabled()) { 312 logger.debug("Opened new PersistenceManager [" + newPm + "] for JDO transaction"); 313 } 314 txObject.setPersistenceManagerHolder(new PersistenceManagerHolder(newPm), true); 315 } 316 317 pm = txObject.getPersistenceManagerHolder().getPersistenceManager(); 318 319 // Delegate to JdoDialect for actual transaction begin. 320 final int timeoutToUse = determineTimeout(definition); 321 Object transactionData = getJdoDialect().beginTransaction(pm.currentTransaction(), 322 new DelegatingTransactionDefinition(definition) { 323 @Override 324 public int getTimeout() { 325 return timeoutToUse; 326 } 327 }); 328 txObject.setTransactionData(transactionData); 329 330 // Register transaction timeout. 331 if (timeoutToUse != TransactionDefinition.TIMEOUT_DEFAULT) { 332 txObject.getPersistenceManagerHolder().setTimeoutInSeconds(timeoutToUse); 333 } 334 335 // Register the JDO PersistenceManager's JDBC Connection for the DataSource, if set. 336 if (getDataSource() != null) { 337 ConnectionHandle conHandle = getJdoDialect().getJdbcConnection(pm, definition.isReadOnly()); 338 if (conHandle != null) { 339 ConnectionHolder conHolder = new ConnectionHolder(conHandle); 340 if (timeoutToUse != TransactionDefinition.TIMEOUT_DEFAULT) { 341 conHolder.setTimeoutInSeconds(timeoutToUse); 342 } 343 if (logger.isDebugEnabled()) { 344 logger.debug("Exposing JDO transaction as JDBC transaction [" + 345 conHolder.getConnectionHandle() + "]"); 346 } 347 TransactionSynchronizationManager.bindResource(getDataSource(), conHolder); 348 txObject.setConnectionHolder(conHolder); 349 } 350 else { 351 if (logger.isDebugEnabled()) { 352 logger.debug("Not exposing JDO transaction [" + pm + "] as JDBC transaction because " + 353 "JdoDialect [" + getJdoDialect() + "] does not support JDBC Connection retrieval"); 354 } 355 } 356 } 357 358 // Bind the persistence manager holder to the thread. 359 if (txObject.isNewPersistenceManagerHolder()) { 360 TransactionSynchronizationManager.bindResource( 361 getPersistenceManagerFactory(), txObject.getPersistenceManagerHolder()); 362 } 363 txObject.getPersistenceManagerHolder().setSynchronizedWithTransaction(true); 364 } 365 366 catch (TransactionException ex) { 367 closePersistenceManagerAfterFailedBegin(txObject); 368 throw ex; 369 } 370 catch (Throwable ex) { 371 closePersistenceManagerAfterFailedBegin(txObject); 372 throw new CannotCreateTransactionException("Could not open JDO PersistenceManager for transaction", ex); 373 } 374 } 375 376 /** 377 * Close the current transaction's EntityManager. 378 * Called after a transaction begin attempt failed. 379 * @param txObject the current transaction 380 */ 381 protected void closePersistenceManagerAfterFailedBegin(JdoTransactionObject txObject) { 382 if (txObject.isNewPersistenceManagerHolder()) { 383 PersistenceManager pm = txObject.getPersistenceManagerHolder().getPersistenceManager(); 384 try { 385 if (pm.currentTransaction().isActive()) { 386 pm.currentTransaction().rollback(); 387 } 388 } 389 catch (Throwable ex) { 390 logger.debug("Could not rollback PersistenceManager after failed transaction begin", ex); 391 } 392 finally { 393 PersistenceManagerFactoryUtils.releasePersistenceManager(pm, getPersistenceManagerFactory()); 394 } 395 txObject.setPersistenceManagerHolder(null, false); 396 } 397 } 398 399 @Override 400 protected Object doSuspend(Object transaction) { 401 JdoTransactionObject txObject = (JdoTransactionObject) transaction; 402 txObject.setPersistenceManagerHolder(null, false); 403 PersistenceManagerHolder persistenceManagerHolder = (PersistenceManagerHolder) 404 TransactionSynchronizationManager.unbindResource(getPersistenceManagerFactory()); 405 txObject.setConnectionHolder(null); 406 ConnectionHolder connectionHolder = null; 407 if (getDataSource() != null && TransactionSynchronizationManager.hasResource(getDataSource())) { 408 connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.unbindResource(getDataSource()); 409 } 410 return new SuspendedResourcesHolder(persistenceManagerHolder, connectionHolder); 411 } 412 413 @Override 414 protected void doResume(Object transaction, Object suspendedResources) { 415 SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources; 416 TransactionSynchronizationManager.bindResource( 417 getPersistenceManagerFactory(), resourcesHolder.getPersistenceManagerHolder()); 418 if (getDataSource() != null && resourcesHolder.getConnectionHolder() != null) { 419 TransactionSynchronizationManager.bindResource(getDataSource(), resourcesHolder.getConnectionHolder()); 420 } 421 } 422 423 /** 424 * This implementation returns "true": a JDO commit will properly handle 425 * transactions that have been marked rollback-only at a global level. 426 */ 427 @Override 428 protected boolean shouldCommitOnGlobalRollbackOnly() { 429 return true; 430 } 431 432 @Override 433 protected void doCommit(DefaultTransactionStatus status) { 434 JdoTransactionObject txObject = (JdoTransactionObject) status.getTransaction(); 435 if (status.isDebug()) { 436 logger.debug("Committing JDO transaction on PersistenceManager [" + 437 txObject.getPersistenceManagerHolder().getPersistenceManager() + "]"); 438 } 439 try { 440 Transaction tx = txObject.getPersistenceManagerHolder().getPersistenceManager().currentTransaction(); 441 tx.commit(); 442 } 443 catch (JDOException ex) { 444 // Assumably failed to flush changes to database. 445 throw convertJdoAccessException(ex); 446 } 447 } 448 449 @Override 450 protected void doRollback(DefaultTransactionStatus status) { 451 JdoTransactionObject txObject = (JdoTransactionObject) status.getTransaction(); 452 if (status.isDebug()) { 453 logger.debug("Rolling back JDO transaction on PersistenceManager [" + 454 txObject.getPersistenceManagerHolder().getPersistenceManager() + "]"); 455 } 456 try { 457 Transaction tx = txObject.getPersistenceManagerHolder().getPersistenceManager().currentTransaction(); 458 if (tx.isActive()) { 459 tx.rollback(); 460 } 461 } 462 catch (JDOException ex) { 463 throw new TransactionSystemException("Could not roll back JDO transaction", ex); 464 } 465 } 466 467 @Override 468 protected void doSetRollbackOnly(DefaultTransactionStatus status) { 469 JdoTransactionObject txObject = (JdoTransactionObject) status.getTransaction(); 470 if (status.isDebug()) { 471 logger.debug("Setting JDO transaction on PersistenceManager [" + 472 txObject.getPersistenceManagerHolder().getPersistenceManager() + "] rollback-only"); 473 } 474 txObject.setRollbackOnly(); 475 } 476 477 @Override 478 protected void doCleanupAfterCompletion(Object transaction) { 479 JdoTransactionObject txObject = (JdoTransactionObject) transaction; 480 481 // Remove the persistence manager holder from the thread. 482 if (txObject.isNewPersistenceManagerHolder()) { 483 TransactionSynchronizationManager.unbindResource(getPersistenceManagerFactory()); 484 } 485 txObject.getPersistenceManagerHolder().clear(); 486 487 // Remove the JDBC connection holder from the thread, if exposed. 488 if (txObject.hasConnectionHolder()) { 489 TransactionSynchronizationManager.unbindResource(getDataSource()); 490 try { 491 getJdoDialect().releaseJdbcConnection(txObject.getConnectionHolder().getConnectionHandle(), 492 txObject.getPersistenceManagerHolder().getPersistenceManager()); 493 } 494 catch (Throwable ex) { 495 // Just log it, to keep a transaction-related exception. 496 logger.debug("Could not release JDBC connection after transaction", ex); 497 } 498 } 499 500 getJdoDialect().cleanupTransaction(txObject.getTransactionData()); 501 502 if (txObject.isNewPersistenceManagerHolder()) { 503 PersistenceManager pm = txObject.getPersistenceManagerHolder().getPersistenceManager(); 504 if (logger.isDebugEnabled()) { 505 logger.debug("Closing JDO PersistenceManager [" + pm + "] after transaction"); 506 } 507 PersistenceManagerFactoryUtils.releasePersistenceManager(pm, getPersistenceManagerFactory()); 508 } 509 else { 510 logger.debug("Not closing pre-bound JDO PersistenceManager after transaction"); 511 } 512 } 513 514 /** 515 * Convert the given JDOException to an appropriate exception from the 516 * {@code org.springframework.dao} hierarchy. 517 * <p>The default implementation delegates to the JdoDialect. 518 * May be overridden in subclasses. 519 * @param ex JDOException that occured 520 * @return the corresponding DataAccessException instance 521 * @see JdoDialect#translateException 522 */ 523 protected DataAccessException convertJdoAccessException(JDOException ex) { 524 return getJdoDialect().translateException(ex); 525 } 526 527 528 /** 529 * JDO transaction object, representing a PersistenceManagerHolder. 530 * Used as transaction object by JdoTransactionManager. 531 */ 532 private class JdoTransactionObject extends JdbcTransactionObjectSupport { 533 534 private PersistenceManagerHolder persistenceManagerHolder; 535 536 private boolean newPersistenceManagerHolder; 537 538 private Object transactionData; 539 540 public void setPersistenceManagerHolder( 541 PersistenceManagerHolder persistenceManagerHolder, boolean newPersistenceManagerHolder) { 542 this.persistenceManagerHolder = persistenceManagerHolder; 543 this.newPersistenceManagerHolder = newPersistenceManagerHolder; 544 } 545 546 public PersistenceManagerHolder getPersistenceManagerHolder() { 547 return this.persistenceManagerHolder; 548 } 549 550 public boolean isNewPersistenceManagerHolder() { 551 return this.newPersistenceManagerHolder; 552 } 553 554 public boolean hasTransaction() { 555 return (this.persistenceManagerHolder != null && this.persistenceManagerHolder.isTransactionActive()); 556 } 557 558 public void setTransactionData(Object transactionData) { 559 this.transactionData = transactionData; 560 this.persistenceManagerHolder.setTransactionActive(true); 561 } 562 563 public Object getTransactionData() { 564 return this.transactionData; 565 } 566 567 public void setRollbackOnly() { 568 Transaction tx = this.persistenceManagerHolder.getPersistenceManager().currentTransaction(); 569 if (tx.isActive()) { 570 tx.setRollbackOnly(); 571 } 572 if (hasConnectionHolder()) { 573 getConnectionHolder().setRollbackOnly(); 574 } 575 } 576 577 @Override 578 public boolean isRollbackOnly() { 579 Transaction tx = this.persistenceManagerHolder.getPersistenceManager().currentTransaction(); 580 return tx.getRollbackOnly(); 581 } 582 583 @Override 584 public void flush() { 585 try { 586 this.persistenceManagerHolder.getPersistenceManager().flush(); 587 } 588 catch (JDOException ex) { 589 throw convertJdoAccessException(ex); 590 } 591 } 592 } 593 594 595 /** 596 * Holder for suspended resources. 597 * Used internally by {@code doSuspend} and {@code doResume}. 598 */ 599 private static class SuspendedResourcesHolder { 600 601 private final PersistenceManagerHolder persistenceManagerHolder; 602 603 private final ConnectionHolder connectionHolder; 604 605 private SuspendedResourcesHolder(PersistenceManagerHolder pmHolder, ConnectionHolder conHolder) { 606 this.persistenceManagerHolder = pmHolder; 607 this.connectionHolder = conHolder; 608 } 609 610 private PersistenceManagerHolder getPersistenceManagerHolder() { 611 return this.persistenceManagerHolder; 612 } 613 614 private ConnectionHolder getConnectionHolder() { 615 return this.connectionHolder; 616 } 617 } 618 619}