com.avaje.ebeaninternal.server.persist.dml.DmlHandler Maven / Gradle / Ivy
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean 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.
*
* Ebean 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 Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.persist.dml;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.core.PstmtBatch;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
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.BeanDelta;
import com.avaje.ebeaninternal.server.type.DataBind;
/**
* Base class for Handler implementations.
*/
public abstract class DmlHandler implements PersistHandler, BindableRequest {
protected static final Logger logger = Logger.getLogger(DmlHandler.class.getName());
/**
* The PreparedStatement used for the dml.
*/
protected DataBind dataBind;
protected ArrayList updateGenValues;
/**
* The originating request.
*/
protected final PersistRequestBean> persistRequest;
protected final StringBuilder bindLog;
protected final Set loadedProps;
protected final SpiTransaction transaction;
protected final boolean emptyStringToNull;
protected final boolean logLevelSql;
private Set additionalProps;
private boolean checkDelta;
private BeanDelta deltaBean;
protected DmlHandler(PersistRequestBean> persistRequest, boolean emptyStringToNull) {
this.persistRequest = persistRequest;
this.emptyStringToNull = emptyStringToNull;
this.loadedProps = persistRequest.getLoadedProperties();
this.transaction = persistRequest.getTransaction();
this.logLevelSql = transaction.isLogSql();
if (logLevelSql) {
this.bindLog = new StringBuilder();
} else {
this.bindLog = null;
}
}
protected void setCheckDelta(boolean checkDelta) {
this.checkDelta = checkDelta;
}
public PersistRequestBean> getPersistRequest() {
return persistRequest;
}
/**
* Get the sql and bind the statement.
*/
public abstract void bind() throws SQLException;
/**
* Execute now for non-batch execution.
*/
public abstract void execute() throws SQLException;
/**
* Add this for batch execution.
*/
public void addBatch() throws SQLException {
PstmtBatch pstmtBatch = persistRequest.getPstmtBatch();
if (pstmtBatch != null){
pstmtBatch.addBatch(dataBind.getPstmt());
} else {
dataBind.getPstmt().addBatch();
}
}
/**
* Close the underlying statement.
*/
public void close() {
try {
if (dataBind != null){
dataBind.close();
}
} catch (SQLException ex) {
logger.log(Level.SEVERE, null, ex);
}
}
/**
* Return the bind log.
*/
public String getBindLog() {
return bindLog == null ? "" : bindLog.toString();
}
/**
* Set the Id value that was bound. This value is used for logging summary
* level information.
*/
public void setIdValue(Object idValue) {
persistRequest.setBoundId(idValue);
}
/**
* Log the bind information to the transaction log.
*/
protected void logBinding() {
if (logLevelSql) {
transaction.log(bindLog.toString());
}
}
/**
* Log the sql to the transaction log.
*/
protected void logSql(String sql) {
if (logLevelSql) {
transaction.log(sql);
}
}
public boolean isIncluded(BeanProperty prop) {
return (loadedProps == null || loadedProps.contains(prop.getName()));
}
public boolean isIncludedWhere(BeanProperty prop) {
if (prop.isDbEncrypted()){
// update without a version property ...
// for encrypted properties only include if it was
// also an updated/modified property
return isIncluded(prop);
}
return prop.isDbUpdatable() && (loadedProps == null || loadedProps.contains(prop.getName()));
}
/**
* Bind a raw value. Used to bind the discriminator column.
*/
public Object bind(String propName, Object value, int sqlType) throws SQLException {
if (logLevelSql) {
bindLog.append(propName).append("=");
bindLog.append(value).append(", ");
}
dataBind.setObject(value, sqlType);
return value;
}
public Object bindNoLog(Object value, int sqlType, String logPlaceHolder) throws SQLException {
if (logLevelSql) {
bindLog.append(logPlaceHolder).append(" ");
}
dataBind.setObject(value, sqlType);
return value;
}
/**
* Bind the value to the preparedStatement.
*/
public Object bind(Object value, BeanProperty prop, String propName, boolean bindNull) throws SQLException {
return bindInternal(logLevelSql, value, prop, propName, bindNull);
}
/**
* Bind the value to the preparedStatement without logging.
*/
public Object bindNoLog(Object value, BeanProperty prop, String propName, boolean bindNull) throws SQLException {
return bindInternal(false, value, prop, propName, bindNull);
}
private Object bindInternal(boolean log, Object value, BeanProperty prop, String propName, boolean bindNull) throws SQLException {
if (!bindNull){
if (emptyStringToNull && (value instanceof String) && ((String)value).length() == 0){
// support Oracle conversion of empty string to null
//value = prop.getDbNullValue(value);
value = null;
}
}
if (!bindNull && value == null) {
// where will have IS NULL clause so don't actually bind
if (log) {
bindLog.append(propName).append("=");
bindLog.append("null, ");
}
} else {
if (log) {
bindLog.append(propName).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);
}
bindLog.append(", ");
}
// do the actual binding to PreparedStatement
prop.bind(dataBind, value);
if (checkDelta) {
if (!prop.isId() && prop.isDeltaRequired()){
if (deltaBean == null){
deltaBean = persistRequest.createDeltaBean();
transaction.getEvent().addBeanDelta(deltaBean);
}
deltaBean.add(prop, value);
}
}
}
return value;
}
/**
* Add the comment to the bind information log.
*/
protected void bindLogAppend(String comment) {
if (logLevelSql) {
bindLog.append(comment);
}
}
/**
* For generated properties set on insert register as additional
* loaded properties if required.
*/
public final void registerAdditionalProperty(String propertyName) {
if (loadedProps != null && !loadedProps.contains(propertyName)){
if (additionalProps == null){
additionalProps = new HashSet();
}
additionalProps.add(propertyName);
}
}
/**
* Set any additional (generated) properties to the set of loaded properties
* if required.
*/
protected void setAdditionalProperties() {
if (additionalProps != null){
// additional generated properties set on insert
// added to the set of loaded properties
additionalProps.addAll(loadedProps);
persistRequest.setLoadedProps(additionalProps);
}
}
/**
* 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.
*
*/
public void registerUpdateGenValue(BeanProperty prop, Object bean, Object value) {
if (updateGenValues == null) {
updateGenValues = new ArrayList();
}
updateGenValues.add(new UpdateGenValue(prop, bean, value));
registerAdditionalProperty(prop.getName());
}
/**
* Set any update generated values to the bean. Must be called after where
* clause has been bound.
*/
public void setUpdateGenValues() {
if (updateGenValues != null) {
for (int i = 0; i < updateGenValues.size(); i++) {
UpdateGenValue updGenVal = updateGenValues.get(i);
updGenVal.setValue();
}
}
}
/**
* 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;
}
if (logLevelSql){
t.log(sql);
}
stmt = getPstmt(t, sql, genKeys);
PstmtBatch pstmtBatch = request.getPstmtBatch();
if (pstmtBatch != null){
pstmtBatch.setBatchSize(stmt, t.getBatchControl().getBatchSize());
}
BatchedPstmt bs = new BatchedPstmt(stmt, genKeys, sql, request.getPstmtBatch(), true);
batch.addStmt(bs, request);
return stmt;
}
/**
* Hold the values from GeneratedValue that need to be set to the bean
* property after the where clause has been built.
*/
private static final class UpdateGenValue {
private final BeanProperty property;
private final Object bean;
private final Object value;
private UpdateGenValue(BeanProperty property, Object bean, Object value) {
this.property = property;
this.bean = bean;
this.value = value;
}
/**
* Set the value to the bean property.
*/
private void setValue() {
// support PropertyChangeSupport
property.setValueIntercept(bean, value);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy