#% GAE・DAOJavaソースファイル作成用の雛型ファイル
#% 2010/09/17 By S.Ito

#% === GAE・DAO =====================================================================
#! dao package
package #package#;

#! header
#loggerComment#import org.apache.log4j.Logger;
import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Transaction;

/**
 * #nameJp# GAE・データストアアクセスクラス
 */
@SuppressWarnings("serial")
public class #mstName#Dao extends GaeDaoBase implements RecordSearch, RecordAction, Serializable {

	// GAEトランザクション
	protected Transaction tran = null;

	// 検索用のリスト
	private List<#mstName#> searchList;

	// ロガー
	#loggerComment#private static Logger logger = Logger.getLogger(#mstName#Dao.class);

	static {
		// フィールド長情報設定
#! field length
		fieldLengthInfoMap.put("#fieldName#", new FieldLengthInfo(#length#, #subLength#));
#! header 1

		// フィルター使用可能フィールド設定
#! filter field
		canUseFilterFieldSet.add("#fieldName#");
#! header 1.2

		// 検索条件指定可能フィールド設定
#! where field
		canUseWhereFieldSet.add("#fieldName#");
#! header 1.4

		// ソート可能フィールド設定
#! sort field
		canUseSortFieldSet.add("#fieldName#");
#! header 2
	}

	/**
	 * コンストラクタ<BR>
	 * 単独で使用するとき
	 */
	public #mstName#Dao() {
		this.tran = null;
	}

	/**
	 * コンストラクタ<BR>
	 * 複数のDAOを同一トランザクション内で使用するとき
	 *
	 * @param tran
	 *            GAEデータストアトランザクション
	 */
	public #mstName#Dao(Transaction tran) {
		this.tran = tran;
	}

	@Override
	public String getKindName() {
		return #mstName#.class.getSimpleName();
	}

	@Override
	public Key createKey(DatastoreService ds, Object keyRec) {
		#mstName# rec = (#mstName#) keyRec;
		Key key = null;
#! create key 1
		key = createKey(ds, key, "#kindName#", "#fieldName#", rec.#getMethod#());
#! create key 2
		key = createKey(ds, key, getKindName(), "#fieldName#", rec.#getMethod#());
#! header 3

		return key;
	}

	@Override
	public List<SortKey> getPrimarySortKeyList(){

		List<SortKey> primarySortKeyList = new LinkedList<SortKey>();

#! get sort key
		primarySortKeyList.add(new SortKey("#fieldName#", SORT_MODE_ASC));
#! header 3.5

		return primarySortKeyList;
	}

	@Override
	public void setAutoNumberField(DatastoreService ds, Object record) throws SQLException {
		#mstName# rec = (#mstName#) record;

#! auto number field int
		if (rec.#getMethod#() == null) {
			long sno = getSequence("#fieldName#");
			#loggerComment#logger.debug("get sequence #mstName#.#fieldName# = " + sno);
			rec.#setMethod#((int)sno);
		}
#! auto number field long
		if (rec.#getMethod#() == null) {
			long sno = getSequence("#fieldName#");
			#loggerComment#logger.debug("get sequence #mstName#.#fieldName# = " + sno);
			rec.#setMethod#(sno);
		}
#! auto number field bigdecimal
		if (rec.#getMethod#() == null) {
			long sno = getSequence("#fieldName#");
			#loggerComment#logger.debug("get sequence #mstName#.#fieldName# = " + sno);
			rec.#setMethod#(BigDecimal.valueOf(sno));
		}
#! header 4
		#loggerComment#logger.debug("insert #mstName# : " + rec.toString());
	}

	@Override
	public void setEntityAll(Entity entity, Object record) {
		#mstName# rec = (#mstName#) record;

#! set entity string
		entity.setProperty("#fieldName#", rec.#getMethod#());
#! set entity string text
		entity.setProperty("#fieldName#", changeGaeSafeText(rec.#getMethod#()));
#! set entity int
		entity.setProperty("#fieldName#", rec.#getMethod#());
#! set entity long
		entity.setProperty("#fieldName#", rec.#getMethod#());
#! set entity bigdecimal
		entity.setProperty("#fieldName#", changeGaeSafeBigDecimal(rec.#getMethod#(), "#fieldName#"));
#! set entity byte
		entity.setProperty("#fieldName#", changeGaeSafeBlob(rec.#getMethod#()));
#! set entity date
		entity.setProperty("#fieldName#", changeGaeSafeDate(rec.#getMethod#()));
#! set entity time
		entity.setProperty("#fieldName#", changeGaeSafeDate(rec.#getMethod#()));
#! set entity timestamp
		entity.setProperty("#fieldName#", changeGaeSafeDate(rec.#getMethod#()));
#! set entity boolean
		entity.setProperty("#fieldName#", rec.#getMethod#());
#! header 5
	}

	@Override
	public Object createRecord(Entity entity) {
		#mstName# rec = new #mstName#();

#! set rec string
		rec.#setMethod#(convertStr(entity.getProperty("#fieldName#")));
#! set rec int
		rec.#setMethod#(convertInt(entity.getProperty("#fieldName#")));
#! set rec long
		rec.#setMethod#(convertLong(entity.getProperty("#fieldName#")));
#! set rec bigdecimal
		rec.#setMethod#(convertBigDecimal(entity.getProperty("#fieldName#")));
#! set rec byte
		rec.#setMethod#(convertByte(entity.getProperty("#fieldName#")));
#! set rec date
		rec.#setMethod#(convertDate(entity.getProperty("#fieldName#")));
#! set rec time
		rec.#setMethod#(convertTime(entity.getProperty("#fieldName#")));
#! set rec timestamp
		rec.#setMethod#(convertTimestamp(entity.getProperty("#fieldName#")));
#! set rec boolean
		rec.#setMethod#(convertBoolean(entity.getProperty("#fieldName#")));
#! header 6

		return rec;
	}

	@Override
	public Object createPrimaryKeyRecord(Entity entity) {
		#mstName# rec = new #mstName#();

#! body

		return rec;
	}

	/**
	 * #nameJp#に1レコード挿入する<br>
	 *
	 * @param rec
	 *            #nameJp#レコード
	 *
	 * @return 挿入件数（ユニークインデックス又は主キーエラー又は楽観的排他制御エラー時は0）
	 * @throws SQLException
	 */
	public int insert(#mstName# rec) throws SQLException {
		try{
			int rtn = super.insert(tran, rec);
			#loggerComment#logger.debug("insert 結果=" + rtn);
			return rtn;
		} catch(SQLException e){
			#loggerComment#logger.fatal("データストアへのレコード挿入処理でエラーが発生しました", e);
			throw e;
		} catch(RuntimeException e){
			#loggerComment#logger.fatal("データストアへのレコード挿入処理でエラーが発生しました", e);
			throw e;
		}
	}

	/**
	 * #nameJp#を更新する
	 *
	 * @param rec
	 *            #nameJp#レコード
	 *
	 * @return 更新件数（既に削除済み又は楽観的排他制御エラー時は0）
	 * @throws SQLException
	 */
	public int update(final #mstName# rec) throws SQLException {
		try{
			#loggerComment#logger.debug("update #mstName# : " + rec.toString());
			int rtn = super.update(tran, rec);
			#loggerComment#logger.debug("update 結果=" + rtn);
			return rtn;
		} catch(SQLException e){
			#loggerComment#logger.fatal("データストアへのレコード更新処理でエラーが発生しました", e);
			throw e;
		} catch(RuntimeException e){
			#loggerComment#logger.fatal("データストアへのレコード更新処理でエラーが発生しました", e);
			throw e;
		}
	}

	/**
	 * #nameJp#を削除する
	 *
	 * @param keyRecs
	 *            #nameJp#レコードのキー配列
	 *
	 * @return 削除件数(実際に削除された件数ではなくrecsの件数が返される、ただし楽観的排他制御エラー時は0が返される)
	 * @throws SQLException
	 */
	public int delete(#mstName#... keyRecs) throws SQLException {
		try{
			#loggerComment#for(#mstName# keyRec : keyRecs){
			#loggerComment#	logger.debug("delete #mstName# : " + keyRec.toString());
			#loggerComment#}

			int rtn =  super.delete(tran, keyRecs);
			#loggerComment#logger.debug("delete 結果=" + rtn);
			return rtn;
		} catch(SQLException e){
			#loggerComment#logger.fatal("データストアへのレコード削除処理でエラーが発生しました", e);
			throw e;
		} catch(RuntimeException e){
			#loggerComment#logger.fatal("データストアへのレコード削除処理でエラーが発生しました", e);
			throw e;
		}
	}

	/**
	 * #nameJp#を主キーを指定して取得
	 *
	 * @param keyRec
	 *            #nameJp#主キーレコード
	 * @return 取得された#nameJp#のレコード（対応するデータが存在しないときはnull）
	 * @throws SQLException
	 */
	public #mstName# get(#mstName# keyRec) throws SQLException {
		try{
			#loggerComment#logger.debug("get #mstName# : key record : " + keyRec.toString());
			#mstName# rec = (#mstName#) super.get(tran, keyRec);
			#loggerComment#logger.debug("get 結果 = " + (rec == null ? "0" : "1"));
			return rec;
		} catch(SQLException e){
			#loggerComment#logger.fatal("データストアへのレコード取得処理でエラーが発生しました", e);
			throw e;
		} catch(RuntimeException e){
			#loggerComment#logger.fatal("データストアへのレコード取得処理でエラーが発生しました", e);
			throw e;
		}
	}

	/**
	 * 指定されたkeyRecord内の有効な（nullでない）主キーを検索条件とし<BR>
	 * #nameJp#を検索し主キーのみ格納されてレコードのリストを取得する
	 *
	 * @param keyRecord
	 *            #nameJp#主キーレコード
	 * @return 取得された#nameJp#（主キーのみ格納）のリスト
	 * @throws SQLException
	 */
	public List<#mstName#> getPrimaryKeys(#mstName# keyRecord) throws SQLException {
		try {
			clearSearchParameter();

			// 有効な主キーの設定
#! body set primary key
			if (keyRecord.#getMethod#() != null) {
				addSearchParameter("#fieldName#", equal, keyRecord.#getMethod#());
			}
#! body 2

			try {
				#loggerComment#logger.debug("getPrimaryKeys : #mstName# : " + getSearchParam());
				searchPrimaryKey(tran, this);
			} catch (Exception e) {
				throw new SQLException(e);
			}

			List<#mstName#> list = searchList;
			searchList = null;

			#loggerComment#logger.debug("getPrimaryKeys 結果 = "+list.size());

			return list;
		} catch(SQLException e){
			#loggerComment#logger.fatal("データストアへの主キー検索処理でエラーが発生しました", e);
			throw e;
		} catch(RuntimeException e){
			#loggerComment#logger.fatal("データストアへの主キー検索処理でエラーが発生しました", e);
			throw e;
		}
	}

	/**
	 * #nameJp#検索
	 *
	 * @return 検索された#nameJp#のレコードリスト
	 * @throws SQLException
	 */
	public List<#mstName#> search() throws Exception {
		try{
			#loggerComment#logger.debug("search : #mstName# : " + getSearchParam());
			search(this);

			List<#mstName#> list = searchList;
			searchList = null;

			#loggerComment#logger.debug("search 結果 = "+list.size());

			return list;
		} catch(Exception e){
			#loggerComment#logger.fatal("データストアへのレコード検索処理でエラーが発生しました", e);
			throw e;
		}
	}

#! footer
	@Override
	public void search(RecordAction searchAction) throws Exception {
		search(tran, searchAction);
	}

	@Override
	public void initAction() throws Exception {
		searchList = new LinkedList<#mstName#>();
	}

	@Override
	public void action(Object record) throws Exception {
		searchList.add((#mstName#) record);
	}

	@Override
	public void closeAction() throws Exception {
		// 何もしない
	}
}
#!

#% === GAE・DAO Base =====================================================================
#! GaeDaoBase package
package #package#;

#! GaeDaoBase main
import java.math.BigDecimal;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;

import com.google.appengine.api.datastore.Blob;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.DatastoreTimeoutException;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.KeyRange;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Text;
import com.google.appengine.api.datastore.Transaction;
import com.google.appengine.api.datastore.Query.FilterOperator;

/**
 * GAE用DAOの基底となるクラス
 */
public abstract class GaeDaoBase {

	// 検索用モード：等しい（完全一致）
	public static final int equal = 1;

	// 検索用モード：前方一致（文字型のみ有効）
	public static final int like = 2;

	// 検索用モード：大なりイコール（>=）
	public static final int bigEqual = 3;

	// 検索用モード：大なり（>）
	public static final int big = 4;

	// 検索用モード：小なりイコール（<=）
	public static final int smallEqual = 5;

	// 検索用モード：小なり（<）
	public static final int small = 6;

	// 検索用モード：不一致（<>）
	public static final int notEqual = 7;

	// 検索用のキーと値リスト
	private List<SearcKeyValue> searchKeyValueList = new LinkedList<SearcKeyValue>();

	// ソートモード：昇順
	protected static final int SORT_MODE_ASC = 1;

	// ソートモード：降順
	protected static final int SORT_MODE_DESC = 2;

	// ソートキーリスト
	private List<SortKey> sortKeyList = new LinkedList<SortKey>();

	// フィールド長情報
	protected static HashMap<String, FieldLengthInfo> fieldLengthInfoMap = new HashMap<String, FieldLengthInfo>();

	// フィルター使用不可能フィールド名のセット
	protected static HashSet<String> canUseFilterFieldSet = new HashSet<String>();

	// 検索条件として指定不可能なフィールド名のセット
	protected static HashSet<String> canUseWhereFieldSet = new HashSet<String>();

	// ソート不可能なフィールド名のセット
	protected static HashSet<String> canUseSortFieldSet = new HashSet<String>();

	/**
	 * GAEデータストアに1レコードを挿入する<br>
	 *
	 * @param tran
	 *            GAEのトランザクション
	 * @param rec
	 *            挿入するレコード
	 *
	 * @return 挿入件数（主キーエラー又は楽観的排他制御エラー時は0）
	 * @throws SQLException
	 */
	public int insert(final Transaction tran, final Object rec) throws SQLException {

		final DatastoreService ds = DatastoreServiceFactory.getDatastoreService();

		ExecDatastore execObject = new ExecDatastore() {

			@Override
			public Object exec(Transaction tran) throws SQLException {

				// 自動採番設定
				setAutoNumberField(ds, rec);

				// 主キー作成
				Key key = createKey(ds, rec);

				// 主キー存在チェック
				try {
					ds.get(tran, key);
					return 0;
				} catch (EntityNotFoundException e) {
					// 何もしない
				}

				// エンティティ作成
				Entity entity = new Entity(key);
				setEntityAll(entity, rec);
				ds.put(tran, entity);

				return 1;
			}
		};

		try {
			return (Integer) exec(ds, execObject, tran, true);
		} catch (ConcurrentModificationException e) { // 楽観的排他制御エラー
			return 0;
		}
	}

	/**
	 * GAEデータストアの1レコードを更新する<br>
	 *
	 * @param tran
	 *            GAEのトランザクション
	 * @param rec
	 *            更新するレコード
	 *
	 * @return 更新件数（主キーエラー又は楽観的排他制御エラー時は0）
	 * @throws SQLException
	 */
	public int update(final Transaction tran, final Object rec) throws SQLException {

		final DatastoreService ds = DatastoreServiceFactory.getDatastoreService();

		ExecDatastore execObject = new ExecDatastore() {

			@Override
			public Object exec(Transaction tran) {

				// 主キー作成
				Key key = createKey(ds, rec);

				// 主キー存在チェック
				try {
					ds.get(tran, key);
				} catch (EntityNotFoundException e) {
					return 0;
				}

				// エンティティ作成
				Entity entity = new Entity(key);
				setEntityAll(entity, rec);
				ds.put(tran, entity);
				return 1;
			}
		};

		try {
			return (Integer) exec(ds, execObject, tran, true);
		} catch (ConcurrentModificationException e) { // 楽観的排他制御エラー
			return 0;
		}
	}

	/**
	 * GAEデータストアのレコードを削除する<br>
	 *
	 * @param tran
	 *            GAEのトランザクション
	 * @param recs
	 *            削除するレコード
	 *
	 * @return 削除件数(実際に削除された件数ではなくrecsの件数が返される、ただし楽観的排他制御エラー時は0が返される)
	 * @throws SQLException
	 */
	public int delete(final Transaction tran, final Object... recs) throws SQLException {

		final DatastoreService ds = DatastoreServiceFactory.getDatastoreService();

		ExecDatastore execObject = new ExecDatastore() {

			@Override
			public Object exec(Transaction tran) {
				// // 主キー配列作成
				Key[] keys = new Key[recs.length];
				for (int i = 0; i < recs.length; i++) {
					keys[i] = createKey(ds, recs[i]);
				}
				ds.delete(tran, keys);
				return keys.length;
			}
		};

		return (Integer) exec(ds, execObject, tran, true);
	}

	/**
	 * GAEデータストアのレコードを取得する<br>
	 *
	 * @param tran
	 *            GAEのトランザクション
	 * @param keyRec
	 *            キーが格納されているレコード
	 *
	 * @return 取得されたレコード（対応するデータが存在しないときはnull）
	 * @throws SQLException
	 */
	public Object get(final Transaction tran, final Object keyRec) throws SQLException {

		final DatastoreService ds = DatastoreServiceFactory.getDatastoreService();

		ExecDatastore execObject = new ExecDatastore() {

			@Override
			public Object exec(Transaction tran) {
				// 主キー作成
				Key key = createKey(ds, keyRec);

				// 主キー存在チェック
				try {

					Entity entity = ds.get(tran, key);
					return createRecord(entity);

				} catch (EntityNotFoundException e) {
					return null;
				}

			}
		};

		try {
			return exec(ds, execObject, tran, false);
		} catch (ConcurrentModificationException e) { // 楽観的排他制御エラー
			return 0;
		}
	}

	/**
	 * 主キー検索
	 *
	 * @param tran
	 *            GAEのトランザクション
	 * @param searchAction
	 *            searchActionオブジェクト
	 * @throws Exception
	 */
	public void searchPrimaryKey(final Transaction tran, final RecordAction searchAction) throws Exception {

		final DatastoreService ds = DatastoreServiceFactory.getDatastoreService();

		ExecDatastore execObject = new ExecDatastore() {

			@Override
			public Object exec(Transaction tran) {
				Exception exception = null;
				try {
					try {
						searchAction.initAction();

						Query query = new Query(getKindName());

						// フィルター設定
						List<SearcKeyValue> inMemoryFilterList = new LinkedList<SearcKeyValue>();
						setFilter(query, inMemoryFilterList);

						List<Entity> list = new ArrayList<Entity>();
						for (Entity entity : ds.prepare(query).asIterable()) {
							// フィルターで設定できない条件を満たすレコードをリストに追加
							if (canSaveEntity(entity, inMemoryFilterList)) {
								list.add(entity);
							}
						}

						// 主キーでソートを行う
						Entity[] sortedEntity = sortEntity(list, getPrimarySortKeyList());

						for (Entity entity : sortedEntity) {
							searchAction.action(createPrimaryKeyRecord(entity));
						}

					} finally {
						searchAction.closeAction();
					}
				} catch (Exception e) {
					exception = e;
				}

				return exception;
			}
		};

		Exception exception = (Exception) exec(ds, execObject, tran, false);
		if (exception != null) {
			throw exception;
		}
	}

	/**
	 * 検索を行う
	 *
	 * @param tran
	 *            GAEのトランザクション
	 * @param searchAction
	 *            searchActionオブジェクト
	 * @throws SQLException
	 */
	public void search(final Transaction tran, final RecordAction searchAction) throws Exception {

		final DatastoreService ds = DatastoreServiceFactory.getDatastoreService();

		ExecDatastore execObject = new ExecDatastore() {

			@Override
			public Object exec(Transaction tran) {
				Exception exception = null;
				try {
					try {
						searchAction.initAction();

						Query query = new Query(getKindName());

						// フィルター設定
						List<SearcKeyValue> inMemoryFilterList = new LinkedList<SearcKeyValue>();
						setFilter(query, inMemoryFilterList);

						List<Entity> list = new ArrayList<Entity>();
						for (Entity entity : ds.prepare(query).asIterable()) {
							// フィルターで設定できない条件を満たすレコードをリストに追加
							if (canSaveEntity(entity, inMemoryFilterList)) {
								list.add(entity);
							}
						}

						// ソートを行う
						Entity[] sortedEntity = sortEntity(list);

						for (Entity entity : sortedEntity) {
							searchAction.action(createRecord(entity));
						}

					} finally {
						searchAction.closeAction();
					}
				} catch (Exception e) {
					exception = e;
				}

				return exception;
			}
		};

		Exception exception = (Exception) exec(ds, execObject, tran, false);
		if (exception != null) {
			throw exception;
		}
	}

	/**
	 * ExecDatastoreオブジェクトを実行する<br>
	 * outsideTranが設定されている時はそのトランザクションでExecDatastoreオブジェクトを実行する<br>
	 * この場合はタイムアウトのリトライ処理、トランザクションのコミット、ロールバックは外部で制御されるものとみなし行われない<br>
	 * <br>
	 * outsideTranが未設定(null)でuseTranがfalseの時はトランザクションを無効（null）
	 * にしてExecDatastoreオブジェクトを実行する<br>
	 * この場合はタイムアウトのリトライ処理は行われる <br>
	 * outsideTranが未設定(null)でuseTranがtrueの時は新たなトランザクションでExecDatastoreオブジェクトを実行する<br>
	 * この場合はタイムアウトのリトライ処理、トランザクションのコミット、ロールバックは自動的に行われる<br>
	 *
	 * @param ds
	 *            データストアサービス
	 * @param execObjct
	 *            ExecDatastoreオブジェクト
	 * @param outsideTran
	 *            外部トランザクション
	 * @param useTran
	 *            トランザクション使用フラグ（outsideTranがnullでこのフラグがfalseのときはトランザクションは使用されない）
	 * @return 処理結果のオブジェクト
	 * @throws SQLException
	 */
	protected Object exec(DatastoreService ds, ExecDatastore execObjct, Transaction outsideTran, boolean useTran)
			throws SQLException {

		if (outsideTran != null) {

			return execObjct.exec(outsideTran);

		} else if (!useTran) {

			long waitMs = 100L;
			for (int i = 0; i < 5; i++) {

				try {

					return execObjct.exec(null);

				} catch (DatastoreTimeoutException e) {

					try {
						Thread.sleep(waitMs);
					} catch (InterruptedException e1) {
						// 何もしない
					}
					waitMs *= 2;
				}

			}
			throw new SQLException("タイムアウトが発生しました");

		} else {

			long waitMs = 100L;
			Transaction tran = null;
			try {
				for (int i = 0; i < 5; i++) {
					tran = null;

					try {

						tran = ds.beginTransaction();
						Object rtn = execObjct.exec(tran);

						tran.commit();
						return rtn;

					} catch (DatastoreTimeoutException e) { // タイムアウト

						if (tran != null && tran.isActive()) {
							tran.rollback();
						}

						try {
							Thread.sleep(waitMs);
						} catch (InterruptedException e1) {
							// 何もしない
						}
						waitMs *= 2;
					}

				}
				throw new SQLException("タイムアウトが発生しました");

			} finally {
				if (tran != null && tran.isActive()) {
					tran.rollback();
				}
			}
		}
	}

	/**
	 * 文字列をGAE用のTextオブジェクトに変換する
	 *
	 * @param val
	 *            文字列
	 * @return Textオブジェクト
	 */
	protected Text changeGaeSafeText(String val) {
		if (val == null) {
			return null;
		}

		return new Text(val);
	}

	/**
	 * バイト配列をGAE用のBlobオブジェクトに変換する
	 *
	 * @param val
	 *            バイト配列
	 * @return Blobオブジェクト
	 */
	protected Blob changeGaeSafeBlob(byte[] val) {
		if (val == null) {
			return null;
		}

		if (val instanceof byte[]) {
			return new Blob((byte[]) val);
		}
		return null;
	}

	/**
	 * java.sql.Date、java.sql.Time、java.sql.Timestampのオブジェクトをjava.util.Dateに変換する
	 *
	 * @param val
	 *            java.sql.Date、java.sql.Time、java.sql.Timestampのオブジェクト
	 * @return java.util.Dateオブジェクト
	 */
	protected Date changeGaeSafeDate(Date val) {
		if (val == null) {
			return null;
		}

		return new Date(val.getTime());
	}

	/**
	 * BigDecimalを大小比較可能な文字列に変換する
	 *
	 * @param val
	 *            BigDecimalオブジェクト
	 * @param fieldName
	 *            フィールド名
	 * @return 大小比較可能な文字列
	 */
	protected String changeGaeSafeBigDecimal(BigDecimal val, String fieldName) {

		if (val == null) {
			return null;
		}

		FieldLengthInfo fieldLengthInfo = fieldLengthInfoMap.get(fieldName);
		if (fieldLengthInfo == null) {
			throw new RuntimeException("BigDecimalのフィールドがフィールド長情報マップに存在しません " + fieldName);
		}

		BigDecimal maxVal = numMax(fieldLengthInfo.length, fieldLengthInfo.subLength);
		BigDecimal minVal = maxVal.negate();

		if (val.compareTo(maxVal) > 0 || val.compareTo(minVal) < 0) {
			throw new RuntimeException("桁数オーバーです " + fieldName);
		}

		DecimalFormat df = new DecimalFormat(numFormat(fieldLengthInfo.length, fieldLengthInfo.subLength));

		String strVal = "";
		if (val.signum() < 0) {
			strVal = "-" + df.format(val.negate());
		} else {
			strVal = "0" + df.format(val);
		}

		return strVal;
	}

	/**
	 * ValueObjectへのセット用にオブジェクトを文字列に変換する
	 *
	 * @param val
	 *            オブジェクト
	 * @return 文字列（ただし、オブジェクトがnullのときはnull）
	 */
	protected String convertStr(Object val) {
		if (val == null) {
			return null;
		}

		if (val instanceof Text) {
			return ((Text) val).getValue();
		}

		if (val instanceof String) {
			return (String) val;
		}
		return val.toString();
	}

	/**
	 * ValueObjectへのセット用にオブジェクトをIntegerオブジェクトに変換する<br>
	 * もし、オブジェクトがLongの時でIntegerの最大最小値を 超えていたら、最大又は最小値にまるめられる
	 *
	 * @param val
	 *            オブジェクト
	 * @return Integerオブジェクト（ただし、オブジェクトがnullのときはnull）
	 */
	protected Integer convertInt(Object val) {
		if (val == null) {
			return null;
		}

		if (val instanceof Integer) {
			return (Integer) val;
		}

		if (val instanceof Long) {
			if (((Long) val) > Integer.MAX_VALUE) {
				return Integer.MAX_VALUE;
			}
			if (((Long) val) < Integer.MIN_VALUE) {
				return Integer.MIN_VALUE;
			}
			long longVal = (Long) val;
			return (int) (longVal);
		}

		try {
			return Integer.parseInt(val.toString());
		} catch (NumberFormatException e) {
			return null;
		}
	}

	/**
	 * ValueObjectへのセット用にオブジェクトをLongオブジェクトに変換する
	 *
	 * @param val
	 *            オブジェクト
	 * @return Longオブジェクト（ただし、オブジェクトがnullのときはnull）
	 */
	protected Long convertLong(Object val) {
		if (val == null) {
			return null;
		}

		if (val instanceof Integer) {
			int intVal = (Integer) val;
			return (long) intVal;
		}

		if (val instanceof Long) {
			return (Long) (val);
		}

		try {
			return Long.parseLong(val.toString());
		} catch (NumberFormatException e) {
			return null;
		}
	}

	/**
	 * ValueObjectへのセット用にオブジェクトをbyte配列に変換する
	 *
	 * @param val
	 *            オブジェクト
	 * @return byte配列（ただし、オブジェクトがnullのときはnull）
	 */
	protected byte[] convertByte(Object val) {
		if (val == null) {
			return null;
		}

		if (val instanceof Blob) {
			return ((Blob) val).getBytes();
		}
		return null;
	}

	/**
	 * ValueObjectへのセット用にオブジェクトをBigDecimalオブジェクトに変換する
	 *
	 * @param val
	 *            オブジェクト
	 * @return BigDecimalオブジェクト（ただし、オブジェクトがnullのときはnull）
	 */
	protected BigDecimal convertBigDecimal(Object val) {
		if (val == null) {
			return null;
		}

		if (val instanceof Integer) {
			int intVal = (Integer) val;
			return BigDecimal.valueOf((long) intVal);
		}

		if (val instanceof Long) {
			return BigDecimal.valueOf((Long) val);
		}

		try {
			return new BigDecimal(val.toString());
		} catch (NumberFormatException e) {
			return null;
		}
	}

	/**
	 * ValueObjectへのセット用にオブジェクトをjava.sql.Dateオブジェクトに変換する
	 *
	 * @param val
	 *            オブジェクト
	 * @return java.sql.Dateオブジェクト（ただし、オブジェクトがnullのときはnull）
	 */
	protected java.sql.Date convertDate(Object val) {
		if (val == null) {
			return null;
		}

		if (val instanceof Date) {
			return new java.sql.Date(((Date) val).getTime());
		}

		return null;
	}

	/**
	 * ValueObjectへのセット用にオブジェクトをjava.sql.Timeオブジェクトに変換する
	 *
	 * @param val
	 *            オブジェクト
	 * @return java.sql.Timeオブジェクト（ただし、オブジェクトがnullのときはnull）
	 */
	protected java.sql.Time convertTime(Object val) {
		if (val == null) {
			return null;
		}

		if (val instanceof Date) {
			return new java.sql.Time(((Date) val).getTime());
		}

		return null;
	}

	/**
	 * ValueObjectへのセット用にオブジェクトをjava.sql.Timestampオブジェクトに変換する
	 *
	 * @param val
	 *            オブジェクト
	 * @return java.sql.Timestampオブジェクト（ただし、オブジェクトがnullのときはnull）
	 */
	protected java.sql.Timestamp convertTimestamp(Object val) {
		if (val == null) {
			return null;
		}

		if (val instanceof Date) {
			return new java.sql.Timestamp(((Date) val).getTime());
		}

		return null;
	}

	/**
	 * ValueObjectへのセット用にオブジェクトをBooleanオブジェクトに変換する
	 *
	 * @param val
	 *            オブジェクト
	 * @return Booleanオブジェクト（ただし、オブジェクトがnullのときはnull）
	 */
	protected Boolean convertBoolean(Object val) {
		if (val == null) {
			return null;
		}

		if (val instanceof Boolean) {
			return (Boolean) val;
		}

		return null;
	}

	/**
	 * BigDecimal用のフォーマッターで使用する文字列を作成する
	 *
	 * @param len
	 *            全体の長さ
	 * @param slen
	 *            小数部の長さ
	 * @return フォーマッターで使用する文字列
	 */
	protected String numFormat(int len, int slen) {
		return str('0', len - slen) + "." + str('0', slen);
	}

	/**
	 * 指定された長さと小数部の長さを持つ最大の数値の文字列を作成する
	 *
	 * @param len
	 *            全体の長さ
	 * @param slen
	 *            小数部の長さ
	 * @return 最大の数値の文字列
	 */
	protected BigDecimal numMax(int len, int slen) {
		return new BigDecimal(str('9', len - slen) + "." + str('9', slen));
	}

	/**
	 * 連続した文字の文字列を作成する
	 *
	 * @param c
	 *            文字
	 * @param len
	 *            文字数
	 * @return 連続した文字の文字列
	 */
	protected String str(char c, int len) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < len; i++) {
			sb.append(c);
		}
		return sb.toString();
	}

	/**
	 * キーの作成
	 *
	 * @param ds
	 *            データストアサービス
	 * @param parentKey
	 *            親キー（存在しない時はnull）
	 * @param kind
	 *            カインド名
	 * @param fieldName
	 *            フィールド名
	 * @param keyValue
	 *            キー値
	 * @return キー
	 */
	protected Key createKey(DatastoreService ds, Key parentKey, String kind, String fieldName, Object keyValue) {
		if (keyValue == null) {
			throw new RuntimeException("主キーにnullは設定できません " + fieldName);
		}

		Key key = null;

		String nameKey = null;
		long idKey = 0;

		do {
			if (keyValue instanceof String) {
				nameKey = (String) keyValue;
				break;
			}

			if (keyValue instanceof Integer) {
				idKey = (Integer) keyValue;
				break;
			}

			if (keyValue instanceof Long) {
				idKey = (Long) keyValue;
				break;
			}

			if (keyValue instanceof BigDecimal) {
				nameKey = changeGaeSafeBigDecimal((BigDecimal) keyValue, fieldName);
				break;
			}

			if (keyValue instanceof byte[]) {
				throw new RuntimeException("主キーにバイト配列は設定できません " + fieldName);
			}

			if (keyValue instanceof java.sql.Date) {
				idKey = ((java.sql.Date) keyValue).getTime();
				break;
			}

			if (keyValue instanceof java.sql.Time) {
				idKey = ((java.sql.Time) keyValue).getTime();
				break;
			}

			if (keyValue instanceof java.sql.Timestamp) {
				idKey = ((java.sql.Timestamp) keyValue).getTime();
				break;
			}

			if (keyValue instanceof Boolean) {
				nameKey = ((Boolean) keyValue) ? "true" : "false";
				break;
			}

			throw new RuntimeException("主キーに設定不可能なオブジェクトが設定されました " + fieldName);
		} while (false);

		if (parentKey == null) {
			if (nameKey != null) {
				key = KeyFactory.createKey(kind, nameKey);
			} else {
				key = KeyFactory.createKey(kind, idKey);
			}
		} else {
			if (nameKey != null) {
				key = KeyFactory.createKey(parentKey, kind, nameKey);
			} else {
				key = KeyFactory.createKey(parentKey, kind, idKey);
			}
		}

		return key;
	}

	/**
	 * 検索用パラメータをクリアする
	 */
	public void clearSearchParameter() {
		searchKeyValueList.clear();
	}

	/**
	 * 検索用パラメータを追加する
	 *
	 * @param keyNmae
	 * @param mode
	 * @param value
	 */
	public void addSearchParameter(String keyNmae, int mode, Object value) {
		if (!canUseWhereFieldSet.contains(keyNmae)) {
			throw new RuntimeException("検索条件として使用不可能なフィールドが指定されました" + keyNmae);
		}

		if (mode != equal && mode != like && mode != bigEqual && mode != big //
				&& mode != smallEqual && mode != small && mode != notEqual) {
			return;
		}

		if (value == null) {
			return;
		}

		if ((value instanceof String) && ((String) value).trim().length() <= 0) {
			return;
		}

		searchKeyValueList.add(new SearcKeyValue(keyNmae, mode, value));
	}
	
	/**
	 * 検索用パラメータ値を文字列として取得する
	 *
	 * @return
	 */
	protected String getSearchParam() {

		StringBuilder sb = new StringBuilder();
		for (SearcKeyValue searcKeyValue : searchKeyValueList) {
			sb.append(searcKeyValue.keyName);
			switch(searcKeyValue.searchMode){
			case equal:
				sb.append("=");
				break;
			case like:
				sb.append(" like ");
				break;
			case bigEqual:
				sb.append(">=");
				break;
			case big:
				sb.append(">");
				break;
			case smallEqual:
				sb.append("<=");
				break;
			case small:
				sb.append("<");
				break;
			case notEqual:
				sb.append("<>");
				break;
			default:
				sb.append("?");
			}
			if (searcKeyValue.value instanceof String && searcKeyValue.searchMode == like) {
				sb.append(changeString((String) searcKeyValue.value)+ "%");
			} else {
				sb.append(changeString(searcKeyValue.value));
			}

			sb.append(", ");
		}
		
		return sb.toString();
	}

	/**
	 * ObjectからStringへの変換を行う
	 *
	 * @param value
	 *            変換元値
	 * @return 変換された値
	 */
	public static String changeString(Object value) {
		if (value == null) {
			return null;
		}
		if (value instanceof String) {
			return changeString((String) value, false);
		}
		if (value instanceof Integer) {
			return changeString((Integer) value, false);
		}
		if (value instanceof Long) {
			return changeString((Long) value, false);
		}
		if (value instanceof BigDecimal) {
			return changeString((BigDecimal) value, false);
		}
		if (value instanceof byte[]) {
			return changeString((byte[]) value, false);
		}
		if (value instanceof Date) {
			return changeString((Date) value, false);
		}
		if (value instanceof java.sql.Time) {
			return changeString((java.sql.Time) value, false);
		}
		if (value instanceof java.sql.Timestamp) {
			return changeString((java.sql.Timestamp) value, false);
		}
		if (value instanceof Boolean) {
			return changeString((Boolean) value, false);
		}
		return value.toString();
	}
	/**
	 * StringからStringへの変換を行う
	 *
	 * @param value
	 *            変換元値
	 * @param boolean nullFlag 強制的に値をnullに変換する
	 * @return 変換された値
	 */
	public static String changeString(String value, boolean nullFlag) {
		if (value == null || nullFlag) {
			return null;
		}
		return value;
	}

	/**
	 * IntegerからStringへの変換を行う
	 *
	 * @param value
	 *            変換元値
	 * @param boolean nullFlag 強制的に値をnullに変換する
	 * @return 変換された値
	 */
	public static String changeString(Integer value, boolean nullFlag) {
		if (value == null || nullFlag) {
			return null;
		}
		return value.toString();
	}

	/**
	 * LongからStringへの変換を行う
	 *
	 * @param value
	 *            変換元値
	 * @param boolean nullFlag 強制的に値をnullに変換する
	 * @return 変換された値
	 */
	public static String changeString(Long value, boolean nullFlag) {
		if (value == null || nullFlag) {
			return null;
		}
		return value.toString();
	}

	/**
	 * BigDecimalからStringへの変換を行う
	 *
	 * @param value
	 *            変換元値
	 * @param boolean nullFlag 強制的に値をnullに変換する
	 * @return 変換された値
	 */
	public static String changeString(BigDecimal value, boolean nullFlag) {
		if (value == null || nullFlag) {
			return null;
		}
		return value.toString();
	}

	/**
	 * byte[] からStringへの変換を行う
	 *
	 * @param value
	 *            変換元値
	 * @param boolean nullFlag 強制的に値をnullに変換する
	 * @return 変換された値
	 */
	public static String changeString(byte[] value, boolean nullFlag) {
		return null;
	}

	/**
	 * DateからStringへの変換を行う
	 *
	 * @param value
	 *            変換元値
	 * @param boolean nullFlag 強制的に値をnullに変換する
	 * @return 変換された値
	 */
	public static String changeString(Date value, boolean nullFlag) {
		if (value == null || nullFlag) {
			return null;
		}
		return (new SimpleDateFormat("yyyy/MM/dd")).format(value);
	}

	/**
	 * TimeからStringへの変換を行う
	 *
	 * @param value
	 *            変換元値
	 * @param boolean nullFlag 強制的に値をnullに変換する
	 * @return 変換された値
	 */
	public static String changeString(java.sql.Time value, boolean nullFlag) {
		if (value == null || nullFlag) {
			return null;
		}
		return (new SimpleDateFormat("HH:mm:ss")).format(value);
	}

	/**
	 * java.sql.TimestampからStringへの変換を行う
	 *
	 * @param value
	 *            変換元値
	 * @param boolean nullFlag 強制的に値をnullに変換する
	 * @return 変換された値
	 */
	public static String changeString(java.sql.Timestamp value, boolean nullFlag) {
		if (value == null || nullFlag) {
			return null;
		}
		return (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS")).format(value);
	}

	/**
	 * BooleanからStringへの変換を行う
	 *
	 * @param value
	 *            変換元値
	 * @param boolean nullFlag 強制的に値をnullに変換する
	 * @return 変換された値
	 */
	public static String changeString(Boolean value, boolean nullFlag) {
		if (value == null || nullFlag) {
			return null;
		}
		return value.toString();
	}

	/**
	 * クエリーにフィルター（条件）を追加する<br>
	 * もし、GAEの制約でクエリーに追加できないフィルターはinMemoryFilterListへ追加する
	 *
	 * @param query
	 *            クエリー
	 * @param inMemoryFilterList
	 *            メモリー検索フィルターリスト
	 */
	protected void setFilter(Query query, List<SearcKeyValue> inMemoryFilterList) {
		String inequalityName = null;

		for (SearcKeyValue searcKeyValue : searchKeyValueList) {

			if (canUseFilterFieldSet.contains(searcKeyValue.keyName)) {

				Object value = changeGaeType(searcKeyValue.value, searcKeyValue.keyName);

				switch (searcKeyValue.searchMode) {
				case equal: // 等しい
					query.addFilter(searcKeyValue.keyName, FilterOperator.EQUAL, value);
					break;

				case like: // 前方一致（文字型のみ有効）
					if (inequalityName == null) {
						query.addFilter(searcKeyValue.keyName, FilterOperator.GREATER_THAN_OR_EQUAL, value.toString());
						query.addFilter(searcKeyValue.keyName, FilterOperator.LESS_THAN, value.toString() + "\ufffd");
						inequalityName = searcKeyValue.keyName;
					} else {
						inMemoryFilterList.add(searcKeyValue);
					}
					break;

				case bigEqual: // 大なりイコール（>=）
					if (inequalityName == null || inequalityName.equals(searcKeyValue.keyName)) {
						query.addFilter(searcKeyValue.keyName, FilterOperator.GREATER_THAN_OR_EQUAL, value);
						inequalityName = searcKeyValue.keyName;
					} else {
						inMemoryFilterList.add(searcKeyValue);
					}
					break;

				case big: // 大なり（>）
					if (inequalityName == null || inequalityName.equals(searcKeyValue.keyName)) {
						query.addFilter(searcKeyValue.keyName, FilterOperator.GREATER_THAN, value);
						inequalityName = searcKeyValue.keyName;
					} else {
						inMemoryFilterList.add(searcKeyValue);
					}
					break;

				case smallEqual: // 小なりイコール（<=）
					if (inequalityName == null || inequalityName.equals(searcKeyValue.keyName)) {
						query.addFilter(searcKeyValue.keyName, FilterOperator.LESS_THAN_OR_EQUAL, value);
						inequalityName = searcKeyValue.keyName;
					} else {
						inMemoryFilterList.add(searcKeyValue);
					}
					break;

				case small: // 小なり（<）
					if (inequalityName == null || (inequalityName.equals(searcKeyValue.keyName))) {
						query.addFilter(searcKeyValue.keyName, FilterOperator.LESS_THAN, value);
						inequalityName = searcKeyValue.keyName;
					} else {
						inMemoryFilterList.add(searcKeyValue);
					}
					break;

				// 検索用モード：不一致（<>）
				case notEqual:
					if (inequalityName == null || (inequalityName.equals(searcKeyValue.keyName))) {
						query.addFilter(searcKeyValue.keyName, FilterOperator.NOT_EQUAL, value);
						inequalityName = searcKeyValue.keyName;
					} else {
						inMemoryFilterList.add(searcKeyValue);
					}
					break;

				}
			} else {
				inMemoryFilterList.add(searcKeyValue);
			}
		}
	}

	/**
	 * 指定されたエンティテイがinMemoryFilterListの条件を満たしているときはtrueを返す
	 *
	 * @param entity
	 *            エンティテイ
	 * @param inMemoryFilterList
	 *            メモリー検索フィルターリスト
	 * @return
	 */
	protected boolean canSaveEntity(Entity entity, List<SearcKeyValue> inMemoryFilterList) {

		for (SearcKeyValue searcKeyValue : inMemoryFilterList) {

			Object entityVal = entity.getProperty(searcKeyValue.keyName);
			Object keyVal = searcKeyValue.value;
			int mode = searcKeyValue.searchMode;

			boolean bool = compObject(mode, entityVal, changeGaeType(keyVal, searcKeyValue.keyName));
			if (!bool) {
				return bool;
			}
		}

		return true;
	}

	/**
	 * ValueObjectに格納されている値をGAEで使用できる型のオブジェクトに変換する
	 *
	 * @param o
	 *            ValueObjectに格納されている値
	 * @param fieldName
	 *            フィールド名
	 * @return GAEで使用できる型のオブジェクト
	 */
	protected Object changeGaeType(Object o, String fieldName) {

		if (o == null) {
			return null;
		}

		if (o instanceof String) {
			return o;
		}

		if (o instanceof Integer) {
			return ((Integer) o).longValue();
		}

		if (o instanceof Long) {
			return o;
		}

		if (o instanceof BigDecimal) {
			return changeGaeSafeBigDecimal((BigDecimal) o, fieldName);
		}

		if (o instanceof java.sql.Timestamp) {
			return new Date(((java.sql.Timestamp) o).getTime());
		}

		if (o instanceof java.sql.Time) {
			return new Date(((java.sql.Time) o).getTime());
		}

		if (o instanceof java.sql.Date) {
			return new Date(((java.sql.Date) o).getTime());
		}

		if (o instanceof Date) {
			return o;
		}

		if (o instanceof Boolean) {
			return o;
		}

		return o.toString();
	}

	/**
	 * o1とo2を指定された比較モードで比較し、真偽を返す
	 *
	 * @param mode
	 *            比較モード
	 * @param o1
	 *            オブジェクト1
	 * @param o2
	 *            オブジェクト2
	 * @return 真偽
	 */
	protected boolean compObject(int mode, Object o1, Object o2) {

		switch (mode) {
		case equal: // 等しい
			return comp(o1, o2) == 0;

		case like: // 前方一致（文字型のみ有効）
			return likeCompObject(o1, o2);

		case bigEqual: // 大なりイコール（>=）
			return comp(o1, o2) >= 0;

		case big: // 大なり（>）
			return comp(o1, o2) > 0;

		case smallEqual: // 小なりイコール（<=）
			return comp(o1, o2) <= 0;

		case small: // 小なり（<）
			return comp(o1, o2) < 0;

			// 検索用モード：不一致（<>）
		case notEqual:
			return comp(o1, o2) != 0;

		}

		return false;
	}

	/**
	 * o1とo2が前方一致であうか真偽を返す
	 *
	 * @param o1
	 * @param o2
	 * @return 真偽
	 */
	public boolean likeCompObject(Object o1, Object o2) {
		if (o1 == null && o2 == null) {
			return true;
		}

		if (o1 == null && o2 != null) {
			return false;
		}

		if (o1 != null && o2 == null) {
			return false;
		}

		String str1;
		String str2;

		if (o1 instanceof Text) {
			str1 = ((Text) o1).getValue();
		} else {
			str1 = o1.toString();
		}

		if (o2 instanceof Text) {
			str2 = ((Text) o2).getValue();
		} else {
			str2 = o2.toString();
		}

		if (str1.length() < str2.length()) {
			return false;
		}

		if (str2.length() == 0) {
			return true;
		}

		return str2.equals(str1.substring(0, str2.length()));
	}

	/**
	 * o1とo2を比較し大・小・等を返す
	 *
	 * @param o1
	 *            オブジェクト1
	 * @param o2
	 *            オブジェクト2
	 * @return -1:o1<o2、0:o1==o2、1=o1>o2
	 */
	public static int comp(Object o1, Object o2) {
		if (o1 == null && o2 == null) {
			return 0;
		}
		if (o1 == null && o2 != null) {
			return -1;
		}
		if (o1 != null && o2 == null) {
			return 1;
		}

		if (o1 instanceof Text) {
			o1 = ((Text) o1).getValue();
		}

		if (o2 instanceof Text) {
			o2 = ((Text) o2).getValue();
		}

		if (o1 instanceof String && o2 instanceof String) {
			return ((String) o1).compareTo((String) o2);
		}

		if (o1 instanceof Integer && o2 instanceof Integer) {
			return ((Integer) o1).compareTo((Integer) o2);
		}

		if (o1 instanceof Long && o2 instanceof Long) {
			return ((Long) o1).compareTo((Long) o2);
		}

		if (o1 instanceof Long && o2 instanceof Integer) {
			return ((Long) o1).compareTo(((Integer) o2).longValue());
		}

		if (o1 instanceof Date && o2 instanceof Date) {
			return ((Date) o1).compareTo((Date) o2);
		}

		if (o1 instanceof Boolean && o2 instanceof Boolean) {
			return ((Boolean) o1).compareTo((Boolean) o2);
		}

		return (o1.toString()).compareTo(o2.toString());

	}

	/**
	 * ソートパラメータをクリアする
	 */
	public void clearSortParameter() {
		sortKeyList.clear();
	}

	/**
	 * ソート昇順パラメータを追加する
	 *
	 * @param fieldNmae
	 *            フィールド名
	 */
	public void addSortAscParameter(String fieldNmae) {
		if (!canUseSortFieldSet.contains(fieldNmae)) {
			throw new RuntimeException("ソート不可能なフィールドが指定されました" + fieldNmae);
		}
		sortKeyList.add(new SortKey(fieldNmae, SORT_MODE_ASC));
	}

	/**
	 * ソート降順パラメータを追加する
	 *
	 * @param fieldNmae
	 *            フィールド名
	 */
	public void addSortDescParameter(String fieldNmae) {
		if (!canUseSortFieldSet.contains(fieldNmae)) {
			throw new RuntimeException("ソート不可能なフィールドが指定されました" + fieldNmae);
		}
		sortKeyList.add(new SortKey(fieldNmae, SORT_MODE_DESC));
	}

	/**
	 * エンティティのソートを行う
	 *
	 * @param entitys
	 *            エンティティ配列
	 */
	protected Entity[] sortEntity(List<Entity> list) {

		if (sortKeyList.size() > 0) {
			return sortEntity(list, sortKeyList);
		} else {
			return sortEntity(list, getPrimarySortKeyList());
		}
	}

	/**
	 * エンティティのソートを行う
	 *
	 * @param entitys
	 *            エンティティ配列
	 * @param sortKeyListParam
	 *            ソートキーが格納されているリスト
	 */
	protected Entity[] sortEntity(List<Entity> list, final List<SortKey> sortKeyListParam) {

		Entity[] entitys = (Entity[]) list.toArray(new Entity[0]);

		Arrays.sort(entitys, new Comparator<Entity>() {

			@Override
			public int compare(Entity o1, Entity o2) {
				for (SortKey sortKey : sortKeyListParam) {
					Object k1 = o1.getProperty(sortKey.keyName);
					Object k2 = o2.getProperty(sortKey.keyName);

					int c = GaeDaoBase.comp(k1, k2);
					if (sortKey.sortMode == SORT_MODE_DESC) {
						c = -c;
					}
					if (c != 0) {
						return c;
					}
				}
				return 0;
			}
		});

		return entitys;
	}

	/**
	 * GAEが管理する自動採番の値を取得する
	 *
	 * @param ds
	 *            データストアサービス
	 * @return 自動採番の値
	 */
	protected long getAutoId(DatastoreService ds) {
		KeyRange keyRange = ds.allocateIds(getKindName(), 1);

		Key key = keyRange.getStart();

		return key.getId();
	}

	/**
	 * シーケンスを取得する<br>
	 *
	 * @param fieldName
	 *            フィールド名
	 *
	 * @return シーケンス
	 * @throws SQLException
	 */
	public long getSequence(final String fieldName) throws SQLException {

		final DatastoreService ds = DatastoreServiceFactory.getDatastoreService();

		ExecDatastore execObject = new ExecDatastore() {

			@Override
			public Object exec(Transaction tran) {

				// 主キー作成
				Key key = KeyFactory.createKey("_iop_sequence", getKindName() + "#" + fieldName);

				// シーケンス読込
				long sequence = 0;
				Entity entity;
				try {
					entity = ds.get(tran, key);
					sequence = (Long) entity.getProperty("sequence");
				} catch (EntityNotFoundException e) {
					entity = new Entity(key);
					sequence = 0L;
				}

				sequence++;
				entity.setProperty("sequence", sequence);
				ds.put(tran, entity);

				return sequence;
			}
		};

		for (int i = 0; i < 10; i++) {
			try {
				return (Long) exec(ds, execObject, null, true);
			} catch (ConcurrentModificationException e) { // 楽観的排他制御エラー
				try {
					Thread.sleep((int) (400 * Math.random()) + 100);
				} catch (InterruptedException e2) {
					// 何もしない
				}
			}
		}

		throw new SQLException("シーケンスの取得に失敗しました");
	}

	/**
	 * カインド名取得
	 *
	 * @return カインド名
	 */
	public abstract String getKindName();

	/**
	 * 主キーを作成する
	 *
	 * @param ds
	 *            データストアサービス
	 * @param keyRec
	 *            キー値が格納されているレコード
	 * @return 主キー
	 */
	public abstract Key createKey(DatastoreService ds, Object keyRec);

	/**
	 * 主キーのソートキーリストを取得する
	 *
	 * @return 主キーのソートキーリスト
	 */
	public abstract List<SortKey> getPrimarySortKeyList();

	/**
	 * 自動採番の設定
	 *
	 * @param ds
	 *            データストアサービス
	 * @param rec
	 *            レコード
	 * @throws SQLException
	 */
	public abstract void setAutoNumberField(DatastoreService ds, Object rec) throws SQLException;

	/**
	 * 指定されたレコードの値をエンティテイに格納する
	 *
	 * @param entity
	 *            エンティテイ
	 * @param record
	 *            レコード
	 */
	public abstract void setEntityAll(Entity entity, Object record);

	/**
	 * 指定されたエンティテイよりレコードを作製する
	 *
	 * @param entity
	 *            エンティテイ
	 * @return レコード
	 */
	public abstract Object createRecord(Entity entity);

	/**
	 * エンティテイより主キーのみのレコードを作製する
	 *
	 * @param entity
	 *            エンティテイ
	 * @return 主キーのみ格納されたレコード
	 */
	public abstract Object createPrimaryKeyRecord(Entity entity);

	/**
	 * 検索用データクラス
	 */
	class SearcKeyValue {
		public String keyName;
		public int searchMode;
		public Object value;

		/**
		 * コンストラクタ
		 *
		 * @param keyName
		 * @param searchMode
		 * @param value
		 */
		public SearcKeyValue(String keyName, int searchMode, Object value) {
			this.keyName = keyName;
			this.searchMode = searchMode;
			this.value = value;
		}
	}

	/**
	 * ソートキークラス
	 */
	class SortKey {
		public String keyName;
		public int sortMode;

		/**
		 * コンストラクタ
		 *
		 * @param keyName
		 * @param searchMode
		 * @param value
		 */
		public SortKey(String keyName, int sortMode) {
			this.keyName = keyName;
			this.sortMode = sortMode;
		}
	}

	/**
	 * フィールド長情報クラス
	 */
	public static class FieldLengthInfo {
		public int length;
		public int subLength;

		public FieldLengthInfo(int length, int subLength) {
			this.length = length;
			this.subLength = subLength;
		}
	}

	/**
	 * データストア実行インターフェース
	 */
	public static interface ExecDatastore {
		public Object exec(Transaction tran) throws SQLException;
	}

}
#!

#% === GAE・DAO Search Action =====================================================================
#! daoSearchAction package
package #package#;

#! daoSearchAction main
/**
 * レコード毎の処理インターフェース
 *
 * @author
 *
 */
public interface RecordAction {

	/**
	 * 初期化
	 *
	 * @throws Exception
	 */
	public void initAction() throws Exception;

	/**
	 * レコード毎に呼び出される処理
	 *
	 * @param record
	 *            処理を行うレコード
	 */
	public void action(Object record) throws Exception;

	/**
	 * 終了
	 *
	 * @throws Exception
	 */
	public void closeAction() throws Exception;
}
#!

#% === GAE・DAO Search Action =====================================================================
#! recordSearch package
package #package#;

#! recordSearch main
/**
 * レコード検索インターフェース
 *
 * @author
 *
 */
public interface RecordSearch {

	/**
	 * 以下の順番で処理を行う<br>
	 * 1.レコード毎の処理インターフェースオブジェクト#initActionを呼び出す<br>
	 * 2.レコードを検索し検索されたレコード毎にレコード毎の処理インターフェースオブジェクト#actionを呼び出す<br>
	 * 3.処理が終了したらレコード毎の処理インターフェースオブジェクト#closeActionを呼び出す
	 *
	 * @param searchAction
	 *            レコード毎の処理インターフェースオブジェクト
	 * @throws Exception
	 */
	public void search(RecordAction searchAction) throws Exception;

}
#!
