package com.avaje.ebeaninternal.server.persist; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import javax.persistence.PersistenceException; import com.avaje.ebeaninternal.api.SpiTransaction; import com.avaje.ebeaninternal.server.core.PersistRequest; import com.avaje.ebeaninternal.server.core.PersistRequestBean; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; /** * Controls the batch ordering of persist requests. *
* Persist requests include bean inserts updates deletes and UpdateSql and * CallableSql requests. *
** This object queues up the requests into appropriate entries according to the * 'depth' and the 'type' of the requests. The depth relates to how saves and * deletes cascade following the associations of a bean. For saving Associated * One cascades reduce the depth (-1) and associated many's increase the depth. * The initial depth of a request is 0. *
*/ public final class BatchControl { /** * Used to sort queue entries by depth. */ private static final BatchDepthComparator depthComparator = new BatchDepthComparator(); /** * Controls batching of the PreparedStatements. This should be flushed after * each 'depth'. */ private final BatchedPstmtHolder pstmtHolder = new BatchedPstmtHolder(); /** * Map of the BatchedBeanHolder objects. They each have a depth and are later * sorted by their depth to get the execution order. */ private final HashMap* Note that UpdateSql and CallableSql will ALWAYS flush first. This is due to * it already having been bound to a PreparedStatement where as the Beans go * through a 2 step process when they are flushed (delayed binding). *
*/ public void setBatchFlushOnMixed(boolean flushBatchOnMixed) { this.batchFlushOnMixed = flushBatchOnMixed; } /** * Return the batchSize. */ public int getBatchSize() { return batchSize; } /** * Set the size of batch execution. ** The user can set this via the Transaction. *
*/ public void setBatchSize(int batchSize) { if (batchSize > 1) { this.batchSize = batchSize; } } /** * Set whether or not to use getGeneratedKeys for this batch execution. ** The user can set this via the transaction *
*/ public void setGetGeneratedKeys(Boolean getGeneratedKeys) { if (getGeneratedKeys != null) { this.getGeneratedKeys = getGeneratedKeys; } } /** * Execute a Orm Update, SqlUpdate or CallableSql. ** These all go straight to jdbc and use addBatch(). Entity beans goto a queue * and wait there so that the jdbc is executed in the correct order according * to the depth. *
*/ public int executeStatementOrBatch(PersistRequest request, boolean batch) { if (!batch || (batchFlushOnMixed && !isBeansEmpty())) { // flush when mixing beans and updateSql flush(); } if (!batch) { // execute the request immediately without batching return request.executeNow(); } if (pstmtHolder.getMaxSize() >= batchSize) { flush(); } // for OrmUpdate, SqlUpdate, CallableSql there is no queue... // so straight to jdbc prepared statement and use addBatch(). // aka executeNow() may use addBatch(). request.executeNow(); return -1; } /** * Entity Bean insert, update or delete. This will either execute the request * immediately or queue it for batch processing later. The queue is flushed * according to the depth (object graph depth). */ public int executeOrQueue(PersistRequestBean> request, boolean batch) { if (!batch || (batchFlushOnMixed && !pstmtHolder.isEmpty())) { // flush when mixing beans and updateSql flush(); } if (!batch) { return request.executeNow(); } if (addToBatch(request)) { // flush as the top level has hit the batch size flush(); } return -1; } /** * Add the request to the batch and return true if we should flush. */ private boolean addToBatch(PersistRequestBean> request) { BatchedBeanHolder beanHolder = getBeanHolder(request); int bufferSize = beanHolder.append(request); // return true if top level has hit batch size return bufferSize == batchSize && beanHolder.getOrder() == 100; } /** * Return the actual batch of PreparedStatements. */ public BatchedPstmtHolder getPstmtHolder() { return pstmtHolder; } /** * Return true if the queue is empty. */ public boolean isEmpty() { return (isBeansEmpty() && pstmtHolder.isEmpty()); } /** * Flush any batched PreparedStatements. */ protected void flushPstmtHolder() { pstmtHolder.flush(getGeneratedKeys); } /** * Execute all the requests contained in the list. */ protected void executeNow(ArrayList