mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
271 lines
6.9 KiB
Java
271 lines
6.9 KiB
Java
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.Str;
|
|
import io.ebeaninternal.server.persist.BatchedPstmt;
|
|
import io.ebeaninternal.server.persist.BatchedPstmtHolder;
|
|
import io.ebeaninternal.server.persist.dmlbind.BindableRequest;
|
|
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};
|
|
private static final int BATCHED_FIRST = 1;
|
|
private static final int BATCHED = 2;
|
|
|
|
/**
|
|
* The originating request.
|
|
*/
|
|
final PersistRequestBean<?> persistRequest;
|
|
|
|
private final StringBuilder bindLog;
|
|
|
|
final SpiTransaction transaction;
|
|
|
|
private final boolean logLevelSql;
|
|
|
|
private final long now;
|
|
|
|
/**
|
|
* The PreparedStatement used for the dml.
|
|
*/
|
|
DataBind dataBind;
|
|
|
|
BatchedPstmt batchedPstmt;
|
|
|
|
String sql;
|
|
|
|
private short batchedStatus;
|
|
|
|
DmlHandler(PersistRequestBean<?> persistRequest) {
|
|
this.now = System.currentTimeMillis();
|
|
this.persistRequest = persistRequest;
|
|
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.
|
|
*/
|
|
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.
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
void logSql(String sql) {
|
|
if (logLevelSql) {
|
|
switch (batchedStatus) {
|
|
case BATCHED_FIRST: {
|
|
transaction.logSql(sql);
|
|
transaction.logSql(Str.add(" -- bind(", bindLog.toString(), ")"));
|
|
return;
|
|
}
|
|
case BATCHED: {
|
|
transaction.logSql(Str.add(" -- bind(", bindLog.toString(), ")"));
|
|
return;
|
|
}
|
|
default: {
|
|
transaction.logSql(Str.add(sql, "; -- bind(", bindLog.toString(), ")"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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, 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) {
|
|
if (bindLog.length() > 0) {
|
|
bindLog.append(",");
|
|
}
|
|
bindLog.append(logPlaceHolder);
|
|
}
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* Check with useGeneratedKeys to get appropriate PreparedStatement.
|
|
*/
|
|
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.
|
|
*/
|
|
PreparedStatement getPstmtBatch(SpiTransaction t, String sql, PersistRequestBean<?> request, boolean genKeys) throws SQLException {
|
|
|
|
BatchedPstmtHolder batch = t.getBatchControl().getPstmtHolder();
|
|
batchedPstmt = batch.getBatchedPstmt(sql);
|
|
if (batchedPstmt != null) {
|
|
batchedStatus = BATCHED;
|
|
return batchedPstmt.getStatement(request);
|
|
}
|
|
|
|
batchedStatus = BATCHED_FIRST;
|
|
PreparedStatement stmt = getPstmt(t, sql, genKeys);
|
|
|
|
batchedPstmt = new BatchedPstmt(stmt, genKeys, sql, t);
|
|
batch.addStmt(batchedPstmt, request);
|
|
return stmt;
|
|
}
|
|
|
|
}
|