Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.github.susom.database.SqlSelectImpl Maven / Gradle / Ivy
/*
* Copyright 2014 The Board of Trustees of The Leland Stanford Junior University.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.susom.database;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is the key class for configuring (query parameters) and executing a database query.
*
* @author garricko
*/
public class SqlSelectImpl implements SqlSelect {
private static final Logger log = LoggerFactory.getLogger(SqlSelectImpl.class);
private static final Object[] ZERO_LENGTH_OBJECT_ARRAY = new Object[0];
private final Connection connection;
private final StatementAdaptor adaptor;
private PreparedStatement ps; // hold reference to support cancel from another thread
private final Object cancelLock = new Object();
private final String sql;
private final LogOptions logOptions;
private List parameterList; // !null ==> traditional ? args
private Map parameterMap; // !null ==> named :abc args
private int timeoutSeconds = -1; // -1 ==> no timeout
private int maxRows = -1; // -1 ==> unlimited
public SqlSelectImpl(Connection connection, String sql, LogOptions logOptions) {
this.connection = connection;
this.sql = sql;
this.logOptions = logOptions;
adaptor = new StatementAdaptor();
}
@Override
public SqlSelect argInteger(Integer arg) {
return positionalArg(adaptor.nullNumeric(arg));
}
@Override
public SqlSelect argInteger(String argName, Integer arg) {
return namedArg(argName, adaptor.nullNumeric(arg));
}
@Override
public SqlSelect argLong(Long arg) {
return positionalArg(adaptor.nullNumeric(arg));
}
@Override
public SqlSelect argLong(String argName, Long arg) {
return namedArg(argName, adaptor.nullNumeric(arg));
}
@Override
public SqlSelect argFloat(Float arg) {
return positionalArg(adaptor.nullNumeric(arg));
}
@Override
public SqlSelect argFloat(String argName, Float arg) {
return namedArg(argName, adaptor.nullNumeric(arg));
}
@Override
public SqlSelect argDouble(Double arg) {
return positionalArg(adaptor.nullNumeric(arg));
}
@Override
public SqlSelect argDouble(String argName, Double arg) {
return namedArg(argName, adaptor.nullNumeric(arg));
}
@Override
public SqlSelect argBigDecimal(BigDecimal arg) {
return positionalArg(adaptor.nullNumeric(arg));
}
@Override
public SqlSelect argBigDecimal(String argName, BigDecimal arg) {
return namedArg(argName, adaptor.nullNumeric(arg));
}
@Override
public SqlSelect argString(String arg) {
return positionalArg(adaptor.nullString(arg));
}
@Override
public SqlSelect argString(String argName, String arg) {
return namedArg(argName, adaptor.nullString(arg));
}
@Override
public SqlSelect argDate(Date arg) {
return positionalArg(adaptor.nullDate(arg));
}
@Override
public SqlSelect argDate(String argName, Date arg) {
return namedArg(argName, adaptor.nullDate(arg));
}
@Override
public SqlSelect withTimeoutSeconds(int seconds) {
timeoutSeconds = seconds;
return this;
}
@Override
public SqlSelect withMaxRows(int rows) {
maxRows = rows;
return this;
}
@Override
public Long queryLong() {
return queryWithTimeout(new RowsHandler() {
@Override
public Long process(Rows rs) throws Exception {
if (rs.next()) {
return rs.getLong(1);
}
return null;
}
});
}
@Override
public List queryLongs() {
return query(new RowsHandler>() {
@Override
public List process(Rows rs) throws Exception {
List result = new ArrayList<>();
if (rs.next()) {
result.add(rs.getLong(1));
}
return result;
}
});
}
@Override
public T query(RowsHandler rowsHandler) {
return queryWithTimeout(rowsHandler);
}
private SqlSelect positionalArg(Object arg) {
if (parameterMap != null) {
throw new DatabaseException("Use either positional or named query parameters, not both");
}
if (parameterList == null) {
parameterList = new ArrayList<>();
}
parameterList.add(arg);
return this;
}
private SqlSelect namedArg(String argName, Object arg) {
if (parameterList != null) {
throw new DatabaseException("Use either positional or named query parameters, not both");
}
if (parameterMap == null) {
parameterMap = new HashMap<>();
}
if (argName.startsWith(":")) {
argName = argName.substring(1);
}
parameterMap.put(argName, arg);
return this;
}
private T queryWithTimeout(RowsHandler handler) {
assert ps == null;
ResultSet rs = null;
Metric metric = new Metric(log.isDebugEnabled());
String executeSql;
Object[] parameters = ZERO_LENGTH_OBJECT_ARRAY;
if (parameterMap != null && parameterMap.size() > 0) {
NamedParameterSql paramSql = new NamedParameterSql(sql);
executeSql = paramSql.getSqlToExecute();
parameters = paramSql.toArgs(parameterMap);
} else {
executeSql = sql;
if (parameterList != null) {
parameters = parameterList.toArray(new Object[parameterList.size()]);
}
}
boolean isWarn = false;
boolean isSuccess = false;
String errorCode = null;
try {
synchronized (cancelLock) {
ps = connection.prepareStatement(executeSql);
}
if (timeoutSeconds >= 0) {
ps.setQueryTimeout(timeoutSeconds);
}
if (maxRows > 0) {
ps.setMaxRows(maxRows);
}
adaptor.addParameters(ps, parameters);
metric.checkpoint("prep");
rs = ps.executeQuery();
metric.checkpoint("exec");
final ResultSet finalRs = rs;
T result = handler.process(new RowsAdaptor(finalRs));
metric.checkpoint("read");
isSuccess = true;
return result;
} catch (SQLException e) {
if (e.getErrorCode() == 1013) {
isWarn = true;
// It's ambiguous based on the Oracle error code whether it was a timeout or cancel
throw new QueryTimedOutException("Timeout of " + timeoutSeconds + " seconds exceeded or user cancelled", e);
}
errorCode = logOptions.generateErrorCode();
throw new DatabaseException(DebugSql.exceptionMessage(executeSql, parameters, errorCode, logOptions), e);
} catch (Exception e) {
errorCode = logOptions.generateErrorCode();
throw new DatabaseException(DebugSql.exceptionMessage(executeSql, parameters, errorCode, logOptions), e);
} finally {
adaptor.closeQuietly(rs, log);
adaptor.closeQuietly(ps, log);
synchronized (cancelLock) {
ps = null;
}
metric.done("close");
if (isSuccess) {
DebugSql.logSuccess("Query", log, metric, executeSql, parameters, logOptions);
} else if (isWarn) {
DebugSql.logWarning("Query", log, metric, "QueryTimedOutException", executeSql, parameters, logOptions);
} else {
DebugSql.logError("Query", log, metric, errorCode, executeSql, parameters, logOptions);
}
}
}
}