/* * 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; } }