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

io.ebeaninternal.server.persist.dml.DmlHandler Maven / Gradle / Ivy

There is a newer version: 15.6.0
Show newest version
package io.ebeaninternal.server.persist.dml;

import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.server.core.PersistRequestBean;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.lib.util.Str;
import io.ebeaninternal.server.persist.BatchedPstmt;
import io.ebeaninternal.server.persist.BatchedPstmtHolder;
import io.ebeaninternal.server.persist.dmlbind.BindableRequest;
import io.ebeaninternal.server.transaction.TransactionManager;
import io.ebeaninternal.server.type.DataBind;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.persistence.OptimisticLockException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * Base class for Handler implementations.
 */
public abstract class DmlHandler implements PersistHandler, BindableRequest {

  private static final Logger logger = LoggerFactory.getLogger(DmlHandler.class);

  private static final int[] GENERATED_KEY_COLUMNS = new int[]{1};

  /**
   * The originating request.
   */
  protected final PersistRequestBean persistRequest;

  protected final StringBuilder bindLog;

  protected final SpiTransaction transaction;

  protected final boolean emptyStringToNull;

  protected final boolean logLevelSql;

  protected final long now;

  /**
   * The PreparedStatement used for the dml.
   */
  protected DataBind dataBind;

  protected String sql;

  /**
   * The generated value for the @Version property. Must be set after where clause is bound.
   */
  protected Object versionValue;

  protected DmlHandler(PersistRequestBean persistRequest, boolean emptyStringToNull) {
    this.now = System.currentTimeMillis();
    this.persistRequest = persistRequest;
    this.emptyStringToNull = emptyStringToNull;
    this.transaction = persistRequest.getTransaction();
    this.logLevelSql = transaction.isLogSql();
    if (logLevelSql) {
      this.bindLog = new StringBuilder(50);
    } else {
      this.bindLog = null;
    }
  }

  @Override
  public long now() {
    return now;
  }

  @Override
  public PersistRequestBean getPersistRequest() {
    return persistRequest;
  }

  /**
   * Bind to the statement returning the DataBind.
   */
  protected DataBind bind(PreparedStatement stmt) {
    return new DataBind(persistRequest.getDataTimeZone(), stmt, transaction.getInternalConnection());
  }

  /**
   * Get the sql and bind the statement.
   */
  @Override
  public abstract void bind() throws SQLException;

  /**
   * Execute now for non-batch execution.
   */
  @Override
  public abstract int execute() throws SQLException;

  /**
   * Check the rowCount.
   */
  protected void checkRowCount(int rowCount) throws OptimisticLockException {
    try {
      persistRequest.checkRowCount(rowCount);
      persistRequest.postExecute();
    } catch (OptimisticLockException e) {
      // add the SQL and bind values to error message
      String m = e.getMessage() + " sql[" + sql + "] bind[" + bindLog + "]";
      persistRequest.getTransaction().logSummary("OptimisticLockException:" + m);
      throw new OptimisticLockException(m, null, e.getEntity());
    }
  }

  /**
   * Add this for batch execution.
   */
  @Override
  public void addBatch() throws SQLException {
    dataBind.getPstmt().addBatch();
  }

  /**
   * Close the underlying statement.
   */
  @Override
  public void close() {
    try {
      if (dataBind != null) {
        dataBind.close();
      }
    } catch (SQLException ex) {
      logger.error(null, ex);
    }
  }

  /**
   * Return the bind log.
   */
  @Override
  public String getBindLog() {
    return bindLog == null ? "" : bindLog.toString();
  }

  /**
   * Set the Id value that was bound. This value is used for logging summary
   * level information.
   */
  @Override
  public void setIdValue(Object idValue) {
    persistRequest.setBoundId(idValue);
  }

  /**
   * Log the sql to the transaction log.
   */
  protected void logSql(String sql) {
    if (logLevelSql) {
      if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
        sql = Str.add(sql, "; --bind(", bindLog.toString(), ")");
      }
      transaction.logSql(sql);
    }
  }

  /**
   * Bind a raw value. Used to bind the discriminator column.
   */
  @Override
  public void bind(Object value, int sqlType) throws SQLException {
    if (logLevelSql) {
      if (bindLog.length() > 0) {
        bindLog.append(",");
      }
      if (value == null) {
        bindLog.append("null");
      } else {
        String sval = value.toString();
        if (sval.length() > 50) {
          bindLog.append(sval.substring(0, 47)).append("...");
        } else {
          bindLog.append(sval);
        }
      }
    }
    dataBind.setObject(value, sqlType);
  }

  @Override
  public void bindNoLog(Object value, int sqlType, String logPlaceHolder) throws SQLException {
    if (logLevelSql) {
      bindLog.append(logPlaceHolder).append(" ");
    }
    dataBind.setObject(value, sqlType);
  }

  /**
   * Bind the value to the preparedStatement.
   */
  @Override
  public void bind(Object value, BeanProperty prop) throws SQLException {
    bindInternal(logLevelSql, value, prop);
  }

  /**
   * Bind the value to the preparedStatement without logging.
   */
  @Override
  public void bindNoLog(Object value, BeanProperty prop) throws SQLException {
    bindInternal(false, value, prop);
  }

  private void bindInternal(boolean log, Object value, BeanProperty prop) throws SQLException {

    if (log) {
      if (bindLog.length() > 0) {
        bindLog.append(",");
      }
      if (prop.isLob()) {
        bindLog.append("[LOB]");
      } else {
        String sv = String.valueOf(value);
        if (sv.length() > 50) {
          sv = sv.substring(0, 47) + "...";
        }
        bindLog.append(sv);
      }
    }
    // do the actual binding to PreparedStatement
    prop.bind(dataBind, value);
  }

  /**
   * Register a generated value on a update. This can not be set to the bean
   * until after the where clause has been bound for concurrency checking.
   * 

* GeneratedProperty values are likely going to be used for optimistic * concurrency checking. This includes 'counter' and 'update timestamp' * generation. *

*/ @Override public void registerGeneratedVersion(Object versionValue) { this.versionValue = versionValue; } /** * Set any update generated values to the bean. Must be called after where * clause has been bound. */ public void setUpdateGenValues() { if (versionValue != null) { persistRequest.setVersionValue(versionValue); } } /** * Check with useGeneratedKeys to get appropriate PreparedStatement. */ protected PreparedStatement getPstmt(SpiTransaction t, String sql, boolean genKeys) throws SQLException { Connection conn = t.getInternalConnection(); if (genKeys) { // the Id generated is always the first column // Required to stop Oracle10 giving us Oracle rowId?? // Other jdbc drivers seem fine without this hint. return conn.prepareStatement(sql, GENERATED_KEY_COLUMNS); } else { return conn.prepareStatement(sql); } } /** * Return a prepared statement taking into account batch requirements. */ protected PreparedStatement getPstmt(SpiTransaction t, String sql, PersistRequestBean request, boolean genKeys) throws SQLException { BatchedPstmtHolder batch = t.getBatchControl().getPstmtHolder(); PreparedStatement stmt = batch.getStmt(sql, request); if (stmt != null) { return stmt; } stmt = getPstmt(t, sql, genKeys); BatchedPstmt bs = new BatchedPstmt(stmt, genKeys, sql, t); batch.addStmt(bs, request); return stmt; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy