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

org.tentackle.dbms.DbTransaction Maven / Gradle / Ivy

There is a newer version: 21.16.1.0
Show newest version
/**
 * Tentackle - http://www.tentackle.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


package org.tentackle.dbms;

import org.tentackle.session.PersistenceException;
import org.tentackle.session.SavepointHandle;

import java.sql.Savepoint;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Holder for transaction local data.
 *
 * @author harald
 */
public class DbTransaction {

  // the jvm-wide transaction counter
  private static final AtomicLong TX_COUNT = new AtomicLong();

  private static final String DEFAULT_TXNAME = "";

  private Db db;                        // the db connection
  private final long creationTime;      // epochal time in ms when started/created
  private final long txNumber;          // the unique transaction number
  private final String txName;          // optional transaction name
  private final long txVoucher;         // the random voucher for commit or rollback
  private int handleCount;              // the last handle number
  private int txLevel;                  // number of nested begin() (only for local Db), Integer.MIN_VALUE if invalid
  private int updateCount;              // number of object modified by executeUpdate since the last begin()
  private AbstractDbObject txObject; // optional top-level object initiating the transaction

  private Map visitors;            // the visitors
  private Map commitTxRunnables;     // the commit runnables
  private Map rollbackTxRunnables; // the commit runnables
  private Map savepoints;                       // the savepoints


  /**
   * Creates a transaction.
   *
   * @param db the db
   * @param txName the transaction name
   * @param fromRemote true if initiated from remote client
   */
  public DbTransaction(Db db, String txName, boolean fromRemote) {

    this.db = db;
    this.txName = txName == null ? DEFAULT_TXNAME : txName;

    txNumber = TX_COUNT.incrementAndGet();
    txLevel = 1;      // nesting level 1 = first begin
    creationTime = System.currentTimeMillis();

    // create new commit/rollback voucher
    long magic;
    do {
      magic = UUID.randomUUID().getLeastSignificantBits();
    }
    while (magic == 0);

    // remote are negative, local positive
    txVoucher = (fromRemote && magic > 0 || !fromRemote && magic < 0) ? -magic : magic;
  }



  /**
   * Sets the db.
   * 

* Used after transferring from server to client. * * @param db the new db */ public void setSession(Db db) { this.db = db; } /** * Gets the db. * * @return the db */ public Db getSession() { return db; } /** * Gets the epochal creation time in ms. * * @return the milliseconds since 1970-01-01 */ public long getCreationTime() { return creationTime; } /** * Gets the number of objects modified since the last begin(). * The method is provided to check whether objects have been * modified at all. * * @return the number of modified objects */ public int getUpdateCount() { return updateCount; } /** * Add to updateCount. * * @param count the number of updates to add */ public void addToUpdateCount(int count) { updateCount += count; } /** * Gets the transaction name. * * @return the name */ public String getTxName() { return txName; } /** * Gets the transaction number. * * @return the tx number */ public long getTxNumber() { return txNumber; } /** * Gets the nesting level. * * @return the tx level */ public int getTxLevel() { return txLevel; } /** * Marks the txLevel invalid. *

* Will suppress any checks and warnings. */ public void invalidateTxLevel() { txLevel = Integer.MIN_VALUE; } /** * Returns whether the txLevel is valid. * * @return true if valid */ public boolean isTxLevelValid() { return txLevel != Integer.MIN_VALUE; } /** * Increments the transaction level. * * @return the new tx level */ public int incrementTxLevel() { if (isTxLevelValid()) { txLevel++; } return txLevel; } /** * Decrements the transaction level. * * @return the new tx level */ public int decrementTxLevel() { if (isTxLevelValid()) { --txLevel; if (txLevel < 1) { throw new PersistenceException(db, "unbalanced tx level"); } } return txLevel; } /** * Gets the transaction voucher. * * @return the voucher */ public long getTxVoucher() { return txVoucher; } /** * Sets the optional transaction object.
* By default, whenever a transaction is initiated by a persistence operation of * a PDO, that object becomes the "parent" of the transaction.
* The {@code txObject} is mainly used for logging and enhanced auditing (partial history) during transactions. * The {@code txObject} is cleared at the end of the transaction. * * @param txObject the transaction object, null to clear */ public void setTxObject(AbstractDbObject txObject) { this.txObject = txObject; } /** * Gets the optional transaction object. * * @return the transaction object, null if none */ public AbstractDbObject getTxObject() { return txObject; } /** * Adds a savepoint to the transaction. * * @param handle the handle * @param savepoint the savepoint */ public void addSavepoint(SavepointHandle handle, Savepoint savepoint) { if (savepoints == null) { savepoints = new HashMap<>(); } savepoints.put(handle, savepoint); } /** * Gets a savepoint by handle. * * @param handle the handle * @return savepoint the savepoint */ public Savepoint getSavepoint(SavepointHandle handle) { return savepoints == null ? null : savepoints.get(handle); } /** * Removes a savepoint. * * @param handle the handle * @return the removed savepoint, null if no such handle */ public Savepoint removeSavepoint(SavepointHandle handle) { return savepoints == null ? null : savepoints.remove(handle); } /** * Returns whether this transaction has active savepoints. * * @return true if savepoints are active */ public boolean isWithSavepoints() { return savepoints != null && !savepoints.isEmpty(); } @Override public String toString() { return "transaction-" + txNumber + (isTxLevelValid() ? ("." + txLevel) : "") + "(" + txName + ") on " + db; } /** * Registers a {@link PersistenceVisitor} to be invoked just before * performing a persistence operation.
* * @param visitor the visitor to register * @return the handle for the visitor */ public DbTransactionHandle registerPersistenceVisitor(PersistenceVisitor visitor) { if (visitors == null) { visitors = new HashMap<>(); } DbTransactionHandle handle = new DbTransactionHandle(txNumber, ++handleCount); visitors.put(handle, visitor); return handle; } /** * Unegisters a {@link PersistenceVisitor}. * * @param handle the visitor's handle to unregister * @return the removed visitor, null if not registered */ public PersistenceVisitor unregisterPersistenceVisitor(DbTransactionHandle handle) { return visitors == null ? null : visitors.remove(handle); } /** * Gets the currently registered persistence visitors. * * @return the visitors, null or empty if none */ public Collection getPersistenceVisitors() { return visitors.values(); } /** * Registers a {@link CommitTxRunnable} to be invoked just before * committing a transaction. * * @param commitRunnable the runnable to register * @return the handle for the runnable */ public DbTransactionHandle registerCommitTxRunnable(CommitTxRunnable commitRunnable) { if (commitTxRunnables == null) { commitTxRunnables = new HashMap<>(); } DbTransactionHandle handle = new DbTransactionHandle(txNumber, ++handleCount); commitTxRunnables.put(handle, commitRunnable); return handle; } /** * Unregisters a {@link CommitTxRunnable}. * * @param handle the runnable's handle to unregister * @return true if removed, else not registered */ public CommitTxRunnable unregisterCommitTxRunnable(DbTransactionHandle handle) { return commitTxRunnables == null ? null : commitTxRunnables.remove(handle); } /** * Gets the currently registered commit runnables. * * @return the runnables, null or empty if none */ public Collection getCommitTxRunnables() { return commitTxRunnables.values(); } /** * Registers a {@link RollbackTxRunnable} to be invoked just before * rolling back a transaction. * * @param rollbackRunnable the runnable to register * @return the handle for the runnable */ public DbTransactionHandle registerRollbackTxRunnable(RollbackTxRunnable rollbackRunnable) { if (rollbackTxRunnables == null) { rollbackTxRunnables = new HashMap<>(); } DbTransactionHandle handle = new DbTransactionHandle(txNumber, ++handleCount); rollbackTxRunnables.put(handle, rollbackRunnable); return handle; } /** * Unregisters a {@link RollbackTxRunnable}. * * @param handle the runnable's handle to unregister * @return true if removed, else not registered */ public RollbackTxRunnable unregisterRollbackTxRunnable(DbTransactionHandle handle) { return rollbackTxRunnables == null ? null : rollbackTxRunnables.remove(handle); } /** * Gets the currently registered rollback runnables. * * @return the runnables, null or empty if none */ public Collection getRollbackTxRunnables() { return rollbackTxRunnables.values(); } /** * Executes all commit runnables and removes them. */ public void invokeCommitTxRunnables() { if (commitTxRunnables != null) { /** * execute pending runnables. * Notice: the runnables should throw DbRuntimeException on failure! */ for (CommitTxRunnable r: commitTxRunnables.values()) { r.commit(db); } commitTxRunnables = null; } } /** * Executes all rollback runnables and removes them. */ public void invokeRollbackTxRunnables() { if (rollbackTxRunnables != null) { /** * execute pending runnables. * Notice: the runnables should throw DbRuntimeException on failure! */ for (RollbackTxRunnable r: rollbackTxRunnables.values()) { r.rollback(db); } rollbackTxRunnables = null; } } /** * Checks whether a persistence operation is allowed.
* This is determined by consulting the {@link PersistenceVisitor}s.
* * @param object the persistence object * @param modType the modification type * @return true if allowed * @see #registerPersistenceVisitor(org.tentackle.dbms.PersistenceVisitor) */ public boolean isPersistenceOperationAllowed(AbstractDbObject object, char modType) { if (visitors != null) { for (PersistenceVisitor visitor: visitors.values()) { object.acceptPersistenceVisitor(visitor, modType); if (!visitor.isPersistenceOperationAllowed(object, modType)) { return false; } } } return true; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy