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.sql.Connection;
019import java.sql.PreparedStatement;
020import java.sql.ParameterMetaData;
021import java.sql.SQLException;
022import java.util.Arrays;
023
024import org.opengion.fukurou.util.StringUtil;
025import org.opengion.fukurou.system.OgRuntimeException ;                         // 6.4.2.0 (2016/01/29)
026import org.opengion.fukurou.system.Closer;
027import org.opengion.fukurou.model.Formatter;
028import org.opengion.fukurou.model.ArrayDataModel;
029import org.opengion.fukurou.model.DataModel;                                            // 8.2.1.0 (2022/07/15)
030import static org.opengion.fukurou.system.HybsConst.CR;                         // 6.1.0.0 (2014/12/26) refactoring
031import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.1.0.0 (2014/12/26) refactoring
032
033/**
034 * DBTableModel インターフェースを継承した TableModel の実装クラスです。
035 * sql文を execute( query ) する事により、データベースを検索した結果を
036 * DBTableModel に割り当てます。
037 *
038 * メソッドを宣言しています
039 * DBTableModel インターフェースは、データベースの検索結果(Resultset)をラップする
040 * インターフェースとして使用して下さい。
041 *
042 * @og.rev 5.2.2.0 (2010/11/01) パッケージ移動(hayabusa.db ⇒ fukurou.db)
043 * @og.group DB/Shell制御
044 *
045 * @version     4.0
046 * @author      Kazuhiko Hasegawa
047 * @since       JDK5.0,
048 */
049// 8.5.5.1 (2024/02/29) spotbugs CT_CONSTRUCTOR_THROW(コンストラクタで、Excweptionを出さない) class を final にすれば、警告は消える。
050// public class DBSimpleTable {
051public final class DBSimpleTable {
052
053        private final String[]  names   ;                       // データ配列に対応するカラム配列(names)
054        private String[]        keys            ;                       // 登録に使用するカラムキー配列(keys)
055        private int[]           keysNo          ;                       // 登録に使用するカラムキー配列番号
056        private String          table           ;                       // 登録テーブル名
057        private String          where           ;                       // where 条件式[カラム名]を含む
058        private int[]           whereNo         ;                       // [カラム名]に対応するデータ配列番号
059        private String[]        constrain       ;                       // key に対応した制約条件
060
061        private String          connID          ;                       // 登録に使用するコネクションID
062        private boolean         useWhere        ;                       // where が設定されると true にセットされます。
063
064        private Connection      conn            ;
065        private PreparedStatement pstmt ;
066        private ParameterMetaData pMeta ;                       // 5.1.2.0 (2010/01/01) setObject に、Type を渡す。(PostgreSQL対応)
067        private String          query           ;                       // エラーメッセージ用の変数
068        private int                     execCnt         ;
069        private ApplicationInfo appInfo ;                       // 3.8.7.0 (2006/12/15)
070        private boolean useParamMetaData;                       // 5.1.2.0 (2010/01/01) setObject に、Type を渡す。(PostgreSQL対応)
071
072        /**
073         * データ配列のカラム名称配列を指定してオブジェクトを構築します。
074         *
075         * @param       nm      カラム名称配列
076         * @throws RuntimeException tbl が null の場合
077         */
078        public DBSimpleTable( final String[] nm ) {
079                if( nm == null ) {
080                        final String errMsg = "データ配列のカラム名称に null は設定できません。";
081                        throw new OgRuntimeException( errMsg );
082                }
083
084                names = new String[nm.length];
085                System.arraycopy( nm,0,names,0,names.length );
086        }
087
088        /**
089         * 登録に使用するカラムキー配列(keys)を登録します。
090         *
091         * 引数のkey配列が null の場合は、names と同じカラム名称配列(names)が使用されます。
092         * キー配列(keys)は、一度しか登録できません。また、addConstrain等のメソッド
093         * 呼び出しを先に実行すると、カラム名称配列(names)が設定されてしまう為、
094         * その後にこのメソッドを呼び出すとエラーが発生します。
095         *
096         * @param       key     登録カラム名称配列(可変長引数)
097         * @see         #addConstrain( String ,String )
098         * @throws RuntimeException すでに キー配列(keys)が登録済み/作成済みの場合
099         */
100        public void setKeys( final String... key ) {
101                if( keys != null ) {
102                        final String errMsg = "すでに キー配列(keys)が登録済みです。";
103                        throw new OgRuntimeException( errMsg );
104                }
105
106                if( key != null && key.length > 0 ) {           // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。
107                        final int size = key.length;
108                        keys = new String[size];
109                        System.arraycopy( key,0,keys,0,size );
110
111                        constrain = new String[size];
112                        Arrays.fill( constrain,"?" );
113
114                        keysNo = new int[size];
115                        for( int i=0; i<size; i++ ) {
116                                final int address = findAddress( names,keys[i] );
117                                if( address >= 0 ) {
118                                        keysNo[i] = address;
119                                }
120                                else {
121                                        final String errMsg = "指定の key は、カラム配列(names)に存在しません"
122                                                                        + " key[" + i + "]=" + key[i]
123                                                                        + " names=" + StringUtil.array2csv( names ) ;
124                                        throw new OgRuntimeException( errMsg );
125                                }
126                        }
127                }
128        }
129
130        /**
131         * カラム名称配列(names)と同じキー配列(keys)を作成します。
132         *
133         * これは、キー配列(keys) が作成されなかった場合の処理です。
134         * keys が null の場合のみ、処理を実行します。
135         *
136         * @see         #setKeys( String[] )
137         */
138        private void makeKeys() {
139                // キー配列(keys) が未設定の場合は、カラム名称配列(names)が設定されます。
140                if( keys == null ) {
141                        keys = names;
142                        final int size = keys.length;
143
144                        constrain = new String[size];
145                        Arrays.fill( constrain,"?" );
146
147                        keysNo = new int[size];
148                        for( int i=0; i<size; i++ ) {
149                                keysNo[i] = i;
150                        }
151                }
152        }
153
154        /**
155         * Insert/Update/Delete 時の登録するテーブル名。
156         *
157         * @param       tbl     テーブル名
158         * @throws RuntimeException tbl が null の場合
159         */
160        public void setTable( final String tbl ) {
161                if( tbl == null ) {
162                        final String errMsg = "table に null は設定できません。";         // 5.1.8.0 (2010/07/01) errMsg 修正
163                        throw new OgRuntimeException( errMsg );
164                }
165
166                table = tbl;
167        }
168
169        /**
170         * データベースの接続先IDを設定します。
171         *
172         * @param       conn    接続先ID
173         */
174        public void setConnectionID( final String conn ) {
175                connID = conn;
176        }
177
178        /**
179         * アクセスログ取得の為、ApplicationInfoオブジェクトを設定します。
180         *
181         * @og.rev 3.8.7.0 (2006/12/15) 新規追加
182         *
183         * @param   appInfo アプリ情報オブジェクト
184         */
185        public void setApplicationInfo( final ApplicationInfo appInfo ) {
186                this.appInfo = appInfo;
187        }
188
189        /**
190         * Insert/Update/Delete 時の PreparedStatement の引数(クエスチョンマーク)制約。
191         *
192         * 制約条件(val)は、そのまま引数に使用されます。通常、? で表される
193         * パラメータに、文字長を制限する場合、SUBSTRB( ?,1,100 ) という
194         * val 変数を与えます。
195         * また、キー一つに対して、値を複数登録したい場合にも、使用できます。
196         * 例えば、NVAL( ?,? ) のような場合、キー一つに値2つを割り当てます。
197         * 値配列の並び順は、キー配列(keys)に対する(?の個数)に対応します。
198         * 注意:カラム名称配列(names)ではありません。また、先にキー配列(keys)を登録
199         * しておかないと、キー配列登録時にエラーが発生します。
200         * 制約条件は、処理するQUERYに対して適用されますので、
201         * key または、val が null の場合は、RuntimeException を Throwします。
202         *
203         * @param       key     制約をかけるキー
204         * @param       val     制約条件式
205         * @see         #setKeys( String[] )
206         * @throws RuntimeException key または、val が null の場合
207         */
208        public void addConstrain( final String key,final String val ) {
209                if( key == null || val == null ) {
210                        final String errMsg = "key または、val に null は設定できません。"
211                                                        + " key=[" + key + "] , val=[" + val + "]" ;
212                        throw new OgRuntimeException( errMsg );
213                }
214
215                // キー配列(keys)が未設定(null)の場合は、カラム名称配列(names)を割り当てます。
216                if( keys == null ) { makeKeys(); }
217
218                // 制約条件のアドレスは、カラム名称配列(names)でなく、キー配列(keys)を使用します。
219                final int address = findAddress( keys,key );
220                if( address >= 0 ) {
221                        constrain[address] = val;
222                }
223                else {
224                        final String errMsg = "指定の key は、キー配列(keys)に存在しません"
225                                                        + " key=[" + key + "] , val=[" + val + "]"
226                                                        + " keys=" + StringUtil.array2csv( keys ) ;
227                        throw new OgRuntimeException( errMsg );
228                }
229        }
230
231        /**
232         * Update/Delete 時のキーとなるWHERE 条件のカラム名を設定します。
233         *
234         * 通常の WHERE 句の書き方と同じで、カラム配列(names)に対応する設定値(values)の値を
235         * 割り当てたい箇所に[カラム名] を記述します。文字列の場合、設定値をセットする
236         * ときに、シングルコーテーションを使用しますが、[カラム名]で指定する場合は、
237         * その前後に、(')シングルコーテーションは、不要です。
238         * WHERE条件は、登録に使用するキー配列(keys)に現れない条件で行を特定することがありますので
239         * カラム名称配列(names)を元にカラム名のアドレスを求めます。
240         * [カラム名]は、? に置き換えて、PreparedStatement として、実行される形式に変換されます。
241         * 例:FGJ='1' and CLM=[CLM] and SYSTEM_ID in ([SYSID],'**')
242         *
243         * @og.rev 4.3.4.0 (2008/12/01) キー配列(keys)が未設定(null)の場合は、カラム名称配列(names)を割り当てる
244         * @og.rev 5.0.2.0 (2009/11/01) バグ修正(keysはデータセットのキーなので、where句のカラムに含まれて入いるわけではない)
245         * @og.rev 6.4.1.2 (2016/01/22) PMD refactoring. where は、null をセットするのではなく、useWhere の設定で判定する。
246         * @og.rev 6.4.3.4 (2016/03/11) Formatterに新しいコンストラクターを追加する。
247         * @og.rev 6.9.5.0 (2018/04/23) カラム名が存在しない場合に、Exception を throw するかどうかを指定可能にする。
248         *
249         * @param  wh WHERE条件のカラム名
250         * @throws RuntimeException [カラム名]がカラム配列(names)に存在しない場合
251         */
252        public void setWhere( final String wh ) {
253
254                // 6.4.1.2 (2016/01/22) PMD refactoring.
255                if( wh == null || wh.isEmpty() ) {
256                        useWhere= false;
257                }
258                else {
259                        // キー配列(keys)が未設定(null)の場合は、カラム名称配列(names)を割り当てます。
260                        // 5.0.2.0 (2009/11/01)
261//                      final ArrayDataModel data = new ArrayDataModel( names );
262//                      final ArrayDataModel data = new ArrayDataModel( names,true );           // 6.9.5.0 (2018/04/23) カラム名が存在しない場合に、Exception を throw する
263                        final DataModel<String> data = new ArrayDataModel( names,true );        // 8.2.1.0 (2022/07/15)
264                        final Formatter format = new Formatter( data,wh );                                      // 6.4.3.4 (2016/03/11)
265                        where   = format.getQueryFormatString();
266                        whereNo = format.getClmNos();
267                        useWhere= true;
268                }
269        }
270
271        /**
272         * データをインサートする場合に使用するSQL文を作成します。
273         *
274         * @og.rev 6.2.3.0 (2015/05/01) CSV形式の作成を、String#join( CharSequence , CharSequence... )を使用。
275         *
276         * @return  インサートSQL
277         * @og.rtnNotNull
278         */
279        private String getInsertSQL() {
280                // キー配列(keys)が未設定(null)の場合は、カラム名称配列(names)を割り当てます。
281                if( keys == null ) { makeKeys(); }
282
283                // 6.2.3.0 (2015/05/01) CSV形式の作成を、String#join( CharSequence , CharSequence... )を使用。
284                final StringBuilder sql = new StringBuilder( BUFFER_MIDDLE )
285                        .append( "INSERT INTO " ).append( table )
286                        .append( " ( " )
287                        .append( String.join( "," , keys ) )                    // 6.2.3.0 (2015/05/01)
288                        .append( " ) VALUES ( " )
289                        .append( String.join( "," , constrain ) )               // 6.2.3.0 (2015/05/01)
290                        .append( " )" );
291
292                useWhere = false;
293
294                return sql.toString();
295        }
296
297        /**
298         * データをアップデートする場合に使用するSQL文を作成します。
299         *
300         * @og.rev 6.4.1.2 (2016/01/22) PMD refactoring. where は、null をセットするのではなく、useWhere の設定で判定する。
301         *
302         * @return  アップデートSQL
303         * @og.rtnNotNull
304         */
305        private String getUpdateSQL() {
306                // キー配列(keys)が未設定(null)の場合は、カラム名称配列(names)を割り当てます。
307                if( keys == null ) { makeKeys(); }
308
309                final StringBuilder sql = new StringBuilder( BUFFER_MIDDLE )
310                        .append( "UPDATE " ).append( table ).append( " SET " )
311                        .append( keys[0] ).append( " = " ).append( constrain[0] );
312
313                for( int i=1; i<keys.length; i++ ) {
314                        sql.append( " , " )
315                                .append( keys[i] ).append( " = " ).append( constrain[i] );
316                }
317
318                // 6.4.1.2 (2016/01/22) PMD refactoring.
319                if( useWhere ) {
320                        sql.append( " WHERE " ).append( where );
321                }
322
323                return sql.toString();
324        }
325
326        /**
327         * データをデリートする場合に使用するSQL文を作成します。
328         *
329         * @og.rev 5.0.2.0 (2009/11/01) バグ修正(削除時はkeysは必要ない)
330         * @og.rev 6.4.1.2 (2016/01/22) PMD refactoring. where は、null をセットするのではなく、useWhere の設定で判定する。
331         *
332         * @return  デリートSQL
333         * @og.rtnNotNull
334         */
335        private String getDeleteSQL() {
336                // キー配列(keys)が未設定(null)の場合は、カラム名称配列(names)を割り当てます。
337                // 5.0.2.0 (2009/11/01)
338                keys = new String[0];
339
340                final StringBuilder sql = new StringBuilder( BUFFER_MIDDLE )
341                        .append( "DELETE FROM " ).append( table );
342
343                // 6.4.1.2 (2016/01/22) PMD refactoring.
344                if( useWhere ) {
345                        sql.append( " WHERE " ).append( where );
346                }
347
348                return sql.toString();
349        }
350
351        /**
352         * Insert 処理の開始を宣言します。
353         * 内部的に、コネクションを接続して、PreparedStatementオブジェクトを作成します。
354         * このメソッドと、close() メソッドは必ずセットで処理してください。
355         *
356         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為、ApplicationInfoオブジェクトを設定
357         * @og.rev 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す。(PostgreSQL対応)
358         * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData を ConnectionFactory経由で取得。(PostgreSQL対応)
359         *
360         * @throws SQLException Connection のオープンに失敗した場合
361         */
362        public void startInsert() throws SQLException {
363                execCnt = 0;
364                query = getInsertSQL();
365                conn  = ConnectionFactory.connection( connID,appInfo );
366                pstmt = conn.prepareStatement( query );
367                // 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す。(PostgreSQL対応)
368                useParamMetaData = ConnectionFactory.useParameterMetaData( connID );    // 5.3.8.0 (2011/08/01)
369                if( useParamMetaData ) {
370                        pMeta = pstmt.getParameterMetaData();
371                }
372        }
373
374        /**
375         * Update 処理の開始を宣言します。
376         * 内部的に、コネクションを接続して、PreparedStatementオブジェクトを作成します。
377         * このメソッドと、close() メソッドは必ずセットで処理してください。
378         *
379         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為、ApplicationInfoオブジェクトを設定
380         * @og.rev 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す。(PostgreSQL対応)
381         * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData を ConnectionFactory経由で取得。(PostgreSQL対応)
382         *
383         * @throws SQLException Connection のオープンに失敗した場合
384         */
385        public void startUpdate() throws SQLException {
386                execCnt = 0;
387                query = getUpdateSQL();
388                conn  = ConnectionFactory.connection( connID,appInfo );
389                pstmt = conn.prepareStatement( query );
390                // 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す。(PostgreSQL対応)
391                useParamMetaData = ConnectionFactory.useParameterMetaData( connID );    // 5.3.8.0 (2011/08/01)
392                if( useParamMetaData ) {
393                        pMeta = pstmt.getParameterMetaData();
394                }
395        }
396
397        /**
398         * Delete 処理の開始を宣言します。
399         * 内部的に、コネクションを接続して、PreparedStatementオブジェクトを作成します。
400         * このメソッドと、close() メソッドは必ずセットで処理してください。
401         *
402         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為、ApplicationInfoオブジェクトを設定
403         * @og.rev 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す。(PostgreSQL対応)
404         * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData を ConnectionFactory経由で取得。(PostgreSQL対応)
405         *
406         * @throws SQLException Connection のオープンに失敗した場合
407         */
408        public void startDelete() throws SQLException {
409                execCnt = 0;
410                query = getDeleteSQL();
411                conn  = ConnectionFactory.connection( connID,appInfo );
412                pstmt = conn.prepareStatement( query );
413                // 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す。(PostgreSQL対応)
414                useParamMetaData = ConnectionFactory.useParameterMetaData( connID );    // 5.3.8.0 (2011/08/01)
415                if( useParamMetaData ) {
416                        pMeta = pstmt.getParameterMetaData();
417                }
418        }
419
420        /**
421         * データ配列を渡して実際のDB処理を実行します。
422         *
423         * この処理の前に、startXXXX をコールしておき、INSER,UPDATE,DELETEのどの
424         * 処理を行うか、宣言しておく必要があります。
425         * 戻り値は、この処理での処理件数です。
426         * 最終件数は、close( boolean ) 時に取得します。
427         *
428         * @og.rev 4.0.0.0 (2007/11/28) SQLException をきちんと伝播させます。
429         * @og.rev 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す。(PostgreSQL対応)
430         * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData 時の setNull 対応(PostgreSQL対応)
431         *
432         * @param       values  カラム配列(names)に対応する設定値配列(可変長引数)
433         *
434         * @return      ここでの処理件数
435         *
436         * @see    #close( boolean )
437         * @throws SQLException Connection のクロースに失敗した場合
438         * @throws RuntimeException Connection DB処理の実行に失敗した場合
439         */
440        public int execute( final String... values ) throws SQLException {
441                final int cnt;
442                try {
443                        int clmNo = 1;  // JDBC のカラム番号は、1から始まる。
444
445                        // 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す。(PostgreSQL対応)
446                        if( useParamMetaData ) {
447                                // keys に値を割り当てます。
448                                for( int i=0; i<keys.length; i++ ) {
449                                        final int type = pMeta.getParameterType( clmNo );
450                                        // 5.3.8.0 (2011/08/01) setNull 対応
451                                        final String val = values[keysNo[i]];
452                                        if( val == null || val.isEmpty() ) {
453                                                pstmt.setNull( clmNo++, type );
454                                        }
455                                        else {
456                                                pstmt.setObject( clmNo++,val,type );
457                                        }
458                                }
459
460                                // where 条件を使用する場合は、値を割り当てます。
461                                if( useWhere ) {
462                                        // 8.5.4.2 (2024/01/12) PMD 7.0.0 ForLoopCanBeForeach
463//                                      for( int i=0; i<whereNo.length; i++ ) {
464                                        for( final int wNo : whereNo ) {
465                                                final int type = pMeta.getParameterType( clmNo );
466                                                // 5.3.8.0 (2011/08/01) setNull 対応
467//                                              final String val = values[whereNo[i]];
468                                                final String val = values[wNo];
469                                                if( val == null || val.isEmpty() ) {
470                                                        pstmt.setNull( clmNo++, type );
471                                                }
472                                                else {
473                                                        pstmt.setObject( clmNo++,val,type );
474                                                }
475                                        }
476                                }
477                        }
478                        else {
479                                // keys に値を割り当てます。
480                                for( int i=0; i<keys.length; i++ ) {
481                                        pstmt.setObject( clmNo++,values[keysNo[i]] );
482                                }
483
484                                // where 条件を使用する場合は、値を割り当てます。
485                                if( useWhere ) {
486                                        // 8.5.4.2 (2024/01/12) PMD 7.0.0 ForLoopCanBeForeach
487//                                      for( int i=0; i<whereNo.length; i++ ) {
488//                                              pstmt.setObject( clmNo++,values[whereNo[i]] );
489//                                      }
490                                        for( final int wNo : whereNo ) {
491                                                pstmt.setObject( clmNo++,values[wNo] );
492                                        }
493                                }
494                        }
495
496                        cnt = pstmt.executeUpdate();
497                        execCnt += cnt;
498                }
499                catch( final SQLException ex) {
500                        Closer.stmtClose( pstmt );
501                        pMeta = null;           // 5.1.2.0 (2010/01/01)
502                        if( conn != null ) {
503                                conn.rollback();
504                                ConnectionFactory.remove( conn,connID );
505                                conn = null;
506                        }
507                        final String errMsg = "DB処理の実行に失敗しました。" + CR
508                                                        + " query=[" + query + "]" + CR
509                                                        + " values=" + StringUtil.array2csv( values );
510                        throw new OgRuntimeException( errMsg ,ex );
511                }
512                return cnt;
513        }
514
515        /**
516         * DB処理をクロースします。
517         *
518         * 引数には、commit させる場合は、true を、rollback させる場合は、false をセットします。
519         * 戻り値は、今まで処理された合計データ件数です。
520         * この処理は、SQLException を内部で RuntimeException に変換している為、catch 節は
521         * 不要ですが、必ず finally 節で呼び出してください。そうしないと、リソースリークの
522         * 原因になります。
523         *
524         * @og.rev 5.1.2.0 (2010/01/01) pMeta のクリア
525         *
526         * @param  commitFlag コミットフラグ [true:commitする/false:rollbacする]
527         *
528         * @return      今までの合計処理件数
529         */
530        public int close( final boolean commitFlag ) {
531                try {
532//                      if( conn != null ) {                                                                                            // 8.1.1.2 (2022/02/25) Modify
533                        if( conn != null && !conn.isClosed() ) {
534                                if( commitFlag ) {      conn.commit();  }
535                                else {                          conn.rollback(); }
536                        }
537                }
538                catch( final SQLException ex) {
539                        ConnectionFactory.remove( conn,connID );
540                        conn = null;
541                        final String errMsg = "DB処理を確定(COMMIT)できませんでした。" + CR
542                                                        + " query=[" + query + "]" + CR ;
543                        throw new OgRuntimeException( errMsg,ex );
544                }
545                finally {
546                        Closer.stmtClose( pstmt );
547                        pMeta = null;           // 5.1.2.0 (2010/01/01)
548                        ConnectionFactory.close( conn,connID );
549                        conn = null;
550                }
551
552                return execCnt;
553        }
554
555        /**
556         * 文字列配列中の値とマッチするアドレスを検索します。
557         * 文字列配列がソートされていない為、バイナリサーチが使えません。よって、
558         * 総当りでループ検索しています。
559         * 総数が多い場合は、遅くなる為、マップにセットして使用することを検討ください。
560         *
561         * @param       data    ターゲットの文字列配列中
562         * @param       key             検索する文字列
563         *
564         * @return      ターゲットの添え字(存在しない場合は、-1)
565         */
566        private int findAddress( final String[] data,final String key ) {
567                int address = -1;
568                if( data != null && key != null ) {
569                        for( int i=0; i<data.length; i++ ) {
570                                if( key.equalsIgnoreCase( data[i] ) ) {
571                                        address = i;
572                                        break;
573                                }
574                        }
575                }
576                return address;
577        }
578}