/*
* Copyright 2004-2015 the Seasar Foundation and the Others.
*
* 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 org.seasar.extension.jdbc.query;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.TemporalType;
import org.seasar.extension.jdbc.ParamType;
import org.seasar.extension.jdbc.PropertyMeta;
import org.seasar.extension.jdbc.Query;
import org.seasar.extension.jdbc.ResultSetHandler;
import org.seasar.extension.jdbc.SqlLog;
import org.seasar.extension.jdbc.SqlLogRegistry;
import org.seasar.extension.jdbc.SqlLogRegistryLocator;
import org.seasar.extension.jdbc.SqlLogger;
import org.seasar.extension.jdbc.ValueType;
import org.seasar.extension.jdbc.exception.QueryTwiceExecutionRuntimeException;
import org.seasar.extension.jdbc.impl.SqlLogImpl;
import org.seasar.extension.jdbc.manager.JdbcManagerImplementor;
import org.seasar.extension.jdbc.parameter.LobParameter;
import org.seasar.extension.jdbc.parameter.TemporalParameter;
import org.seasar.extension.jdbc.util.BindVariableUtil;
import org.seasar.extension.sql.SqlArgWrapper;
import org.seasar.framework.exception.SQLRuntimeException;
import org.seasar.framework.log.Logger;
import org.seasar.framework.util.ResultSetUtil;
/**
* @author higa
*
*/
/**
* クエリの抽象クラスです。
*
* @author higa
* @param <S>
* <code>Query</code>のサブタイプです。
*/
public abstract class AbstractQuery<S extends Query<S>> implements Query<S>,
SqlLogger {
/**
* 内部的なJDBCマネージャです。
*/
protected JdbcManagerImplementor jdbcManager;
/**
* クエリを呼び出すクラスです。
*/
protected Class<?> callerClass;
/**
* クエリを呼び出すメソッド名です。
*/
protected String callerMethodName;
/**
* クエリタイムアウトの秒数です。
*/
protected int queryTimeout;
/**
* ログを出力するオブジェクトです。
*/
protected Logger logger;
/**
* 実行されるSQL
*/
protected String executedSql;
/**
* パラメータのリストです。
*/
protected List<Param> paramList = new ArrayList<Param>();
/**
* Queryが完了している場合に<code>true</code>です。
*/
protected boolean completed;
/**
* {@link AbstractQuery}を作成します。
*
* @param jdbcManager
* 内部的なJDBCマネージャ
*/
public AbstractQuery(JdbcManagerImplementor jdbcManager) {
this.jdbcManager = jdbcManager;
}
@SuppressWarnings("unchecked")
public S callerClass(Class<?> callerClass) {
this.callerClass = callerClass;
return (S) this;
}
@SuppressWarnings("unchecked")
public S callerMethodName(String callerMethodName) {
this.callerMethodName = callerMethodName;
return (S) this;
}
@SuppressWarnings("unchecked")
public S queryTimeout(int queryTimeout) {
this.queryTimeout = queryTimeout;
return (S) this;
}
public void logSql(String sql, Object... vars) {
String completeSql = null;
if (logger.isDebugEnabled()) {
completeSql = BindVariableUtil.getCompleteSql(sql, vars);
logger.debug(completeSql);
}
SqlLogRegistry sqlLogRegistry = SqlLogRegistryLocator.getInstance();
if (sqlLogRegistry != null) {
if (completeSql == null) {
completeSql = BindVariableUtil.getCompleteSql(sql, vars);
}
Class<?>[] classes = new Class[vars.length];
for (int i = 0; i < vars.length; ++i) {
classes[i] = vars[i].getClass();
}
SqlLog sqlLog = new SqlLogImpl(sql, completeSql, vars, classes);
sqlLogRegistry.add(sqlLog);
}
}
/**
* クエリの準備をします。
*
* @param methodName
* メソッド名
*/
protected abstract void prepare(String methodName);
/**
* SQLをログに出力します。
*/
protected void logSql() {
String completeSql = null;
Object[] vars = null;
ValueType[] valueTypes = null;
if (logger.isDebugEnabled()) {
vars = getParamValues();
valueTypes = getParamValueTypes();
completeSql = BindVariableUtil.getCompleteSql(executedSql, vars,
valueTypes);
logger.debug(completeSql);
}
SqlLogRegistry sqlLogRegistry = SqlLogRegistryLocator.getInstance();
if (sqlLogRegistry != null) {
if (completeSql == null) {
vars = getParamValues();
valueTypes = getParamValueTypes();
completeSql = BindVariableUtil.getCompleteSql(executedSql,
vars, valueTypes);
}
SqlLog sqlLog = new SqlLogImpl(executedSql, completeSql, vars,
getParamClasses());
sqlLogRegistry.add(sqlLog);
}
}
/**
* 検索を呼び出すクラスとメソッド名を準備します。
*
* @param methodName
* メソッド名
*/
protected void prepareCallerClassAndMethodName(String methodName) {
assertNotCompleted(methodName);
if (callerClass == null) {
callerClass = getClass();
}
logger = Logger.getLogger(callerClass);
if (callerMethodName == null) {
callerMethodName = methodName;
}
}
/**
* 内部的なJDBCマネージャを返します。
*
* @return 内部的なJDBCマネージャ
*/
public JdbcManagerImplementor getJdbcManager() {
return jdbcManager;
}
/**
* 実行されるSQLを返します。
*
* @return 実行されるSQL
*/
public String getExecutedSql() {
return executedSql;
}
/**
* パラメータの値の配列を返します。
*
* @return パラメータの値の配列
*/
public Object[] getParamValues() {
Object[] ret = new Object[paramList.size()];
for (int i = 0; i < paramList.size(); i++) {
ret[i] = paramList.get(i).value;
}
return ret;
}
/**
* パラメータの値のクラスの配列を返します。
*
* @return パラメータの値のクラスの配列
*/
public Class<?>[] getParamClasses() {
Class<?>[] ret = new Class<?>[paramList.size()];
for (int i = 0; i < paramList.size(); i++) {
ret[i] = paramList.get(i).paramClass;
}
return ret;
}
/**
* パラメータの値型の配列を返します。
*
* @return パラメータの値型の配列
*/
public ValueType[] getParamValueTypes() {
ValueType[] ret = new ValueType[paramList.size()];
for (int i = 0; i < paramList.size(); i++) {
ret[i] = paramList.get(i).valueType;
}
return ret;
}
/**
* クエリを呼び出すクラスを返します。
*
* @return クエリを呼び出すクラス
*/
public Class<?> getCallerClass() {
return callerClass;
}
/**
* クエリを呼び出すメソッド名を返します。
*
* @return クエリを呼び出すメソッド名
*/
public String getCallerMethodName() {
return callerMethodName;
}
/**
* クエリタイムアウトを返します。
*
* @return クエリタイムアウト
*/
public int getQueryTimeout() {
return queryTimeout;
}
/**
* パラメータを返します。
*
* @param index
* インデックス
* @return パラメータ
*/
protected Param getParam(int index) {
return paramList.get(index);
}
/**
* パラメータの数を返します。
*
* @return パラメータの数
*/
protected int getParamSize() {
return paramList.size();
}
/**
* パラメータを追加します。
*
* @param value
* パラメータの値
* @return パラメータ
*/
protected Param addParam(Object value) {
if (value == null) {
throw new NullPointerException("value");
}
return addParam(value, value.getClass());
}
/**
* パラメータを追加します。
*
* @param value
* パラメータの値
* @param propertyMeta
* プロパティのメタデータ
* @return パラメータ
*/
protected Param addParam(Object value, PropertyMeta propertyMeta) {
if (propertyMeta == null) {
throw new NullPointerException("propertyMeta");
}
ValueType valueType = jdbcManager.getDialect().getValueType(
propertyMeta);
return addParam(value, propertyMeta.getPropertyClass(), valueType);
}
/**
* パラメータを追加します。
*
* @param value
* パラメータの値
* @param paramClass
* パラメータのクラス
* @return パラメータ
*/
protected Param addParam(Object value, Class<?> paramClass) {
if (paramClass == null) {
throw new NullPointerException("paramClass");
}
Param param = new Param();
if (value instanceof TemporalParameter) {
TemporalParameter parameter = TemporalParameter.class.cast(value);
param.value = parameter.getValue();
param.paramClass = parameter.getTemporalClass();
param.valueType = getValueType(param.paramClass, false, parameter
.getTemporalType());
} else if (value instanceof LobParameter) {
LobParameter parameter = LobParameter.class.cast(value);
param.value = parameter.getValue();
param.paramClass = parameter.getLobClass();
param.valueType = getValueType(param.paramClass, true, null);
} else {
param.value = value;
param.paramClass = paramClass;
param.valueType = getValueType(param.paramClass, false, null);
}
paramList.add(param);
return param;
}
/**
* パラメータを追加します。
*
* @param value
* パラメータの値
* @param paramClass
* パラメータのクラス
* @param valueType
* 値タイプ
* @return パラメータ
*/
protected Param addParam(Object value, Class<?> paramClass,
ValueType valueType) {
if (value instanceof SqlArgWrapper) {
value = ((SqlArgWrapper) value).getValue();
}
Param param = new Param(value, paramClass);
param.valueType = valueType;
paramList.add(param);
return param;
}
/**
* 値タイプを返します。
*
* @param paramClass
* パラメータのクラス
* @param lob
* <code>LOB</code>かどうか
* @param temporalType
* 時制の種別
* @return 値タイプ
*/
protected ValueType getValueType(Class<?> paramClass, boolean lob,
TemporalType temporalType) {
return jdbcManager.getDialect().getValueType(paramClass, lob,
temporalType);
}
/**
* <code>IN</code>パラメータの準備をします。
*
* @param ps
* 準備されたステートメント
*/
protected void prepareInParams(PreparedStatement ps) {
int size = paramList.size();
try {
for (int i = 0; i < size; i++) {
Param param = paramList.get(i);
if (param.paramType != ParamType.OUT) {
param.valueType.bindValue(ps, i + 1, param.value);
}
}
} catch (SQLException e) {
throw new SQLRuntimeException(e);
}
}
/**
* パラメータをリセットします。
*/
protected void resetParams() {
paramList.clear();
}
/**
* 結果セットを処理します。
*
* @param handler
* 結果セットハンドラ
* @param rs
* 結果セット
* @return 処理結果
* @throws SQLRuntimeException
* SQL例外が発生した場合。
*/
protected Object handleResultSet(ResultSetHandler handler, ResultSet rs)
throws SQLRuntimeException {
Object ret = null;
try {
ret = handler.handle(rs);
} catch (SQLException e) {
throw new SQLRuntimeException(e);
} finally {
ResultSetUtil.close(rs);
}
return ret;
}
/**
* {@link CharSequence}の配列を{@link String}の配列に変換して返します。
*
* @param names
* {@link CharSequence}の配列
* @return {@link String}の配列
*/
protected String[] toStringArray(final CharSequence... names) {
final String[] result = new String[names.length];
for (int i = 0; i < result.length; ++i) {
final CharSequence name = names[i];
result[i] = name == null ? null : name.toString();
}
return result;
}
/**
* このQueryが完了していないことをチェックします。
*
* @param methodName
* メソッド名
*/
protected void assertNotCompleted(final String methodName) {
if (completed) {
throw new QueryTwiceExecutionRuntimeException(getClass(),
methodName);
}
}
/**
* このQueryが完了しました。
*/
protected void completed() {
completed = true;
}
}