All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.ebean.spring.txn.SpringJdbcTransactionManager Maven / Gradle / Ivy

package io.ebean.spring.txn;

import io.avaje.applog.AppLog;
import io.ebean.TxScope;
import io.ebean.config.CurrentTenantProvider;
import io.ebean.config.ExternalTransactionManager;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.server.transaction.TransactionManager;
import org.springframework.jdbc.datasource.ConnectionHolder;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import jakarta.persistence.PersistenceException;
import javax.sql.DataSource;
import java.util.List;

import static java.lang.System.Logger.Level.DEBUG;

/**
 * A Spring-aware {@link ExternalTransactionManager}.
 * 

* Will look for Spring transactions and use them if they exist. * * @author E Mc Greal * @since 18.05.2009 */ public final class SpringJdbcTransactionManager implements ExternalTransactionManager { private static final System.Logger log = AppLog.getLogger(SpringJdbcTransactionManager.class); private final CurrentTenantProvider tenantProvider; private DataSource dataSource; private TransactionManager transactionManager; /** * Instantiate with a CurrentTenantProvider for Multi-Tenant support. *

* When a new transaction is detected, this will set the TenantId on that transaction. */ public SpringJdbcTransactionManager(CurrentTenantProvider tenantProvider) { this.tenantProvider = tenantProvider; } /** * Instantiates a new spring aware transaction scope manager. */ public SpringJdbcTransactionManager() { this.tenantProvider = null; } /** * Initialise this with the Ebean internal transaction manager. */ @Override public void setTransactionManager(Object txnMgr) { // RB: At this stage not exposing TransactionManager to // the public API and hence the Object type and casting here this.transactionManager = (TransactionManager) txnMgr; this.dataSource = transactionManager.dataSource(); } /** * Looks for a current Spring managed transaction and wraps/returns that as a Ebean transaction. *

* Returns null if there is no current spring transaction (lazy loading outside a spring txn). */ @Override public Object getCurrentTransaction() { // Get the current Spring ConnectionHolder associated to the current spring managed transaction ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource); if (holder == null || !holder.isSynchronizedWithTransaction()) { // no current Spring transaction SpiTransaction currentEbeanTransaction = transactionManager.inScope(); if (currentEbeanTransaction == null || !currentEbeanTransaction.isActive()) { return null; } else { return currentEbeanTransaction; } } SpringTxnListener springTxnLister = listener(); if (springTxnLister != null) { // we have already seen this transaction return springTxnLister.transaction(); } else { // This is a new spring transaction that we have not seen before. // "wrap" it in a SpringJdbcTransaction for use with Ebean final var newTrans = new SpringJdbcTransaction(holder, transactionManager); if (tenantProvider != null) { final var tenantId = tenantProvider.currentId(); if (tenantId != null) { newTrans.setTenantId(tenantId); } } // Create and register a Spring TransactionSynchronization for this transaction springTxnLister = createListener(newTrans); TransactionSynchronizationManager.registerSynchronization(springTxnLister); return transactionManager.externalBeginTransaction(newTrans, TxScope.required()); } } /** * Search for our specific transaction listener. *

* If it exists then we have already seen and "wrapped" this transaction. */ private SpringTxnListener listener() { if (TransactionSynchronizationManager.isSynchronizationActive()) { List synchronizations = TransactionSynchronizationManager.getSynchronizations(); // search for our specific listener for (TransactionSynchronization synchronization : synchronizations) { if (synchronization instanceof SpringTxnListener) { return (SpringTxnListener) synchronization; } } } return null; } /** * Create a listener to enable Ebean to be notified when transactions commit and rollback. */ private SpringTxnListener createListener(SpringJdbcTransaction transaction) { return new SpringTxnListener(transactionManager, transaction); } /** * A Spring TransactionSynchronization that we register with Spring to get notified when * a Spring managed transaction has been committed or rolled back. *

* When Ebean is notified (of the commit/rollback) it can then manage its cache, notify * BeanPersistListeners etc. */ private static class SpringTxnListener implements TransactionSynchronization { private final TransactionManager transactionManager; private final SpringJdbcTransaction transaction; private SpringTxnListener(TransactionManager transactionManager, SpringJdbcTransaction t) { this.transactionManager = transactionManager; this.transaction = t; } private SpringJdbcTransaction transaction() { return transaction; } @Override public void flush() { transaction.flush(); } @Override public void beforeCommit(boolean readOnly) { if (!readOnly) { transaction.preCommit(); } } @Override public void afterCompletion(int status) { switch (status) { case STATUS_COMMITTED: log.log(DEBUG, "Spring Txn [{0}] committed", transaction.id()); transaction.postCommit(); break; case STATUS_ROLLED_BACK: log.log(DEBUG, "Spring Txn [{0}] rollback", transaction.id()); transaction.postRollback(null); break; default: // this should never happen throw new PersistenceException("Invalid status " + status); } // Remove this transaction object as it is completed transactionManager.externalRemoveTransaction(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy