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

org.tentackle.dbms.DbBatch 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.log.Logger;
import org.tentackle.session.PersistenceException;

import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**
 * Maintains batches of prepared statements.
 * 

* If batching is enabled for a session, transactions will run in batched mode. In this mode, well-defined standard prepared * statements are collected and executed later as a JDBC batch, which will gain some performance. *

* Batching is only performed within the same type of root entities, because the execution order is * important to ensure referential integrity. Furthermore, the batch is flushed on every savepoint, * whenever the same object is persisted again, a non-batchable statement is executed or the transaction committed. *

* By default, batching is enabled via the backend property batchsize=..., if the value is > 1. * However, the batchsize can be set for each session programmatically as well. If certain transactions must be * treated individually, the application can provide its own {@link org.tentackle.session.SessionFactory} and * override {@link Db#createTransaction(String, boolean)} as well as {@link DbTransaction#createBatch()}. *

* IMPORTANT: batching is not enabled by default, because it has its drawbacks, especially when it comes to * exceptions and error diagnosis. Furthermore, the performance gains depend on a lot of parameters, * for example, the type of database backend, how indexes are affected, etc... * Some JDBC drivers also need special connection parameters to take full advantage of batching. *

* Use with great care in production! */ @SuppressWarnings("rawtypes") // due to AbstractDbObject

public class DbBatch { private static final Logger LOGGER = Logger.get(DbBatch.class); private final Map statements; // statement: private final Set objects; // all objects so far private final DbTransaction transaction; // the current transaction private final int batchSize; // session's batchsize private int lastRootClassId; // last root class ID /** * Creates a batch for prepared statements. * * @param transaction the current transaction * @param batchSize the batch size, ≤ 1 if no batching */ public DbBatch(DbTransaction transaction, int batchSize) { this.transaction = transaction; this.batchSize = batchSize; statements = new LinkedHashMap<>(); // linked map to keep statements in the order they were added objects = new HashSet<>(); // to force flush as soon as the object is affected more than once } /** * Gets the transaction this batch belongs to. * * @return the transaction */ public DbTransaction getTransaction() { return transaction; } /** * Gets the batch size. * * @return the batchsize, ≤ 1 if no batching */ public int getBatchSize() { return batchSize; } /** * Adds a statement and its related PDO to this batch. * * @param modType the statement type * @param statement the insert, update or delete statement * @param object the PDO affected by the statement * @param rootClassId the root class ID */ public void add(ModificationType modType, PreparedStatementWrapper statement, AbstractDbObject object, int rootClassId) { if (statement.getStatementKey() == null) { throw new PersistenceException(statement.getSession(), "batching not supported for one-shot prepared statements"); } if (rootClassId != lastRootClassId || !objects.add(object)) { execute(true); // order can change -> flush! lastRootClassId = rootClassId; objects.add(object); } statements.computeIfAbsent(statement.getStatementKey(), statementKey -> createBatchStatement(statement, modType)).add(object); } /** * Batches a statement for execution.
* When the session's batchsize is reached, all pending statements are executed in the order of registration * up to the given statement. * * @param statement the statement */ public void batch(PreparedStatementWrapper statement) { if (statement.getBatchCount() >= batchSize - 1) { // -1 due to addBatch in DbBatchStatement#execute executePending(false, statement); } } /** * Executes all pending statements.
* Statements with more than one parameter set are executed in batch mode, * otherwise in standard mode. * * @param flush forget all statements as well */ public void execute(boolean flush) { executePending(true, null); if (flush) { statements.clear(); objects.clear(); } } /** * Creates a batched statement. * * @param statement the prepared statement * @param modType the statement type * @return the batched statement */ protected DbBatchStatement createBatchStatement(PreparedStatementWrapper statement, ModificationType modType) { statement.setBatch(this); return new DbBatchStatement(statement, modType); } /** * Executes all pending batched statements. * * @param finish true if this is the last invocation * @param upToStatement the statement in the batch to stop after its execution */ protected void executePending(boolean finish, PreparedStatementWrapper upToStatement) { for (DbBatchStatement statement : statements.values()) { statement.execute(finish); if (statement.getStatement() == upToStatement) { break; } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy