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

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

/*
 * Tentackle - https://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.sql.Backend;

import java.io.Serial;

import static org.tentackle.common.Constants.CN_ID;



/**
 * An {@link IdSource} to create unique object IDs.
 * 

* Only used if backend does not support sequences.
* Provides some degree of optimization to update the table only once at end of transaction. * * @author harald * @see ObjectSequenceId */ public class ObjectId extends AbstractIdSource { private static final StatementId SELECT_ID = new StatementId(); private static final StatementId INCREMENT_ID = new StatementId(); private static final StatementId UPDATE_ID = new StatementId(); private long lastId; // last ID private long lastTxCount; // for optimization within larger transactions private boolean registerTxPending; // true if we need to register final update in Db /** * Creates an object id. * * @param name the source and table name */ public ObjectId(String name) { super(name); } @Override public boolean isLockFree() { // becomes part of transaction and therefore is not lock-free return false; } @Override public synchronized long nextId(Db db) { assertDbNotRemote(db); final long txVoucher = db.begin("nextId"); // begin tx if not already done try { if (db.getTxNumber() > lastTxCount) { // new transaction (either from begin() above or previously from application) lastTxCount = db.getTxNumber(); // remember current tx registerTxPending = true; // next invocation will register final update // increment by 1 (this starts isolation withing the transaction!) PreparedStatementWrapper incrementStatement = db.getPreparedStatement(new StatementKey(INCREMENT_ID, getClass()), false, b -> Backend.SQL_UPDATE + getName() + Backend.SQL_SET + CN_ID + Backend.SQL_EQUAL + CN_ID + "+1" ); PreparedStatementWrapper selectStatement = db.getPreparedStatement(new StatementKey(SELECT_ID, getClass()), false, b -> Backend.SQL_SELECT + CN_ID + Backend.SQL_FROM + getName() ); /* * Note: this will get the write-lock on object-id. Further nextId() will only increment the id without any * database operation until the transaction gets closed and the final update is done. */ assertOneRowAffected(db, incrementStatement.executeUpdate()); // read back new value try (ResultSetWrapper rs = selectStatement.executeQuery()) { if (rs.next()) { lastId = rs.getLong(1); db.commit(txVoucher); } else { throw new PersistenceException(db, "table " + getName() + " is empty"); } } } else { // same tx: optimize and count only // begin() didn't return a new voucher if (txVoucher != 0) { throw new PersistenceException(db, "transaction counter corrupted"); } lastId++; if (registerTxPending) { // register final update for end of transaction db.registerCommitTxRunnable(new CommitTxRunnable() { @Serial private static final long serialVersionUID = 1L; @Override public void commit(Db db) { PreparedStatementWrapper updateStatement = db.getPreparedStatement(new StatementKey(UPDATE_ID, getClass()), false, b -> Backend.SQL_UPDATE + getName() + Backend.SQL_SET + CN_ID + Backend.SQL_EQUAL_PAR ); updateStatement.setLong(1, lastId); // set current value of ID assertOneRowAffected(db, updateStatement.executeUpdate()); } }); registerTxPending = false; } db.commit(txVoucher); } } catch (PersistenceException px) { db.rollback(txVoucher); throw px; } catch (RuntimeException rx) { db.rollback(txVoucher); throw new PersistenceException(db, rx); } return lastId; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy