com.avaje.ebeaninternal.server.persist.dml.DmlHandler Maven / Gradle / Ivy
package com.avaje.ebeaninternal.server.persist.dml;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.lib.util.Str;
import com.avaje.ebeaninternal.server.persist.BatchedPstmt;
import com.avaje.ebeaninternal.server.persist.BatchedPstmtHolder;
import com.avaje.ebeaninternal.server.persist.dmlbind.BindableRequest;
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
import com.avaje.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);
/**
* 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.
int[] columns = { 1 };
return conn.prepareStatement(sql, 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);
batch.addStmt(bs, request);
return stmt;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy