Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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.log.Logger;
import org.tentackle.misc.IdSerialTuple;
import org.tentackle.session.ClassId;
import org.tentackle.session.PersistenceException;
import org.tentackle.session.TableName;
import org.tentackle.session.ThreadLocalSessionHolder;
import org.tentackle.sql.Backend;
import java.io.Serial;
/*
* @> $mapfile
*
* # modification tracking table
* name := $classname
* id := $classid
* table := $tablename
*
* ## attributes
* String(64) trackedName tablename the tablename
*
* ## indexes
* unique index tablename := tablename
*
* @<
*/
/**
* Modification information per table.
*
* @author harald
*/
@ClassId(/**/1/**/) // @wurblet < Inject $classid
@TableName(/**/"modification"/**/) // @wurblet < Inject --string $tablename
public class DbModification extends AbstractDbObject {
@Serial
private static final long serialVersionUID = 2L;
private static final Logger LOGGER = Logger.get(DbModification.class);
/** Variables common to all instances of {@link DbModification}. */
public static final DbObjectClassVariables CLASSVARIABLES =
DbObjectClassVariables.create(DbModification.class);
/**
* Initializes the modification table.
*
* @param db the session
*/
public static void initializeModificationTable(Db db) {
LOGGER.info("initializing modification table");
long txVoucher = db.begin("initialize");
try {
try (StatementWrapper st = db.createStatement()) { // non-prepared statements can only be used once!
st.executeUpdate(Backend.SQL_DELETE + Backend.SQL_FROM + CLASSVARIABLES.tableName);
}
try (StatementWrapper st = db.createStatement()) {
st.executeUpdate(Backend.SQL_INSERT_INTO + CLASSVARIABLES.tableName + Backend.SQL_LEFT_PARENTHESIS +
CN_ID + Backend.SQL_COMMA + AbstractDbObject.CN_SERIAL +
Backend.SQL_INSERT_VALUES + "0,1)");
}
db.commit(txVoucher);
}
catch (RuntimeException ex) {
db.rollback(txVoucher);
throw ex;
}
}
// @wurblet fieldnames ColumnNames --model=$mapfile
////GEN-BEGIN:fieldnames
/** database column name for 'trackedName'. */
public static final String CN_TRACKEDNAME = "tablename";
////GEN-END:fieldnames
// @wurblet fieldlengths ColumnLengths --model=$mapfile
////GEN-BEGIN:fieldlengths
/** maximum number of characters for 'trackedName'. */
int CL_TRACKEDNAME = 64;
////GEN-END:fieldlengths
// @wurblet declare Declare --model=$mapfile
////GEN-BEGIN:declare
/** the tablename. */
private String trackedName;
////GEN-END:declare
/**
* Creates a modification object for a given tablename.
*
* @param db the session, null if thread-local
* @param trackedName the tracked name
*/
public DbModification(Db db, String trackedName) {
super(db);
this.trackedName = trackedName;
if (db == null) {
setSessionHolder(new ThreadLocalSessionHolder());
}
}
/**
* Creates an empty modification object.
*
* @param db the session
*/
public DbModification(Db db) {
super(db);
}
/**
* Creates an empty modification object.
*/
public DbModification() {
super();
}
@Override
public DbObjectClassVariables getClassVariables() {
return CLASSVARIABLES;
}
@Override
public String toString() {
return trackedName;
}
// @wurblet methods MethodsImpl --noif --model=$mapfile
////GEN-BEGIN:methods
@Override
public void getFields(ResultSetWrapper rs) {
super.getFields(rs);
if (rs.configureSection(CLASSVARIABLES)) {
rs.configureColumn(CN_TRACKEDNAME);
rs.configureColumn(CN_ID);
rs.configureColumn(CN_SERIAL);
}
trackedName = rs.getString();
setId(rs.getLong());
setSerial(rs.getLong());
}
@Override
public int setFields(PreparedStatementWrapper st) {
int ndx = super.setFields(st);
st.setString(++ndx, trackedName);
st.setLong(++ndx, getId());
st.setLong(++ndx, getSerial());
return ndx;
}
@Override
public String createInsertSql(Backend backend) {
return Backend.SQL_INSERT_INTO + getTableName() + Backend.SQL_LEFT_PARENTHESIS +
CN_TRACKEDNAME + Backend.SQL_COMMA +
CN_ID + Backend.SQL_COMMA +
CN_SERIAL +
Backend.SQL_INSERT_VALUES +
Backend.SQL_PAR_COMMA.repeat(2) +
Backend.SQL_PAR + Backend.SQL_RIGHT_PARENTHESIS;
}
@Override
public String createUpdateSql(Backend backend) {
return Backend.SQL_UPDATE + getTableName() + Backend.SQL_SET +
CN_TRACKEDNAME + Backend.SQL_EQUAL_PAR_COMMA +
CN_SERIAL + Backend.SQL_EQUAL + CN_SERIAL + Backend.SQL_PLUS_ONE +
Backend.SQL_WHERE + CN_ID + Backend.SQL_EQUAL_PAR +
Backend.SQL_AND + CN_SERIAL + Backend.SQL_EQUAL_PAR;
}
/**
* Gets the attribute trackedName.
*
* @return the tablename
*/
public String getTrackedName() {
return trackedName;
}
/**
* Sets the attribute trackedName.
*
* @param trackedName the tablename
*/
public void setTrackedName(String trackedName) {
assertMutable();
this.trackedName = trackedName;
}
////GEN-END:methods
/**
* Counts modifications.
* Adds the count to the table's entry and the master entry.
*
* @param count the number of modifications to add
* @return the number of modification rows updated (should be 2)
*/
public int countModification(long count) {
assertNotRemote();
PreparedStatementWrapper st = getPreparedStatement(COUNT_MODIFICATION_ID, b ->
Backend.SQL_UPDATE + getTableName() + Backend.SQL_SET + CN_SERIAL + Backend.SQL_EQUAL + CN_SERIAL +
"+?" + Backend.SQL_WHERE + CN_TRACKEDNAME + Backend.SQL_EQUAL_PAR + Backend.SQL_OR + CN_ID + Backend.SQL_EQUAL_PAR
);
st.setLong(1, count);
st.setString(2, trackedName);
st.setLong(3, 0); // this is for the master!
return st.executeUpdate();
}
private static final StatementId COUNT_MODIFICATION_ID = new StatementId();
/**
* Adds an entry for this counter (== database object class)
* to the modification table.
* The tableserial is derived from the highest serial of all objects (i.e. class)
* this counter refers to.
*
* @param tableExists true if related to a PDO, false if just a name
* @param tableSerialTableName the table to scan for max tableserial if PDO manages a tableserial
*/
public void addToModificationTable(boolean tableExists, String tableSerialTableName) {
try (StatementWrapper st = getSession().createStatement()) {
int count;
if (tableExists) {
count = st.executeUpdate (
getBackend().isExpressionReferringToTableBeingUpdatedSupported() ?
// ordinary dbms
(
"INSERT INTO " + getTableName() + " (" + CN_TRACKEDNAME + "," +
CN_ID + "," + CN_SERIAL +
") VALUES ('" +
trackedName +
"',(SELECT MAX(" + CN_ID + ")+1 FROM " + getTableName() + ")," +
(tableSerialTableName != null ? ("(SELECT " + getBackend().getCoalesceKeyword() +
"(MAX(" + CN_TABLESERIAL + "),0) FROM " + tableSerialTableName + ")") : "1") +
")"
)
:
// some databases such as MySQL do not allow updates with expressions referring to the table being updated.
// This does the trick though ;-)
(
"INSERT INTO " + getTableName() + " SET " + CN_TRACKEDNAME + "='" + trackedName + "', " +
CN_ID + "=(SELECT * FROM (SELECT MAX(" + CN_ID + ")+1 FROM " +
getTableName() + ") AS x), " + CN_SERIAL + "=" +
(tableSerialTableName != null ? ("(SELECT " + getBackend().getCoalesceKeyword() +
"(MAX(" + CN_TABLESERIAL + "),0) FROM " + tableSerialTableName + ")") : "1")
)
);
}
else {
// just a name, not related to any PDO or database table
count = st.executeUpdate (
getBackend().isExpressionReferringToTableBeingUpdatedSupported() ?
// ordinary dbms
(
"INSERT INTO " + getTableName() + " (" + CN_TRACKEDNAME + "," +
CN_ID + "," + CN_SERIAL +
") VALUES ('" +
trackedName +
"',(SELECT MAX(" + CN_ID + ")+1 FROM " + getTableName() + "),0)"
)
:
// some databases such as MySQL do not allow updates with expressions referring to the table being updated.
// This does the trick though ;-)
(
"INSERT INTO " + getTableName() + " SET " + CN_TRACKEDNAME + "='" + trackedName + "', " +
CN_ID + "=(SELECT * FROM (SELECT MAX(" + CN_ID + ")+1 FROM " +
getTableName() + ") AS x), " + CN_SERIAL + "=0"
)
);
}
if (count != 1) {
throw new PersistenceException(this, "adding modification entry failed");
}
}
}
/**
* Refreshes the contents of this modification entry by its name.
*
* @return the modification count
*/
public long refresh() {
getSession().assertNotRemote();
PreparedStatementWrapper st = getPreparedStatement(SELECT_MODIFICATION_ID, b ->
Backend.SQL_SELECT + CN_ID + Backend.SQL_COMMA + CN_SERIAL + Backend.SQL_FROM + getTableName() +
Backend.SQL_WHERE + CN_TRACKEDNAME + Backend.SQL_EQUAL_PAR
);
st.setParallelOk(true);
st.setString(1, trackedName);
try (ResultSetWrapper rs = st.executeQuery()) {
if (rs.next()) {
setId(rs.getLong(CN_ID));
setSerial(rs.getLong(CN_SERIAL));
}
else {
setId(0);
setSerial(0);
}
return getSerial();
}
}
private static final StatementId SELECT_MODIFICATION_ID = new StatementId();
/**
* Selects the id and serial by tablename.
*
* @param tableName the database tablename
* @return the id/serial tuple, null if no such table registered so far
*/
public IdSerialTuple selectIdSerial(String tableName) {
getSession().assertNotRemote();
PreparedStatementWrapper st = getPreparedStatement(SELECT_ID_ID, b ->
Backend.SQL_SELECT + CN_ID + Backend.SQL_COMMA + CN_SERIAL +
Backend.SQL_FROM + getTableName() + Backend.SQL_WHERE +
CN_TRACKEDNAME + Backend.SQL_EQUAL_PAR
);
st.setParallelOk(true);
st.setString(1, tableName);
try (ResultSetWrapper rs = st.executeQuery()) {
if (rs.next()) {
return new IdSerialTuple(rs.getLong(1), rs.getLong(2));
}
return null;
}
}
private static final StatementId SELECT_ID_ID = new StatementId();
/**
* Selects the tablename for a given ID.
*
* @param id the table ID
* @return the tablename, null if the such an ID does not exist
*/
public String selectTableNameForId(long id) {
getSession().assertNotRemote();
PreparedStatementWrapper st = getPreparedStatement(SELECT_TABLENAME_ID, b ->
Backend.SQL_SELECT + CN_TRACKEDNAME + Backend.SQL_FROM + getTableName() +
Backend.SQL_WHERE + CN_ID + Backend.SQL_EQUAL_PAR
);
st.setParallelOk(true);
st.setLong(1, id);
try (ResultSetWrapper rs = st.executeQuery()) {
if (rs.next()) {
return rs.getString(1);
}
return null;
}
}
private static final StatementId SELECT_TABLENAME_ID = new StatementId();
/**
* Reads the master-serial.
*
* @return the master serial
*/
public long selectMasterSerial() {
getSession().assertNotRemote();
PreparedStatementWrapper st = getPreparedStatement(SELECT_MASTERSERIAL_ID, b ->
Backend.SQL_SELECT + CN_SERIAL + Backend.SQL_FROM + getTableName() +
Backend.SQL_WHERE + CN_ID + Backend.SQL_EQUAL_PAR
);
st.setParallelOk(true);
st.setLong(1, 0); // ID=0 is the master
try (ResultSetWrapper rs = st.executeQuery()) {
if (rs.next()) {
return rs.getLong(1);
}
else {
throw new PersistenceException(getSession(), "can't read master serial from " + getTableName());
}
}
}
private static final StatementId SELECT_MASTERSERIAL_ID = new StatementId();
}