    001/*
    002 * Copyright (c) 2009 The openGion Project.
    003 *
    004 * Licensed under the Apache License, Version 2.0 (the "License");
    005 * you may not use this file except in compliance with the License.
    006 * You may obtain a copy of the License at
    007 *
    008 *     http://www.apache.org/licenses/LICENSE-2.0
    009 *
    010 * Unless required by applicable law or agreed to in writing, software
    011 * distributed under the License is distributed on an "AS IS" BASIS,
    012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
    013 * either express or implied. See the License for the specific language
    014 * governing permissions and limitations under the License.
    015 */
    016package org.opengion.fukurou.db;
    017
    018import java.util.List;
    019import java.util.ArrayList;
    020import java.util.Locale ;
    021import java.util.Arrays ;
    022import java.util.Set ;
    023import java.util.HashSet ;
    024import java.util.LinkedHashSet ;
    025import java.util.StringJoiner ;
    026
    027import org.opengion.fukurou.util.StringUtil;
    028import org.opengion.fukurou.system.OgBuilder ;
    029import org.opengion.fukurou.system.OgRuntimeException ;
    030import static org.opengion.fukurou.system.HybsConst.CR;
    031import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;
    032
    033/**
    034 * QueryMaker は、カラム名などから、SELECT,INSERT,UPDATE,DALETE 文字列を作成するクラスです。
    035 *
    036 * 基本的には、カラム名と、それに対応する値のセットで、QUERY文を作成します。
    037 * 値には、[カラム名] が使用でき、出力される値として、? が使われます。
    038 * これは、PreparedStatement に対する引数で、処理を行うためです。
    039 * この[カラム名]のカラム名は、検索された側のカラム名で、INSERT/UPDATE/DELETE等が実行される
    040 * ﾃﾞｰﾀﾍﾞｰｽ（ﾃｰﾌﾞﾙ)のカラム名ではありません。(偶然、一致しているかどうかは別として)
    041 *
    042 * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    043 *
    044 * @version  6.8.6.0 (2018/01/19)
    045 * @author       Kazuhiko Hasegawa
    046 * @since    JDK6.0,
    047 */
    048public class QueryMaker {
    049        private static final String QUERY_TYPE = "SELECT,INSERT,UPDATE,DELETE,MERGE" ;
    050
    051        private final List<String> whrList = new ArrayList<>() ;        // where条件に含まれる [カラム名] のリスト(パラメータ一覧)
    052
    053        private String queryType ;              // QUERYタイプ(SELECT,INSERT,UPDATE,DELETE,MERGE) を指定します。
    054        private String table ;
    055        private String names ;
    056        private String omitNames ;
    057        private String where ;
    058        private String whrNames ;
    059        private String orderBy ;
    060        private String cnstKeys ;
    061        private String cnstVals ;
    062
    063        private int             clmLen;                 // names カラムの "?" に置き換えられる個数
    064        private boolean isSetup ;               // セットアップ済みを管理しておきます。
    065        private String[] nameAry;
    066
    067        /**
    068         * デフォルトコンストラクター
    069         *
    070         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    071         */
    072        public QueryMaker() { super(); }                // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
    073
    074        /**
    075         * 処理の前に、入力データの整合性チェックや、初期設定を行います。
    076         *
    077         * あまり、何度も実行したくないので、フラグ管理しておきます。
    078         *
    079         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    080         * @og.rev 6.9.0.2 (2018/02/13) omitNamesの対応
    081         * @og.rev 6.9.8.0 (2018/05/28) setup() は、内部処理からのみ呼ばれるので、private化します。
    082         */
    083//      public void setup() {
    084        private void setup() {
    085                if( isSetup ) { return; }               // セットアップ済み
    086
    087                if( StringUtil.isNull( table ) ) {
    088                        final String errMsg = "指定の table に、null、ゼロ文字列は指定できません。"
    089                                                        + " table=" + table ;
    090                        throw new OgRuntimeException( errMsg );
    091                }
    092
    093                if( StringUtil.isNull( names ) ) {
    094                        final String errMsg = "指定の names に、null、ゼロ文字列は指定できません。"
    095                                                        + " names=" + names ;
    096                        throw new OgRuntimeException( errMsg );
    097                }
    098
    099                // 6.9.0.2 (2018/02/13) omitNamesの対応
    100                final String[]    nmAry  = StringUtil.csv2Array( names );
    101                final Set<String> nmSet  = new LinkedHashSet<>( Arrays.asList( nmAry ) );               // names の順番は、キープします。
    102                final String[]    omtAry = StringUtil.csv2Array( omitNames );
    103                final Set<String> omtSet = new HashSet<>( Arrays.asList( omtAry ) );                    // 除外する順番は、問いません。
    104                nmSet.removeAll( omtSet );
    105
    106                // 初期設定
    107                clmLen  = nmSet.size();
    108                nameAry = nmSet.toArray( new String[clmLen] );
    109
    110//              // 初期設定
    111//              nameAry = StringUtil.csv2Array( names );
    112//              clmLen = nameAry.length;
    113
    114                // [カラム名] List は、whereNames + where の順番です。(whrListの登録順を守る必要がある)
    115                // where条件も、この順番に連結しなければなりません。
    116                where = StringUtil.join( " AND " , whrNames , formatSplit( where ) );   // formatSplit で、whrListの登録を行っている。
    117
    118                isSetup = true;
    119        }
    120
    121        /**
    122         * データを検索する場合に使用するSQL文を作成します。
    123         *
    124         * SELECT names FROM table WHERE where ORDER BY orderBy ;
    125         *
    126         * cnstKeys,cnstVals は、使いません。
    127         * where,orderBy は、それぞれ、値が存在しない場合は、設定されません。
    128         *
    129         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    130         * @og.rev 6.9.0.2 (2018/02/13) omitNamesの対応
    131         *
    132         * @return  検索SQL
    133         * @og.rtnNotNull
    134         */
    135        public String getSelectSQL() {
    136                if( !"SELECT".equals( queryType ) ) {
    137                        final String errMsg = "指定のQUERYタイプと異なるSQL文を要求しています。" + CR
    138                                                        + " 要求SQL=SELECT  queryType=" + queryType ;
    139                        throw new OgRuntimeException( errMsg );
    140                }
    141
    142                setup();
    143
    144                return new OgBuilder()
    145//                      .append(   "SELECT "    , names )
    146                        .append(   "SELECT "            )
    147                        .join(     ","          , nameAry )             // 6.9.0.2 (2018/02/13) names ではなく、omitNames後のカラム配列を使用します。
    148                        .append(   " FROM "     , table )
    149                        .appendNN( " WHERE "    , where )               // nullなら、追加しない。where + whereNames
    150                        .appendNN( " ORDER BY " , orderBy )             // nullなら、追加しない。
    151                        .toString();
    152        }
    153
    154        /**
    155         * データを追加する場合に使用するSQL文を作成します。
    156         *
    157         * INSERT INTO table ( names,cnstKeys ) VALUES ( values,cnstVals ) ;
    158         *
    159         * cnstKeys,cnstVals は、INSERTカラムとして使います。
    160         * where,orderBy は、使いません。
    161         *
    162         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    163         * @og.rev 6.9.0.2 (2018/02/13) omitNamesの対応
    164         *
    165         * @return  追加SQL
    166         * @og.rtnNotNull
    167         */
    168        public String getInsertSQL() {
    169                if( !"INSERT".equals( queryType ) && !"MERGE".equals( queryType ) ) {
    170                        final String errMsg = "指定のQUERYタイプと異なるSQL文を要求しています。" + CR
    171                                                        + " 要求SQL=INSERT  queryType=" + queryType ;
    172                        throw new OgRuntimeException( errMsg );
    173                }
    174
    175                setup();
    176
    177                return new OgBuilder()
    178                        .append( "INSERT INTO " ).append( table )
    179//                      .append( " ( " ).append( names )
    180                        .append( " ( " )
    181                        .join(     "," , nameAry  )                     // 6.9.0.2 (2018/02/13) names ではなく、omitNames後のカラム配列を使用します。
    182                        .appendNN( "," , cnstKeys )
    183                        .append( " ) VALUES ( " )
    184                        .appendRoop( 0,clmLen,",",i -> "?" )
    185                        .appendNN( "," , cnstVals )
    186                        .append( " )" )
    187                        .toString();
    188        }
    189
    190        /**
    191         * データを更新する場合に使用するSQL文を作成します。
    192         *
    193         * UPDATE table SET names[i]=values[i], ･･･cnstKeys[i]=cnstVals[i], ･･･ WHERE where;
    194         *
    195         * cnstKeys,cnstVals は、UPDATEカラムとして使います。
    196         * orderBy は、使いません。
    197         *
    198         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    199         * @og.rev 6.9.9.1 (2018/08/27) cnstKeys,cnstValsは、個数違いの場合のみ、エラーです。
    200         *
    201         * @return  更新SQL
    202         * @og.rtnNotNull
    203         */
    204        public String getUpdateSQL() {
    205                if( !"UPDATE".equals( queryType ) && !"MERGE".equals( queryType ) ) {
    206                        final String errMsg = "指定のQUERYタイプと異なるSQL文を要求しています。" + CR
    207                                                        + " 要求SQL=UPDATE  queryType=" + queryType ;
    208                        throw new OgRuntimeException( errMsg );
    209                }
    210
    211                setup();
    212
    213                final String[] cnKey = StringUtil.csv2Array( cnstKeys );        // @og.rtnNotNull
    214                final String[] cnVal = StringUtil.csv2Array( cnstVals );        // @og.rtnNotNull
    215
    216                // 整合性チェック
    217                // 6.9.8.0 (2018/05/28) FindBugs:null でないことがわかっている値の冗長な null チェック
    218//              if( cnKey != null && cnVal == null ||
    219//                      cnKey == null && cnVal != null ||
    220//                      cnKey != null && cnVal != null && cnKey.length != cnVal.length ) {
    221                // 6.9.9.1 (2018/08/27) cnstKeys,cnstValsは、個数違いの場合のみ、エラーです。
    222//              if( cnKey.length == 0 || cnVal.length == 0 || cnKey.length != cnVal.length ) {
    223//                      final String errMsg = "指定の keys,vals には、null、ゼロ件配列、または、個数違いの配列は指定できません。"
    224                if( cnKey.length != cnVal.length ) {
    225                        final String errMsg = "指定の keys,vals の個数が違ます。"
    226                                                        + " keys=" + cnstKeys
    227                                                        + " vals=" + cnstVals ;
    228                        throw new OgRuntimeException( errMsg );
    229                }
    230
    231                // 6.9.8.0 (2018/05/28) FindBugs:コンストラクタで初期化されていないフィールドを null チェックなしで null 値を利用している
    232                // queryType と、nameAry は、setup() メソッドで設定されるため、FindBugs の指摘は、対応済みとなります。
    233                // とりあえず、条件判定を入れておいて、FindBugs の警告が出ないようにしておきます。
    234                if( nameAry == null ) {
    235                        // nameAry は、setup() メソッドで設定されるため、このエラーは出ません。
    236                        final String errMsg = "何らかの不測の事態が発生しました。本来、このエラーは出ません。";
    237                        throw new OgRuntimeException( errMsg );
    238                }
    239
    240                return new OgBuilder()
    241                        .append( "UPDATE " ).append( table )
    242                        .append( " SET " )
    243                        .appendRoop( 0,clmLen      ,",",i -> nameAry[i] + "=?" )
    244                        .appendRoop( 0,cnVal.length,",",i -> cnKey[i]   + "="  + cnVal[i] )
    245                        .appendNN( " WHERE " , where )          // nullなら、追加しない。where + whereNames
    246                        .toString();
    247        }
    248
    249        /**
    250         * データを削除する場合に使用するSQL文を作成します。
    251         *
    252         * DELETE FROM table WHERE where;
    253         *
    254         * cnstKeys,cnstVal,orderBys は、使いません。
    255         * where は、値が存在しない場合は、設定されません。
    256         * orderBy は、使いません。
    257         *
    258         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    259         *
    260         * @return  削除SQL
    261         * @og.rtnNotNull
    262         */
    263        public String getDeleteSQL() {
    264                if( !"DELETE".equals( queryType ) ) {
    265                        final String errMsg = "指定のQUERYタイプと異なるSQL文を要求しています。" + CR
    266                                                        + " 要求SQL=DELETE  queryType=" + queryType ;
    267                        throw new OgRuntimeException( errMsg );
    268                }
    269
    270                setup();
    271
    272                return new OgBuilder()
    273                        .append(   "DELETE FROM " ).append( table )
    274                        .appendNN( " WHERE " , where )          // nullなら、追加しない。where + whereNames
    275                        .toString();
    276        }
    277
    278        /**
    279         * [カラム名]を含む文字列を分解し、Map に登録します。
    280         *
    281         * これは、[カラム名]を含む文字列を分解し、カラム名 を取り出し、whrList に
    282         * 追加していきます。
    283         * 戻り値は、[XXXX] を、? に置換済みの文字列になります。
    284         *
    285         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    286         *
    287         * @param       fmt     [カラム名]を含む文字列
    288         * @return  PreparedStatementに対応した変換後の文字列
    289         */
    290        private String formatSplit( final String fmt ) {
    291                if( StringUtil.isNull( fmt ) ) { return fmt; }          // null,ゼロ文字列チェック
    292
    293                final StringBuilder rtnStr = new StringBuilder( BUFFER_MIDDLE );
    294
    295                int start = 0;
    296                int index = fmt.indexOf( '[' );
    297                while( index >= 0 ) {
    298                        final int end = fmt.indexOf( ']',index );
    299                        if( end < 0 ) {
    300                                final String errMsg = "[ と ] との対応関係がずれています。"
    301                                                                + "format=[" + fmt + "] : index=" + index ;
    302                                throw new OgRuntimeException( errMsg );
    303                        }
    304
    305                        // [ より前方の文字列は、rtnStr へ追加する。
    306                        if( index > 0 ) { rtnStr.append( fmt.substring( start,index ) ); }
    307        //              index == 0 は、][ と連続しているケース
    308
    309                        // [XXXX] の XXXX部分と、位置(?の位置になる)を、Listに登録
    310                        whrList.add( fmt.substring( index+1,end ) );
    311
    312                        rtnStr.append( '?' );           // [XXXX] を、? に置換する。
    313
    314                        start = end+1 ;
    315                        index = fmt.indexOf( '[',start );
    316                }
    317                // ] の後方部分は、rtnStr へ追加する。
    318                rtnStr.append( fmt.substring( start ) );                // '[' が見つからなかった場合は、この処理で、すべての fmt データが、append される。
    319
    320                return rtnStr.toString();
    321        }
    322
    323        /**
    324         * QUERYタイプ(SELECT,INSERT,UPDATE,DELETE,MERGE) を指定します。
    325         *
    326         * 引数が nullか、ゼロ文字列の場合は、登録しません。
    327         *
    328         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    329         *
    330         * @param       queryType       QUERYタイプ
    331         */
    332        public void setQueryType( final String queryType ) {
    333                if( !StringUtil.isNull( queryType ) ) {
    334                        if( QUERY_TYPE.contains( queryType ) ) {
    335                                this.queryType = queryType;
    336                        }
    337                        else {
    338                                final String errMsg = "queryType は、" + QUERY_TYPE + " から、指定してください。";
    339                                throw new OgRuntimeException( errMsg );
    340                        }
    341                }
    342        }
    343
    344        /**
    345         * テーブル名をセットします。
    346         *
    347         * 引数が nullか、ゼロ文字列の場合は、登録しません。
    348         *
    349         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    350         *
    351         * @param       table   テーブル名
    352         */
    353        public void setTable( final String table ) {
    354                if( !StringUtil.isNull( table ) ) {
    355                        this.table = table;
    356                }
    357        }
    358
    359        /**
    360         * テーブル名を取得します。
    361         *
    362         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    363         *
    364         * @return      テーブル名
    365         */
    366        public String getTable() {
    367                return table;
    368        }
    369
    370        /**
    371         * カラム名をセットします。
    372         *
    373         * カラム名は、登録時に、大文字に変換しておきます。
    374         * カラム名は、CSV形式でもかまいません。
    375         * 引数が nullか、ゼロ文字列の場合は、登録しません。
    376         *
    377         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    378         *
    379         * @param   names  キー（大文字のみ。内部で変換しておきます。）
    380         */
    381        public void setNames( final String names ) {
    382                if( !StringUtil.isNull( names ) ) {
    383                        this.names = names.toUpperCase(Locale.JAPAN);
    384                }
    385        }
    386
    387        /**
    388         * カラム名を取得します。
    389         *
    390         * 登録時に、すでに、大文字に変換していますので、
    391         * ここで取得するカラム名も、大文字に変換されています。
    392         *
    393         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    394         *
    395         * @return      カラム名(大文字に変換済み)
    396         */
    397        public String getNames() {
    398                return names;
    399        }
    400
    401        /**
    402         * 除外するカラム名をセットします。
    403         *
    404         * カラム名は、登録時に、大文字に変換しておきます。
    405         * カラム名は、CSV形式でもかまいません。
    406         * 引数が nullか、ゼロ文字列の場合は、登録しません。
    407         *
    408         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    409         *
    410         * @param   omitNames  キー（大文字のみ。内部で変換しておきます。）
    411         */
    412        public void setOmitNames( final String omitNames ) {
    413                if( !StringUtil.isNull( omitNames ) ) {
    414                        this.omitNames = omitNames.toUpperCase(Locale.JAPAN);
    415                }
    416        }
    417
    418        /**
    419         * WHERE条件をセットします。
    420         *
    421         * whereNames属性と同時に使用する場合は、"AND" で、処理します。
    422         * 引数が nullか、ゼロ文字列の場合は、登録しません。
    423         *
    424         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    425         *
    426         * @param   where  WHERE条件
    427         */
    428        public void setWhere( final String where ) {
    429                if( !StringUtil.isNull( where ) ) {
    430                        this.where = where;
    431                }
    432        }
    433
    434        /**
    435         * WHERE条件となるカラム名をCSV形式でセットします。
    436         *
    437         * カラム名配列より、WHERE条件を、KEY=[KEY] 文字列で作成します。
    438         * where属性と同時に使用する場合は、"AND" で、処理します。
    439         * 引数が nullか、ゼロ件配列の場合は、登録しません。
    440         *
    441         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    442         *
    443         * @param       whNames WHERE句作成のためのカラム名
    444         */
    445        public void setWhereNames( final String whNames ) {
    446                if( !StringUtil.isNull( whNames ) ) {
    447                        final String[] whAry = StringUtil.csv2Array( whNames );
    448
    449                        final StringJoiner sj = new StringJoiner( " AND " );            // 区切り文字
    450                        for( final String whName : whAry ) {
    451                                whrList.add( whName );
    452                                sj.add( whName + "=?" );
    453                        }
    454                        whrNames = sj.toString();
    455                }
    456        }
    457
    458        /**
    459         * orderBy条件をセットします。
    460         *
    461         * 引数が nullか、ゼロ文字列の場合は、登録しません。
    462         *
    463         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    464         *
    465         * @param   orderBy  orderBy条件
    466         */
    467        public void setOrderBy( final String orderBy ) {
    468                if( !StringUtil.isNull( orderBy ) ) {
    469                        this.orderBy = orderBy;
    470                }
    471        }
    472
    473        /**
    474         * 固定値のカラム名をセットします。
    475         *
    476         * nullでなく、ゼロ文字列でない場合のみセットします。
    477         * カラム名は、CSV形式でもかまいません。
    478         * 引数が nullか、ゼロ文字列の場合は、登録しません。
    479         *
    480         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    481         *
    482         * @param   keys  固定値のカラム名
    483         */
    484        public void setConstKeys( final String keys ) {
    485                if( !StringUtil.isNull( keys ) ) {
    486                        this.cnstKeys = keys;
    487                }
    488        }
    489
    490        /**
    491         * 固定値のカラム名に対応した、固定値文字列をセットします。
    492         *
    493         * nullでなく、ゼロ文字列でない場合のみセットします。
    494         * 固定値は、CSV形式でもかまいません。
    495         * 引数が nullか、ゼロ文字列の場合は、登録しません。
    496         *
    497         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    498         *
    499         * @param   vals  固定値
    500         */
    501        public void setConstVals( final String vals ) {
    502                if( !StringUtil.isNull( vals ) ) {
    503                        this.cnstVals = vals;
    504                }
    505        }
    506
    507        /**
    508         * PreparedStatement で、パラメータとなるカラム名の配列を返します。
    509         *
    510         * これは、QUERYの変数部分 "[カラム名]" を、"?" に置き換えており、
    511         * この、カラム名の現れた順番に、配列として返します。
    512         * ﾃﾞｰﾀﾍﾞｰｽ処理では、パラメータを設定する場合に、このカラム名を取得し、
    513         * オリジナル（SELECT）のカラム番号から、その値を取得しなければなりません。
    514         *
    515         * カラム名配列は、QUERYタイプ(queryType)に応じて作成されます。
    516         * SELECT : パラメータ は使わないので、長さゼロの配列
    517         * INSERT : where条件は使わず、names部分のみなので、0 ～ clmLen までの配列
    518         * UPDATE : names も、where条件も使うため、すべての配列
    519         * DELETE : names条件は使わず、where部分のみなので、clmLen ～ clmLen＋whrLen までの配列(clmLen以降の配列)
    520         *
    521         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
    522         * @og.rev 6.9.8.0 (2018/05/28) セットアップチェックが漏れていた。
    523         *
    524         * @param       useInsert       queryType="MERGE" の場合に、false:UPDATE , true:INSERT のパラメータのカラム名配列を返します。
    525         * @return      パラメータとなるカラム名の配列
    526         * @og.rtnNotNull
    527         */
    528        public String[] getParamNames( final boolean useInsert ) {
    529                // 6.9.8.0 (2018/05/28) FindBugs:コンストラクタで初期化されていないフィールドを null チェックなしで null 値を利用している
    530                // queryType と、nameAry は、setup() メソッドで設定されるため、FindBugs の指摘は、対応済みとなります。
    531                // とりあえず、条件判定を入れておいて、FindBugs の警告が出ないようにしておきます。
    532
    533                // 6.9.8.0 (2018/05/28) セットアップチェックが漏れていた。
    534                if( !isSetup || StringUtil.isNull( queryType ) || nameAry == null ) {
    535                        final String errMsg = "getParamNames(boolean) は、SQL文を取得してから、行ってください。";
    536                        throw new OgRuntimeException( errMsg );
    537                }
    538
    539                final String[] whrAry = whrList.toArray( new String[whrList.size()] );
    540                final String[] allAry = Arrays.copyOf( nameAry , nameAry.length + whrList.size() );
    541                System.arraycopy( whrAry , 0 , allAry , nameAry.length , whrAry.length );               // allAry = nameAry + whrAry の作成
    542
    543                String[] rtnClms = null;
    544                switch( queryType ) {
    545                        case "SELECT" : rtnClms = new String[0];        break;          // パラメータはない。
    546                        case "INSERT" : rtnClms = nameAry;                      break;          // names指定の分だけ、パラメータセット
    547                        case "UPDATE" : rtnClms = allAry;                       break;          // names+whereの分だけ、パラメータセット
    548                        case "DELETE" : rtnClms = whrAry;                       break;          // whereの分だけ、パラメータセット
    549                        case "MERGE"  : rtnClms = allAry;                       break;          // useInsert=false は、UPDATEと同じ
    550                        default : break;
    551                }
    552
    553                if( useInsert && "MERGE".equals( queryType ) ) {
    554                        rtnClms = nameAry;                                      // MERGEで、useInsert=true は、INSERTと同じ
    555                }
    556
    557                return rtnClms;
    558        }
    559}