/*
 * MosP - Mind Open Source Project    http://www.mosp.jp/
 * Copyright (C) MIND Co., Ltd.       http://www.e-mind.co.jp/
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package jp.mosp.common.common;

import java.lang.reflect.Field;

import jp.getset.gae.bigtable.PreparedStatement;
import jp.getset.gae.GaeUnsupportedException;
import jp.getset.gae.bigtable.PreparedOption;
import jp.getset.gae.bigtable.PreparedOptionBuffer;
import jp.getset.gae.bigtable.ResultSet;
import jp.getset.gae.bigtable.Statement.Mode;
import jp.getset.gae.bigtable.option.PreparedCondition;
import jp.getset.gae.bigtable.option.PreparedProperty;
import jp.getset.gae.bigtable.option.PreparedSort;
import jp.getset.gae.driver.java.sql.Connection;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Transaction;

import jp.mosp.common.dto.CmAspUserDto;
import jp.mosp.common.dto.CmUserDto;

/**
 *  Daoの基本機能を提供する。<br><br>
 *  Statement取得やSQL実行等、DB操作関連メソッドを有する。<br>
 */
public abstract class BaseDao {
	
	// 定数
	/**
	 * デフォルト列名(作成者)。<br>
	 * {@link #setCommonParams(boolean)}、
	 * {@link #mappingCommonInfo(BaseDto)}で用いられる。<br>
	 * 不要な場合は、""(空文字列)を設定する。
	 */
	protected String INSERT_DATE = "adddt";
	
	/**
	 * デフォルト列名(作成日)。<br>
	 * {@link #setCommonParams(boolean)}、
	 * {@link #mappingCommonInfo(BaseDto)}で用いられる。<br>
	 * 不要な場合は、""(空文字列)を設定する。
	 */
	protected String INSERT_USER = "addcd";
	
	/**
	 * デフォルト列名(更新者)。<br>
	 * {@link #setCommonParams(boolean)}、
	 * {@link #mappingCommonInfo(BaseDto)}で用いられる。<br>
	 * 不要な場合は、""(空文字列)を設定する。
	 */
	protected String UPDATE_DATE = "upddt";
	
	/**
	 * デフォルト列名(更新日)。<br>
	 * {@link #setCommonParams(boolean)}、
	 * {@link #mappingCommonInfo(BaseDto)}で用いられる。<br>
	 * 不要な場合は、""(空文字列)を設定する。
	 */
	protected String UPDATE_USER = "updcd";
	
	// フィールド
	/**
	 * MosPアプリケーション設定(/WEB-INF/mosp.properties)。<br>
	 */
	protected Properties        cfg;
	
	/**
	 * ログインASPユーザーID。<br>
	 */
	protected String            aspUserId;
	
	/**
	 * ログインユーザーID。<br>
	 */
	protected String            userId;
	
	/**
	 * Command。<br>
	 */
	protected String            cmd;
	
	
	/**
	 * DBコネクション。<br>
	 */
	protected Connection        conn;
	
	/**
	 * 各種SQL実行の際に利用するステートメント。<br>
	 */
	protected PreparedStatement ps;
	
	/**
	 * 各種検索SQL実行結果。<br>
	 */
	protected ResultSet         rs;
	
	/**
	 * 各種更新SQL実行件数。<br>
	 */
	protected int               cnt;
	
	/**
	 * パラメーターインデックス。<br>
	 */
	protected int index;
	
	// GAE
	protected PreparedOptionBuffer pob = new PreparedOptionBuffer();
//	protected Transaction transaction = null;
	
	// ログ用文字列
	String       sql;
	StringBuffer params;

	/**
	 * コンストラクタ。<br>
	 */
	protected BaseDao(){
		/* 処理無し */
	}
	
	/**
	 * コンストラクタ。<br>
	 * {@link #initDao(Properties, String, CmAspUserDto, CmUserDto, Connection)}
	 * で初期化する。
	 * @param cfg     設定ファイル
	 * @param cmd     コマンド
	 * @param aspUser ASPユーザー
	 * @param user    ユーザー
	 * @param conn    DBコネクション
	 */
	protected BaseDao(
			Properties   cfg,
			String       cmd,
			CmAspUserDto aspUser,
			CmUserDto    user,
			Connection   conn
	){
		initDao(cfg, cmd, aspUser, user, conn);
	}
	
	/**
	 * DAO初期化。<br>
	 * @param cfg     設定ファイル
	 * @param cmd     コマンド
	 * @param aspUser ASPユーザー
	 * @param user    ユーザー
	 * @param conn    DBコネクション
	 */
	public void initDao(
			Properties   cfg,
			String       cmd,
			CmAspUserDto aspUser,
			CmUserDto    user,
			Connection   conn
	) {
		this.cfg = cfg;
		this.cmd = cmd;
		if (aspUser != null) {
			this.aspUserId = aspUser.getAspUserId();
		}
		if (user != null) {
			this.userId = user.getUserId();
		}
		this.conn = conn;
		params = new StringBuffer();
	}
	
	
	/**
	 * ResultSet開放。<br>
	 * @throws SQLException
	 */
	protected void releaseResultSet() throws SQLException {
		if (rs != null) {
			rs.close();
		}
	}
	
	/**
	 * PreparedStatement取得。<br>
	 * @param sql 実行SQL
	 * @throws SQLException
	 */
	protected void prepareStatement(String sql) throws SQLException {
		// GAE
		if (ps == null) {
			throw new GaeUnsupportedException("ステートメントがnullです。");
		}
		if (sql != null) {
			if (!this.pob.hasOption()) {
				throw new GaeUnsupportedException("GAE対応されていないDAOからの呼び出しです。");
			}
		}
		ps.setPreparedOptionBuffer(this.pob);
		ps.setTransaction(conn.getTransaction());
		this.pob = new PreparedOptionBuffer();
	}

	
	/**
	 * PreparedStatement開放。<br>
	 * @throws SQLException
	 */
	protected void releasePreparedStatement() throws SQLException {
		if (ps != null) {
			ps.close();
		}
		this.sql = "";
	}
	
	
	/**
	 * パラメータ設定(String)。<br>
	 * @param idx インデックス
	 * @param param パラメータ
	 * @throws SQLException
	 */
	protected void setParam(int idx, String param) throws SQLException {
		if (ps != null) {
			ps.setString(idx, param);
			params.append(param);
			params.append(MospConst.DATA_SEPARATOR);
		}
	}
	
	/**
	 * パラメータ設定(int)。<br>
	 * @param idx インデックス
	 * @param param パラメータ
	 * @throws SQLException
	 */
	protected void setParam(int idx, int param) throws SQLException {
		if (ps != null) {
			ps.setInt(idx, param);
			params.append(param);
			params.append(MospConst.DATA_SEPARATOR);
		}
	}
	
	/**
	 * パラメータ設定(long)。<br>
	 * @param idx インデックス
	 * @param param パラメータ
	 * @throws SQLException
	 */
	protected void setParam(int idx, long param) throws SQLException {
		if (ps != null) {
			ps.setLong(idx, param);
			params.append(param);
			params.append(MospConst.DATA_SEPARATOR);
		}
	}
	
	/**
	 * パラメータ設定(float)。<br>
	 * @param idx インデックス
	 * @param param パラメータ
	 * @throws SQLException
	 */
	protected void setParam(int idx, float param) throws SQLException {
		if (ps != null) {
			ps.setFloat(idx, param);
			params.append(param);
			params.append(MospConst.DATA_SEPARATOR);
		}
	}
	
	/**
	 * パラメータ設定(double)。<br>
	 * @param idx インデックス
	 * @param param パラメータ
	 * @throws SQLException
	 */
	protected void setParam(int idx, double param) throws SQLException {
		if (ps != null) {
			ps.setDouble(idx, param);
			params.append(param);
			params.append(MospConst.DATA_SEPARATOR);
		}
	}
	
	
	/**
	 * パラメータ設定(Date)。<br>
	 * java.sql.Timestampとして設定する。<br>
	 * @param idx インデックス
	 * @param param パラメータ
	 * @throws SQLException
	 */
	protected void setParam(int idx, Date param) throws SQLException {
		if (ps != null) {
			if (param == null) {
				ps.setTimestamp(idx, null);
			} else {
				ps.setTimestamp(idx, new Timestamp(param.getTime()));
			}
			params.append(param);
			params.append(MospConst.DATA_SEPARATOR);
		}
	}
	
	/**
	 * パラメータ設定(Date)。<br>
	 * java.sql.Date(年月日)として設定する。<br>
	 * @param idx インデックス
	 * @param param パラメータ
	 * @throws SQLException
	 */
	protected void setDateParam(int idx, Date param) throws SQLException {
		if (ps != null) {
			if (param == null) {
				ps.setDate(idx, null);
			} else {
				ps.setDate(idx, new java.sql.Date(param.getTime()));
			}
			params.append(param);
			params.append(MospConst.DATA_SEPARATOR);
		}
	}
	
	/**
	 * パラメータ消去。<br>
	 * @throws SQLException
	 */
	protected void clearParams() throws SQLException {
		if (ps != null) {
			ps.clearParameters();
		}
		params = new StringBuffer();
	}
	
	
	/**
	 * 検索系SQL実行。<br>
	 * @throws SQLException
	 */
	protected void executeQuery() throws SQLException {
		if (ps != null) {
			rs = ps.executeQuery();
		}
		ps = null;
	}
	
	
	/**
	 * 更新系SQL実行。<br>
	 * @throws SQLException
	 */
	protected void executeUpdate() throws SQLException {
		if (ps != null) {
			cnt = ps.executeUpdate();
			LogUtility.log(cfg, aspUserId, userId, cmd, 2, MospConst.LOG_EXECUTE, params.toString() + sql);
		}
	}
	
	
	/**
	 * 更新系SQL実行(ログ出力制御付)。<br>
	 * @param needLog ログ出力要否
	 * @throws SQLException
	 */
	protected void executeUpdate(boolean needLog) throws SQLException {
		if (ps != null) {
			cnt = ps.executeUpdate();
			if (needLog) {
				LogUtility.log(cfg, aspUserId, userId, cmd, 2, MospConst.LOG_EXECUTE, params.toString() + sql);
			}
		}
	}
	
	
	/**
	 * 全件検索SQL取得。<br>
	 * @param cls Daoクラス
	 * @return 全件検索SQL文字列
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException
	 */
	protected String getSelectQuery(Class<?> cls) throws NoSuchFieldException, IllegalAccessException {
		// GAE
		ps = new PreparedStatement(PreparedStatement.Mode.SELECT, getTable(cls));
		return null;
	}
	
	/**
	 * PreparedStatementの設定
	 * @param Mode  mode ステートメントモード
	 * @param Class cls  対象DAOクラス
	 */
	protected void setPreparedStatement(PreparedStatement.Mode mode, Class<?> cls) {
		try {
			this.setPreparedStatement(mode, getTable(cls));
		} catch (IllegalAccessException iae) {
			throw new RuntimeException(iae);
		} catch (NoSuchFieldException nsfe) {
			throw new RuntimeException(nsfe);
		}
	}
	
	/**
	 * PreparedStatementの設定
	 * @param Mode   mode ステートメントモード
	 * @param String kind 対象kind
	 */
	protected void setPreparedStatement(PreparedStatement.Mode mode, String kind) {
		ps = new PreparedStatement(mode, kind);
	}
	
	/**
	 * PreparedStatementの設定
	 * @param Mode mode ステートメントモード
	 */
	protected void setPreparedStatement(PreparedStatement.Mode mode) {
		this.setPreparedStatement(mode, getClass());
	}
	
	/**
	 * 検索SQL取得。<br>
	 * @param cls Daoクラス
	 * @return 全件検索SQL文字列
	 * @throws IllegalAccessException
	 */
	protected String getSelectStatement(Class<?> cls) throws IllegalAccessException {
		// GAE
/*		StringBuffer query = new StringBuffer();
		query.append("SELECT ");
		Field[] fields = cls.getFields();
		for (int i = 0; i < fields.length; i++) {
			Field field = fields[i];
			String name = field.getName();
			if (name.indexOf("COL_") == 0) {
				query.append(field.get(null));
				query.append(", ");
			}
		}
		query.append(getCommonColumn());
		query.delete(query.length() - 2, query.length() - 1);
		return query.toString();*/
		String ret = null;
		try {
			ret = getSelectQuery(cls);
		} catch (NoSuchFieldException e) {
			throw new RuntimeException(e);
		}
		return ret;
	}
	
	/**
	 * 検索SQL文を取得する。<br>
	 * @param cls Daoクラス
	 * @return 全件検索SQL文字列
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException 
	 */
	protected String getSelectStatement(
			Class<?> cls, boolean needTableName
	) throws IllegalAccessException, NoSuchFieldException {
		// GAE
/*		StringBuffer query = new StringBuffer();
		query.append("SELECT ");
		Field[] fields = cls.getFields();
		for (int i = 0; i < fields.length; i++) {
			Field field = fields[i];
			String name = field.getName();
			if (name.indexOf("COL_") == 0) {
				if (needTableName) {
					query.append(getTable(cls) + ".");
				}
				query.append(field.get(null));
				query.append(", ");
			}
		}
		query.append(getCommonColumn(cls, needTableName));
		query.delete(query.length() - 2, query.length() - 1);
		return query.toString();*/
		return getSelectQuery(cls);
	}
	
	/**
	 * 件数検索SQLを取得する。<br>
	 * @param cls Daoクラス
	 * @return 全件検索SQL文字列
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException 
	 */
	protected String getSelectCountQuery(
			Class<?> cls
	) throws IllegalAccessException, NoSuchFieldException {
		StringBuffer query = new StringBuffer();
		query.append(getSelectCountStatement());
		query.append(" FROM ");
		query.append(getTable(cls));
		query.append(" ");
		return query.toString();
	}
	
	/**
	 * 件数検索SQLを取得する。<br>
	 * @return 件数検索SQL文字列(SELECT COUNT(*))
	 */
	protected String getSelectCountStatement() {
		StringBuffer query = new StringBuffer();
		query.append("SELECT COUNT(*) ");
		setPreparedStatement(PreparedStatement.Mode.SELECT);
		this.pob.appendOperationCount();
		return null;
//		return query.toString();
	}
	
	/**
	 * 挿入SQL取得。<br>
	 * @param cls dtoクラス
	 * @return 挿入SQL文字列
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException
	 */
	protected String getInsertQuery(
			Class<?> cls
	) throws NoSuchFieldException, IllegalAccessException {
		// GAE
		ps = new PreparedStatement(PreparedStatement.Mode.INSERT, getTable(cls));
		List<String> keys = getColProperties(cls);
		PreparedOption preparedOption = new PreparedOption();
		for (String key : keys) {
			PreparedProperty property = new PreparedProperty(key);
			preparedOption.add(property);
		}
		pob.appendStoredOption(preparedOption);
		getCommonParams();
		return "";
	}
	
	/**
	 * GAE
	 * @param cls
	 * @return
	 * @throws IllegalAccessException
	 */
	private List<String> getColProperties(Class<?> cls) throws IllegalAccessException {
		return getPrefixedProperties(cls, "COL_");
	}
	
	/**
	 * 挿入SQLのベースを取得する。<br>
	 * @param cls dtoクラス
	 * @return 挿入SQLベース文字列
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException
	 */
	private String getInsertQueryBase(
			Class<?> cls
	) throws IllegalAccessException, NoSuchFieldException {
		StringBuffer query = new StringBuffer();
		query.append("INSERT INTO ");
		query.append(getTable(cls));
		query.append("(");
		Field[] fields = getClass().getFields();
		for (int i = 0; i < fields.length; i++) {
			Field field = fields[i];
			String name = field.getName();
			if (name.indexOf("COL_") == 0) {
				query.append(field.get(null));
				query.append(", ");
			}
		}
		query.append(getCommonColumn());
		query.delete(query.length() - 2, query.length() - 1);
		query.append(") VALUES");
		return query.toString();
	}

	/**
	 * {@link #INSERT_USER}、{@link #INSERT_DATE}、
	 * {@link #UPDATE_USER}、{@link #UPDATE_DATE}
	 * のSQL文字列を取得する。<br>
	 * @return 作成者、作成日、更新者、更新日列SQL文字列
	 */
	private String getCommonColumn() {
		StringBuffer query = new StringBuffer();
		if (!INSERT_DATE.equals("")) {
			query.append(INSERT_DATE + ", ");
		}
		if (!INSERT_USER.equals("")) {
			query.append(INSERT_USER + ", ");
		}
		if (!UPDATE_DATE.equals("")) {
			query.append(UPDATE_DATE + ", ");
		}
		if (!UPDATE_USER.equals("")) {
			query.append(UPDATE_USER + ", ");
		}
		return query.toString();
	}
	
	/**
	 * {@link #INSERT_USER}、{@link #INSERT_DATE}、
	 * {@link #UPDATE_USER}、{@link #UPDATE_DATE}
	 * のSQL文字列を取得する。<br>
	 * @return 作成者、作成日、更新者、更新日列SQL文字列
	 * @throws IllegalAccessException 
	 * @throws NoSuchFieldException 
	 */
	private String getCommonColumn(
			Class<?> cls, boolean needTableName
	) throws NoSuchFieldException, IllegalAccessException {
		StringBuffer query = new StringBuffer();
		if (!INSERT_DATE.equals("")) {
			if (needTableName) {
				query.append(getTable(cls) + ".");
			}
			query.append(INSERT_DATE + ", ");
		}
		if (!INSERT_USER.equals("")) {
			if (needTableName) {
				query.append(getTable(cls) + ".");
			}
			query.append(INSERT_USER + ", ");
		}
		if (!UPDATE_DATE.equals("")) {
			if (needTableName) {
				query.append(getTable(cls) + ".");
			}
			query.append(UPDATE_DATE + ", ");
		}
		if (!UPDATE_USER.equals("")) {
			if (needTableName) {
				query.append(getTable(cls) + ".");
			}
			query.append(UPDATE_USER + ", ");
		}
		return query.toString();
	}
	
	/**
	 * {@link #INSERT_USER}、{@link #INSERT_DATE}、
	 * {@link #UPDATE_USER}、{@link #UPDATE_DATE}
	 * のパラメータ設定SQL文字列を取得する。<br>
	 * @return 作成者、作成日、更新者、更新日列パラメータ設定SQL文字列
	 */
	private String getCommonParams() {
		// GAE
		if (ps == null) {
			throw new GaeUnsupportedException();
		}
		PreparedOption preparedOption = new PreparedOption();
		if (!INSERT_DATE.equals("")) {
			preparedOption.add(new PreparedProperty(INSERT_DATE));
		}
		if (!INSERT_USER.equals("")) {
			preparedOption.add(new PreparedProperty(INSERT_USER));
		}
		if (!UPDATE_DATE.equals("")) {
			preparedOption.add(new PreparedProperty(UPDATE_DATE));
		}
		if (!UPDATE_USER.equals("")) {
			preparedOption.add(new PreparedProperty(UPDATE_USER));
		}
//		ps.addPreparedOptions(preparedOption);
		pob.appendStoredOption(preparedOption);
		return "";
	}
	
	/**
	 * 更新SQL取得。<br>
	 * @param cls dtoクラス
	 * @return 更新SQL文字列
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException
	 */
	protected String getUpdateQuery(
			Class<?> cls
	) throws NoSuchFieldException, IllegalAccessException {
		// GAE
		ps = new PreparedStatement(PreparedStatement.Mode.UPDATE, getTable(cls));
		List<String> keys = getColProperties(cls);
		PreparedOption preparedOption = new PreparedOption();
		for (String key : keys) {
			preparedOption.add(new PreparedProperty(key));
		}
		if (!UPDATE_DATE.equals("")) {
			preparedOption.add(new PreparedProperty(UPDATE_DATE));
		}
		if (!UPDATE_USER.equals("")) {
			preparedOption.add(new PreparedProperty(UPDATE_USER));
		}
		pob.appendStoredOption(preparedOption);
//		ps.setPreparedOptions(preparedOption);
		getConditionForKey(cls);
		return "";
	}
	
	
	/**
	 * 削除SQL取得。<br>
	 * @param cls dtoクラス
	 * @return 削除SQL文字列
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException
	 */
	protected String getDeleteQuery(
			Class<?> cls
	) throws NoSuchFieldException, IllegalAccessException {
		// GAE
		ps = new PreparedStatement(PreparedStatement.Mode.DELETE, getTable(cls));
		getConditionForKey(cls);
		return "";
	}
	
	
	/**
	 * キーによる条件SQL取得。<br>
	 * @param cls dtoクラス
	 * @return キーによる条件SQL文字列
	 * @throws IllegalAccessException
	 */
	protected String getConditionForKey(Class<?> cls) throws IllegalAccessException {
		// GAE
		if (ps == null) {
			throw new GaeUnsupportedException();
		}
		List<String> keys = getKeyProperties(cls);
		PreparedOption preparedOption = new PreparedOption();
		for (String key : keys) {
			preparedOption.add(new PreparedCondition(key, Query.FilterOperator.EQUAL));
		}
//		ps.addPreparedOptions(preparedOption);
		pob.appendStoredOption(preparedOption);
		return "";
	}
	
	/**
	 * GAE
	 * @param cls
	 * @return
	 * @throws IllegalAccessException
	 */
	private List<String> getKeyProperties(Class<?> cls) throws IllegalAccessException {
		return getPrefixedProperties(cls, "KEY_");
	}
	
	/**
	 * GAE
	 * @param cls
	 * @param prefix
	 * @return
	 * @throws IllegalAccessException
	 */
	private List<String> getPrefixedProperties(Class<?> cls, String prefix) throws IllegalAccessException {
		List<String> properties = new ArrayList<String>();
		Field[] fields = cls.getFields();
		for (Field field : fields) {
			String fieldsName = field.getName();
			if (fieldsName.indexOf(prefix) == 0) {
				properties.add(field.get(null).toString());
			}
		}
		return properties;
	}
	
	/**
	 * キーによる条件SQL取得(追加用)。<br>
	 * @param cls dtoクラス
	 * @return キーによる条件SQL文字列
	 * @throws IllegalAccessException
	 */
	protected String getAddConditionForKey(Class<?> cls) throws IllegalAccessException {
		StringBuffer query = new StringBuffer();
		query.append(" AND ");
		Field[] fields = cls.getFields();
		for (int i = 0; i < fields.length; i++) {
			Field field = fields[i];
			String name = field.getName();
			if (name.indexOf("KEY_") == 0) {
				query.append(field.get(null));
				query.append(" = ? AND ");
			}
		}
		query.delete(query.length() - 4, query.length());
		query.append(" ");
		return query.toString();
	}
	
	/**
	 * キーによるソートSQL取得。<br>
	 * @param cls daoクラス
	 * @return キーによるソートSQL文字列
	 * @throws IllegalAccessException
	 */
	protected String getOrderForKey(Class<?> cls) throws IllegalAccessException {
		// GAE
		if (ps == null) {
			throw new GaeUnsupportedException();
		}
		List<String> keys = getKeyProperties(cls);
		PreparedOption preparedOption = new PreparedOption();
		for (String key : keys) {
			preparedOption.add(new PreparedSort(key));
		}
		pob.appendFilledOption(preparedOption);
//		ps.addPreparedOptions(preparedOption);
		return "";
	}
	
	/**
	 * テーブル名取得。<br>
	 * @param cls daoクラス
	 * @return テーブル名文字列
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException
	 * @throws MospException 
	 * @throws SQLException 
	 */
	protected String getTable(
			Class<?> cls
	) throws NoSuchFieldException, IllegalAccessException {
		Field table = cls.getField("TABLE");
		return (String)table.get(null);
	}
	
	/**
	 * 頁操作用SQL取得。<br>
	 * @param sortKey     ソートキー
	 * @param isAscending 昇順フラグ
	 * @return 頁操作用SQL文字列
	 */
	protected String getPageStatement(String sortKey, boolean isAscending) {
		StringBuffer query = new StringBuffer();
		query.append(getOrderBy() + sortKey + " ");
		if (!isAscending) {
			query.append(getDesc());
			this.pob.appendSort(sortKey, Query.SortDirection.DESCENDING);
		} else {
			this.pob.appendSort(sortKey, Query.SortDirection.ASCENDING);
		}
		// パラメタ設定時にthis.pob.setLimit()するとこ
		query.append(getLimit () + "? ");
		query.append(getOffset() + "? ");
		return null;
//		return query.toString();
	}
	
	/**
	 * FOR UPDATE SQL取得。<br>
	 * @return FOR UPDATE 文字列
	 */
	protected String getForUpdate()  {
		// GAE
		conn.beginTransaction();
/*		if (conn.getTransaction() == null) {
			DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService();
			conn.setTransaction(datastoreService.beginTransaction());
		}*/
		return null;
	}
	
	/**
	 * ORDER BY SQL取得。<br>
	 * @return ORDER BY 文字列
	 */
	protected String getOrderBy()  {
		return "ORDER BY ";
	}
	
	/**
	 * DESC SQL取得。<br>
	 * @return DESC 文字列
	 */
	protected String getDesc()  {
		return "DESC ";
	}
	
	/**
	 * LIMIT SQL取得。<br>
	 * @return LIMIT 文字列
	 */
	protected String getLimit()  {
		return "LIMIT ";
	}

	/**
	 * OFFSET SQL取得。<br>
	 * @return OFFSET 文字列
	 */
	protected String getOffset()  {
		return "OFFSET ";
	}
	
	/**
	 * 共通情報取得。<br>
	 * @param dto 対象dto
	 * @throws SQLException
	 */
	protected void mappingCommonInfo(BaseDto dto) throws SQLException {
		if (!INSERT_DATE.equals("")) {
			Date insertDate = new Date(rs.getTimestamp(INSERT_DATE).getTime());
			dto.setInsertDate(insertDate);
		}
		if (!INSERT_USER.equals("")) {
			dto.setInsertUser(rs.getString(INSERT_USER));
		}
		if (!UPDATE_DATE.equals("")) {
			Date updateDate = new Date(rs.getTimestamp(UPDATE_DATE).getTime());
			dto.setUpdateDate(updateDate);
		}
		if (!UPDATE_USER.equals("")) {
			dto.setUpdateUser(rs.getString(UPDATE_USER));
		}
	}
	
	
	/**
	 * 共通情報設定。<br>
	 * @param isInsert 挿入文フラグ(true：挿入文、false：挿入文)
	 * @throws SQLException
	 */
	protected void setCommonParams(boolean isInsert) throws SQLException {
		Date date = new Date();
		if (isInsert) {
			if (!INSERT_DATE.equals("")) {
				setParam(index++, date);
			}
			if (!INSERT_USER.equals("")) {
				setParam(index++, userId);
			}
		}
		if (!UPDATE_DATE.equals("")) {
			setParam(index++, date);
		}
		if (!UPDATE_USER.equals("")) {
			setParam(index++, userId);
		}
	}
	
	
	/**
	 * 挿入件数確認。<br>
	 * @param expectedCount 想定される挿入件数
	 * @throws MospException
	 */
	protected void chkInsert(int expectedCount) throws MospException {
		if (cnt != expectedCount) {
			throw new MospException(MospConst.EX_INSERT_FAILED);
		}
	}
	
	
	/**
	 * 更新件数確認。<br>
	 * @param expectedCount 想定される更新件数
	 * @throws MospException
	 */
	protected void chkUpdate(int expectedCount) throws MospException {
		if (cnt != expectedCount) {
			throw new MospException(MospConst.EX_UPDATE_FAILED);
		}
	}
	
	
	/**
	 * 削除件数確認。<br>
	 * @param expectedCount 想定される削除件数
	 * @throws MospException
	 */
	protected void chkDelete(int expectedCount) throws MospException {
		if (cnt != expectedCount) {
			throw new MospException(MospConst.EX_DELETE_FAILED);
		}
	}
	
	/**
	 * RDBMS種類を取得する。<br>
	 * {@link #conn}の情報からRDBMSの種類を特定する。<br>
	 * @return RDBMS種類
	 * @throws MospException 
	 */
	protected int getRdbmsType() throws MospException {
		try {
			String rdbmsName = conn.getMetaData().getDatabaseProductName();
			if (rdbmsName.equals("MySQL")) {
				return MospConst.RDBMS_MYSQL;
			} else if (rdbmsName.equals("PostgreSQL")) {
				return MospConst.RDBMS_PGSQL;
			}
			LogUtility.log(cfg, "", "", "", 5,
					MospConst.LOG_MOSPERR, MospConst.LOG_MSG_3003 + rdbmsName);
			throw new MospException(MospConst.EX_UNKNOWN_RDBMS);
		} catch (SQLException e) {
			throw new MospException(
					MospConst.EX_RUNTIME_ERR, MospUtility.getStackTrace(e));
		}
	}
	
	/**
	 * テーブル定義取得SQLを取得する。<br>
	 * RDBMS依存機能。MySQLをベースとしている。<br>
	 * @param tableName テーブル名
	 * @return テーブル定義取得SQL
	 * @throws SQLException 
	 * @throws MospException 
	 */
	protected String getTableDescriptionQuery(
			String tableName
	) throws MospException {
		StringBuffer sb = new StringBuffer();
		switch(getRdbmsType()) {
			case MospConst.RDBMS_MYSQL:
				sb.append("SHOW FULL COLUMNS FROM ");
				sb.append(tableName);
				break;
			case MospConst.RDBMS_PGSQL:
				sb.append("SELECT ");
				sb.append("pg_attribute.attname as FIELD, ");
				sb.append("pg_type.typname as TYPE, ");
				sb.append("pg_description.description as COMMENT ");
				sb.append("FROM ");
				sb.append("pg_attribute, ");
				sb.append("pg_type, ");
				sb.append("pg_description, ");
				sb.append("pg_stat_all_tables ");
				sb.append("WHERE ");
				sb.append("pg_stat_all_tables.relname = '");
				sb.append(tableName.toLowerCase());
				sb.append("' ");
				sb.append("AND ");
				sb.append("pg_stat_all_tables.relid = pg_description.objoid ");
				sb.append("AND ");
				sb.append("pg_description.objsubid <> 0 ");
				sb.append("AND ");
				sb.append("pg_description.objoid = pg_attribute.attrelid ");
				sb.append("AND ");
				sb.append("pg_description.objsubid = pg_attribute.attnum ");
				sb.append("AND ");
				sb.append("pg_type.oid = pg_attribute.atttypid ");
				sb.append("ORDER BY ");
				sb.append("pg_description.objsubid");
				break;
			default:
		}
		return sb.toString();
	}
	
	/**
	 * 挿入SQLを取得する。<br>
	 * 挿入レコード数分を一度に挿入するSQLを作成する。<br>
	 * 但し、挿入レコード最大数以上はSQLを作成しない。<br>
	 * @param cls  daoクラス
	 * @param size 挿入レコード数
	 * @param max  挿入レコード最大数
	 * @return 挿入SQL文字列
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException 
	 */
	protected String getInsertQuery(
			Class<?> cls, int size, int max
	) throws IllegalAccessException, NoSuchFieldException {
		StringBuffer query = new StringBuffer();
		query.append(getInsertQueryBase(cls));
		// パラメータ個数
		Field[] fields = getClass().getFields();
		// 1レコード分SQL作成
		StringBuffer sb = new StringBuffer();
		sb.append("(");
		for (int i = 0; i < fields.length; i++) {
			Field field = fields[i];
			String name = field.getName();
			if (name.indexOf("COL_") == 0) {
				sb.append("?, ");
			}
		}
		sb.append(getCommonParams());
		sb.delete(sb.length() - 2, sb.length());
		sb.append("), ");
		// 件数分作成(但しmaxを上限とする)
		for (int i = 0; i < size; i++) {
			if (i == max) {
				break;
			}
			query.append(sb);
		}
		query.delete(query.length() - 2, query.length());
		return query.toString();
	}
	
	/**
	 * 更新系SQLを実行する。<br>
	 * 処理インデックスが挿入レコード上限数か挿入対象件数に達した場合、
	 * 挿入処理を行う。<br>
	 * {@link #getInsertQuery(Class)}と併せて用いる。
	 * 大量のデータを挿入する場合、パフォーマンスの向上が見込める。<br>
	 * @param cls   dtoクラス
	 * @param size  挿入対象件数
	 * @param max   挿入レコード上限数(一度に挿入する上限)
	 * @param idx   処理インデックス
	 * @throws SQLException
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException 
	 */
	protected void executeUpdate(
			Class<?> cls, int size, int max, int idx
	) throws SQLException, IllegalAccessException, NoSuchFieldException {
		// インデックスを実際の件数に合わせる
		int executeIndex = idx + 1;
		// 挿入レコード上限数か挿入対象件数に達した場合
		if (executeIndex % max == 0 || executeIndex == size) {
			executeUpdate(false);
			clearParams();
			index = 1;
			if (size - executeIndex < max && executeIndex != size) {
				releasePreparedStatement();
				prepareStatement(getInsertQuery(cls, size - executeIndex, max));
			}
		} else {
			cnt = 0;
		}
	}
	
	/**
	 * GAE対応クエリを設定する
	 * @param mode
	 * @param properties
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException
	 */
	protected void setPreparedStatementWithProperties(
			PreparedStatement.Mode mode, String[] properties
	) throws IllegalAccessException, NoSuchFieldException {
		this.setPreparedStatement(mode);
		PreparedOption preparedOption = new PreparedOption();
		for (String property : properties) {
			preparedOption.add(new PreparedProperty(property));
		}
		this.pob.appendStoredOption(preparedOption);
	}
}
