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.hayabusa.report;
017
018import org.opengion.hayabusa.common.HybsSystem;
019import org.opengion.hayabusa.common.HybsSystemException;
020import org.opengion.fukurou.db.ConnectionFactory;
021import org.opengion.fukurou.db.DBUtil;                                  // 5.5.5.1 (2012/08/07)
022import org.opengion.fukurou.util.StringUtil;
023import org.opengion.fukurou.util.FileUtil;
024import org.opengion.fukurou.util.ApplicationInfo;
025import org.opengion.fukurou.util.Closer;
026
027import java.io.File;
028import java.io.IOException;
029import java.util.List;
030import java.util.Arrays;
031
032import java.sql.Connection;
033import java.sql.PreparedStatement;
034import java.sql.SQLException;
035
036/**
037 * 【EXCEL取込】雛形EXCELシートと、データEXCELシートから、指定のDBにデータを登録するクラスクラスです。
038 * 雛形EXCELシートは、{@カラム} で記述されており、このカラムのEXCEL上のセルの位置を元に、
039 * データEXCELシートから所定のデータを読みこみ、雛形明細定義(GE57)で指定のテーブルに
040 * 抜き出したデータを登録します。
041 * 雛形明細定義(GE57)では、システムID+帳票ID+シート番号をキーに、読み取る対応シートや
042 * シート毎にヘッダーテーブル、明細テーブルの指定、繰返必須カラムのしていなどにより、
043 * 読取る方式と、書き込むテーブルを指定します。
044 *
045 * @og.rev 3.8.0.0 (2005/06/07) 新規追加
046 * @og.group 帳票システム
047 *
048 * @version  4.0
049 * @author   Kazuhiko Hasegawa
050 * @since    JDK5.0,
051 */
052public class ExcelInsert {
053        private static final String CR = HybsSystem.CR ;
054
055        private final StringBuilder errMsg ;
056
057        // DBTableReport に対して設定する情報
058        private final String EXCELIN ;          // EXCEL ファイルの取込DIR ファイル名は、要求番号.xls
059
060        // 受け渡し変数
061        private final String    SYSTEM_ID       ;
062        private final String    YKNO            ;
063        private final String    LISTID          ;
064        private final boolean   DEBUG           ;       // 3.8.5.0 (2006/03/06) デバッグ用のフラグを追加
065
066        // GE54,GE57 帳票定義、明細情報
067        private String          MODELDIR        = null;         // GE54 雛形EXCELディレクトリ
068        private String          MODELFILE       = null;         // GE54 雛形EXCELファイル名
069        private String[]        SHEETNO         = null;         // GE57 雛形EXCELシート番号
070        private String[]        SHEETREF        = null;         // GE57 データEXCELシート番号
071        private String[]        HEADDBID        = null;         // GE57 ヘッダーテーブル
072        private String[]        BODYDBID        = null;         // GE57 明細テーブル
073        private String[]        LOOPCLM         = null;         // GE57 繰返必須カラム名
074
075        private ExcelLayout layout = null;
076
077        // GE54,GE57 の帳票定義情報を取得するSQL文です。
078        private static final String GE54_GE57_SELECT =
079                "SELECT A.MODELDIR,A.MODELFILE,B.SHEETNO,B.SHEETREF,B.HEADDBID,B.BODYDBID,B.LOOPCLM" +
080                " FROM GE54 A INNER JOIN GE57 B" +
081                " ON   A.SYSTEM_ID = B.SYSTEM_ID AND A.LISTID = B.LISTID" +
082                " WHERE A.FGJ = '1' AND B.FGJ = '1'" +
083                " AND  A.SYSTEM_ID = ?" +
084                " AND  A.LISTID = ?" +
085                " ORDER BY B.SHEETNO" ;
086
087        /** コネクションにアプリケーション情報を追記するかどうか指定 */
088        public static final boolean USE_DB_APPLICATION_INFO  = HybsSystem.sysBool( "USE_DB_APPLICATION_INFO" ) ;
089
090        // 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
091        private final ApplicationInfo appInfo;
092        private final String DBID = HybsSystem.sys( "RESOURCE_DBID" );          // 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対応
093
094        /**
095         * コンストラクター
096         * 引数を受けとって、インスタンスを作成します。
097         *
098         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
099         *
100         * @param system_id システムID
101         * @param ykno 要求番号
102         * @param listId 帳票ID
103         * @param excelinDir 出力ディレクトリ
104         * @param debug デバッグフラグ
105         */
106        public ExcelInsert( final String system_id, final String ykno, final String listId, final String excelinDir, final boolean debug ) {
107                SYSTEM_ID = system_id;
108                YKNO      = ykno;
109                LISTID    = listId;
110                EXCELIN   = excelinDir;
111                DEBUG     = debug;
112                errMsg    = new StringBuilder();
113
114                // 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
115                if( USE_DB_APPLICATION_INFO ) {
116                        appInfo = new ApplicationInfo();
117                        // ユーザーID,IPアドレス,ホスト名
118                        appInfo.setClientInfo( SYSTEM_ID,HybsSystem.HOST_ADRS,HybsSystem.HOST_NAME );
119                        // 画面ID,操作,プログラムID
120                        appInfo.setModuleInfo( "ExcelInsert",YKNO,LISTID );
121                }
122                else {
123                        appInfo = null;
124                }
125        }
126
127        /**
128         * 変換処理を実行します。
129         *
130         * @og.rev 3.8.0.9 (2005/10/17) エラーメッセージ強化
131         *
132         * @return 結果 [true:正常/false:異常]
133         */
134        public boolean execute() {
135                System.out.print( "ExcelInsert Started ... " );
136                boolean flag ;
137
138                try {
139                        // 初期化 GE54,GE57 帳票定義マスタより必要な情報を取得します。
140                        flag = initialDataSet();
141                        if( flag ) { System.out.print( "INIT," ); }
142
143                        // 雛型ファイルの存在チェックを行います。
144                        // 3.5.4.9 (2004/02/25) 存在チェックエラー(原因不明)の暫定対応
145                        File templateExcel = null;
146                        if( flag ) {
147                                templateExcel = FileUtil.checkFile( MODELDIR, MODELFILE + ".xls" );
148                                flag = templateExcel != null ;          // チェックの結果が null なら、見つからなかった。
149                                // 3.8.0.9 (2005/10/17) エラーメッセージ強化
150                                if( flag ) { System.out.print( "MDL IN," ); }
151                                else {
152                                        errMsg.append( "ExcelInsert MODELFILE Not Found Error!" ).append( CR );
153                                        errMsg.append( "==============================" ).append( CR );
154                                        errMsg.append( "MODELDIR=" ).append( MODELDIR ).append( CR ) ;
155                                        errMsg.append( "MODELFILE=" ).append( MODELFILE ).append( ".xls" ) ;
156                                        errMsg.append( CR ) ;
157                                }
158                        }
159
160                        // EXCELデータファイルの存在チェックを行います。
161                        File inputExcel = null;
162                        if( flag ) {
163                                inputExcel = FileUtil.checkFile( EXCELIN, YKNO + ".xls" );
164                                flag = inputExcel != null ;             // チェックの結果が null なら、見つからなかった。
165                                // 3.8.0.9 (2005/10/17) エラーメッセージ強化
166                                if( flag ) { System.out.print( "XLS IN," ); }
167                                else {
168                                        errMsg.append( "ExcelInsert EXCELIN Not Found Error!" ).append( CR );
169                                        errMsg.append( "==============================" ).append( CR );
170                                        errMsg.append( "DIR=" ).append( EXCELIN ).append( CR ) ;
171                                        errMsg.append( "FILE=" ).append( YKNO ).append( ".xls" ) ;
172                                        errMsg.append( CR ) ;
173                                }
174                        }
175
176                        // 雛形ファイルより、処理対象行列を読み取ります。
177                        if( flag ) {
178                                flag = getModelData( templateExcel );
179                                if( flag ) { System.out.print( "MDL DT," ); }
180                        }
181
182                        // EXCELデータファイルを読取り、データベースに書き込みます。
183                        if( flag ) {
184                                flag = readAndInsertDB( inputExcel );
185                                if( flag ) { System.out.print( "IN DB," ); }
186                        }
187                }
188                catch ( RuntimeException ex ) {
189                        errMsg.append( "ExcelInsert Execute Exception Error!" ).append( CR );
190                        errMsg.append( "==============================" ).append( CR );
191                        errMsg.append( StringUtil.stringStackTrace( ex ) ) ;
192                        errMsg.append( CR ) ;
193                        flag = false;
194                }
195
196                System.out.println( "End." );
197                return flag ;
198        }
199
200        /**
201         * 初期データセットを行います。
202         * ここでは、GE54,GE57 テーブルより必要な情報を取得します。
203         *
204         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
205         * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策
206         *
207         * @return 結果 [true:正常/false:異常]
208         */
209        private boolean initialDataSet() {
210                String[] args = new String[] { SYSTEM_ID,LISTID };
211                // A.MODELDIR,A.MODELFILE,B.SHEETNO,B.SHEETREF,B.HEADDBID,B.BODYDBID,B.LOOPCLM
212                String[][] vals = DBUtil.dbExecute( GE54_GE57_SELECT,args,appInfo, DBID );      // 3.8.7.0 (2006/12/15)
213                if( vals == null || vals.length == 0 ) {
214                        errMsg.append( "Data does not exist in GE54 table." ).append( CR );
215                        errMsg.append( "==============================" ).append( CR );
216                        errMsg.append( "SYSTEM_ID=[" ).append( SYSTEM_ID ).append( "] , " );
217                        errMsg.append( "LISTID=["    ).append( LISTID    ).append( "]" );
218                        errMsg.append( CR );
219                        return false;
220                }
221
222                int maxRow = vals.length;               // 先の条件判断で、最低 1 件以上存在する。
223                MODELDIR        = StringUtil.nval( vals[0][0],MODELDIR   );
224                MODELFILE       = StringUtil.nval( vals[0][1],MODELFILE  );
225
226                if( MODELDIR  == null || MODELDIR.length()  == 0 ||
227                        MODELFILE == null || MODELFILE.length() == 0 ) {
228                        errMsg.append( "MODELDIR and MODELFILE is necessary in GE54 table." ).append( CR );
229                        errMsg.append( "==============================" ).append( CR );
230                        errMsg.append( "SYSTEM_ID=[" ).append( SYSTEM_ID ).append( "] , " );
231                        errMsg.append( "LISTID=["    ).append( LISTID    ).append( "] , " );
232                        errMsg.append( "MODELDIR=["  ).append( MODELDIR  ).append( "] , " );
233                        errMsg.append( "MODELFILE=[" ).append( MODELFILE ).append( "] " );
234                        errMsg.append( CR );
235                        return false;
236                }
237
238                SHEETNO         = new String[maxRow];
239                SHEETREF        = new String[maxRow];
240                HEADDBID        = new String[maxRow];
241                BODYDBID        = new String[maxRow];
242                LOOPCLM         = new String[maxRow];
243
244                for( int row=0; row<maxRow; row++ ) {
245                        SHEETNO[row]    = StringUtil.nval( vals[row][2],null );
246                        SHEETREF[row]   = StringUtil.nval( vals[row][3],null );
247                        HEADDBID[row]   = StringUtil.nval( vals[row][4],null );
248                        BODYDBID[row]   = StringUtil.nval( vals[row][5],null );
249                        LOOPCLM[row]    = StringUtil.nval( vals[row][6],null );
250
251                        // SHEETNO と SHEETREF は、どちら『も』必須
252                        // HEADDBID と BODYDBID は、どちら『か』必須
253                        if( SHEETNO[row] == null || SHEETREF[row] == null ||
254                                ( HEADDBID[row] == null && BODYDBID[row] == null ) ) {
255                                errMsg.append( "SHEETNO と SHEETREF は、どちら『も』必須" ).append( CR );
256                                errMsg.append( "HEADDBID と BODYDBID は、どちら『か』必須" ).append( CR );
257                                errMsg.append( "==============================" ).append( CR );
258                                errMsg.append( "SYSTEM_ID=[" ).append( SYSTEM_ID     ).append( "] , " );
259                                errMsg.append( "LISTID=["    ).append( LISTID        ).append( "] , " );
260                                errMsg.append( "SHEETNO=["   ).append( SHEETNO[row]  ).append( "] , " );
261                                errMsg.append( "SHEETREF=["  ).append( SHEETREF[row] ).append( "] , " );
262                                errMsg.append( "HEADDBID=["  ).append( HEADDBID[row] ).append( "] , " );
263                                errMsg.append( "BODYDBID=["  ).append( BODYDBID[row] ).append( "] " );
264                                errMsg.append( CR );
265                                return false;
266                        }
267                }
268
269                return true;
270        }
271
272        /**
273         * 雛形ファイルより、対象行列を読み取ります。
274         *
275         * @param       file    雛形ファイル
276         *
277         * @return 結果 [true:正常/false:異常]
278         */
279        private boolean getModelData( final File file ) {
280                try {
281                        layout = HybsHSSFListener.makeExcelLayout( file,false );
282                }
283                catch( IOException ex ) {
284                        errMsg.append( "Template Excel File can not ModelData." ).append( CR );
285                        errMsg.append( "==============================" ).append( CR );
286                        errMsg.append( "File=" ).append( file.getAbsolutePath() );
287                        errMsg.append( StringUtil.stringStackTrace( ex ) );
288                        errMsg.append( CR );
289                        return false;
290                }
291
292                return true;
293        }
294
295        /**
296         * EXCELを読取り、データベースに書き込みます。
297         *
298         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
299         *
300         * @param       file    EXCELファイル
301         *
302         * @return 結果 [true:正常/false:異常]
303         */
304        private boolean readAndInsertDB( final File file ) {
305
306                ExcelDataPickup pickup = new ExcelDataPickup( layout,file,DEBUG );
307
308                // 実際のデータシートの枚数
309                int sheetSize = pickup.getSheetSize();
310                // SHEETREF に対して、実際に割り当てなおしたシート対応
311                int[] reference = makeSheetReference( sheetSize,SHEETREF );
312
313                DatabaseExecute exec = new DatabaseExecute();
314                exec.setApplicationInfo( appInfo );             // 3.8.7.0 (2006/12/15)
315                int ykno = Integer.parseInt(YKNO) ;
316                for( int shNo=0; shNo<sheetSize; shNo++ ) {
317                        int ref = reference[shNo];
318                        if( ref < 0 ) { continue; }     // 処理対象外
319
320                        pickup.execute( Integer.parseInt( SHEETNO[ref] ),shNo,LOOPCLM[ref] ) ;
321
322                        String headerQuery = layout.getHeaderInsertQuery( HEADDBID[ref] );
323                        if( headerQuery != null ) {
324                                exec.setStatement( headerQuery );
325
326                                String[] headerData = layout.getHeaderInsertData( SYSTEM_ID,ykno,shNo );
327                                exec.dbExecute( headerData );
328                        }
329
330                        String bodyQuery = layout.getBodyInsertQuery( BODYDBID[ref] );
331                        if( bodyQuery != null ) {
332                                exec.setStatement( bodyQuery );
333
334                                List<String[]> bodyData  = layout.getBodyInsertData( SYSTEM_ID,ykno,shNo );
335                                for( int j=0; j<bodyData.size(); j++ ) {
336                                        exec.dbExecute( bodyData.get(j) );
337                                }
338                        }
339                }
340                exec.commit();
341                pickup.close();
342
343                return true;
344        }
345
346        /**
347         * GE57 に指定のSHEETNOとSHEETREF配列より、実際にアクセスするシート番号に対応したリファレンス配列を求めます。
348         * SHEETNO は、雛形EXCELの使用するシート番号を指定します。SHEETREFは、その雛形シートを
349         * 利用して処理するデータEXCELのシートを指定します。シート番号は、0から始まります。
350         * この、データEXCELシート(SHEETREF)は、単一数、カンマ結合、LAST文字 で指定します。
351         * 単一数:雛形シートと1対1で対応するデータEXCELシート番号
352         * カンマ結合:3,4,5 や、2,5 などの複数シートをひとつの雛形シートで処理する場合に設定します。
353         * LAST文字:5,LAST や LAST と記述することで、それ以降の全データシートを雛形シートで処理します。
354         *
355         * ここでは、SHEETREF配列 を実際のデータEXCELシート数分の配列に再配置し、その元のアドレスを
356         * 指すリファレンス情報を返します。
357         * このリファレンス情報を元に、SHEETNO,HEADDBID,BODYDBID,LOOPCLM などの元の配列にアクセスし、
358         * 設定値を取得してきます。
359         *
360         * 例)
361         *  SHEETNO  = { "1","2"  ,"3","4"  ,"6" };
362         *  SHEETREF = { "1","2,6","4","5,3","8,LAST" };
363         *  HEADDBID = { "A","B"  ,"C","D"  ,"E" };
364         *  データシート数=11
365         *
366         * i=[0]  , No=[1], REF=[1]
367         * i=[1]  , No=[2], REF=[2,6]
368         * i=[2]  , No=[3], REF=[4]
369         * i=[3]  , No=[4], REF=[5,3]
370         * i=[4]  , No=[6], REF=[8,LAST]
371         * =========================
372         * REF=[0]  , Ref=[-1], SHEETNO[]   = -  , HEADDBID[]   = -
373         * REF=[1]  , Ref=[0],  SHEETNO[[0]]=[1] , HEADDBID[[0]]=[A]
374         * REF=[2]  , Ref=[1],  SHEETNO[[1]]=[2] , HEADDBID[[1]]=[B]
375         * REF=[3]  , Ref=[3],  SHEETNO[[3]]=[4] , HEADDBID[[3]]=[D]
376         * REF=[4]  , Ref=[2],  SHEETNO[[2]]=[3] , HEADDBID[[2]]=[C]
377         * REF=[5]  , Ref=[3],  SHEETNO[[3]]=[4] , HEADDBID[[3]]=[D]
378         * REF=[6]  , Ref=[1],  SHEETNO[[1]]=[2] , HEADDBID[[1]]=[B]
379         * REF=[7]  , Ref=[-1], SHEETNO[]   = -  , HEADDBID[]   = -
380         * REF=[8]  , Ref=[4],  SHEETNO[[4]]=[6] , HEADDBID[[4]]=[E]
381         * REF=[9]  , Ref=[4],  SHEETNO[[4]]=[6] , HEADDBID[[4]]=[E]
382         * REF=[10] , Ref=[4],  SHEETNO[[4]]=[6] , HEADDBID[[4]]=[E]
383         *
384         * @param       size    データシートの総件数
385         * @param       sheetRef        データEXCELシートの対応する配列(単一数、カンマ結合、LAST文字 が使用可能)
386         *
387         * @return      データ件数分に再配置した、雛形EXCELシート番号配列。使用しない場合は、-1 がセット。
388         */
389        private int[] makeSheetReference( final int size,final String[] sheetRef ) {
390
391                int[] reference = new int[size];
392                Arrays.fill( reference ,-1 );
393
394                int maxNo = -1;
395                for( int i=0; i<sheetRef.length; i++ ) {
396                        String[] temp = StringUtil.csv2Array( sheetRef[i] );
397                        for( int j=0; j<temp.length; j++ ) {
398                                if( temp[j].equals( "LAST" ) ) {
399                                        for( int k=maxNo; k<size; k++ ) {
400                                                reference[k]  = i ;
401                                        }
402                                        i=size;
403                                        break;
404                                }
405                                else {
406                                        int no = Integer.parseInt(temp[j]) ;
407                                        if( no < size ) {
408                                                reference[no]  = i ;
409                                                if( maxNo < no ) { maxNo = no+1; }
410                                        }
411                                        else {
412                                                String errMsg = "データシートと雛形明細定義の対応ができません。"
413                                                                + " データシート総件数=[" + size + "] "
414                                                                + " sheetRef[" + i + "]=" + sheetRef[i] ;
415                                                throw new HybsSystemException( errMsg );
416                                        }
417                                }
418                        }
419                }
420                return reference ;
421        }
422
423        /**
424         * エラーが存在した場合に、エラーメッセージを返します。
425         *
426         * @return エラーメッセージ String
427         */
428        public String getErrMsg() {
429                return errMsg.toString();
430        }
431}
432
433/**
434 * 連続した データベース処理を行う為の、管理処理クラスです。
435 * ExcelInsert でのコーディングを分けるためだけのクラスです。
436 *
437 * オブジェクト作成時に、DEFAULT 接続を内部にキープし、setStatement( String )で
438 * PreparedStatementオブジェクトを作成します。このメソッドを呼ぶまでは、
439 * 同じ PreparedStatementオブジェクトを使い続けます。
440 * dbExecute( String[] ) メソッドで、PreparedStatement に設定する引数配列をセットします。
441 * この段階では、commit も、PreparedStatementのclose も行いませんので、連続して、
442 * dbExecute( String[] ) メソッドを呼び出すことが可能です。
443 * 最後に、commit() で、Connection は、プールに返されます。
444 *
445 * エラー時は、rollback() して、Connection は、破棄されます。
446 *
447 * @og.group 帳票システム
448 *
449 * @version  4.0
450 * @author   Kazuhiko Hasegawa
451 * @since    JDK5.0,
452 */
453class DatabaseExecute {
454        // 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更
455        private static final String DBID = null ;
456
457        private Connection conn = null;
458        private PreparedStatement pstmt = null;
459        private String   tempSql  = null;       // エラー時にSQL文を表示させる場合に使用します。
460        private ApplicationInfo appInfo = null;
461
462        /**
463         * アクセスログ取得の為,ApplicationInfoオブジェクトを設定します。
464         *
465         * @og.rev 3.8.7.0 (2006/12/15) 新規追加
466         *
467         * @param   appInfo ApplicationInfo
468         */
469        public void setApplicationInfo( final ApplicationInfo appInfo ) {
470                this.appInfo = appInfo;
471        }
472
473        /**
474         * PreparedStatementオブジェクトを作成します。
475         * 次に、このメソッドを呼ぶまでは、同じ PreparedStatementオブジェクトを使い続けます。
476         *
477         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
478         * @og.rev 4.0.0.1 (2007/12/03) try ~ catch ~ finally をきちんと行う。
479         *
480         * @param stmt String
481         */
482        public void setStatement( final String stmt ) {
483                boolean errFlag = true ;
484                tempSql = stmt;
485                try {
486                        // 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
487                        if( conn == null ) { conn = ConnectionFactory.connection( DBID,appInfo ); }
488                        Closer.stmtClose( pstmt );
489                        pstmt = conn.prepareStatement( stmt );
490                        errFlag = false ;
491                }
492                catch (SQLException ex) {
493                        String errMsg = "Statement を作成できませんでした。" + HybsSystem.CR
494                                                + "SQL=[" + stmt + "]"
495                                                + ex.getMessage() + ":" + ex.getSQLState() ;
496                        throw new HybsSystemException( errMsg,ex );
497                }
498                finally {
499                        if( errFlag ) { errorFinally(); }
500                }
501        }
502
503        /**
504         * Connection を commit します。
505         * このオブジェクトを終了する最後に行います。
506         *
507         */
508        public void commit() {
509                boolean errFlag = true ;
510                try {
511                        conn.commit();
512                        errFlag = false ;
513                }
514                catch (SQLException ex) {
515                        Closer.rollback( conn );
516                        String errMsg = "Connection をコミットできませんでした。" + HybsSystem.CR
517                                                + ex.getMessage() + ":" + ex.getSQLState() ;
518                        throw new HybsSystemException( errMsg,ex );
519                }
520                finally {
521                        Closer.stmtClose( pstmt );
522                        if( errFlag ) { ConnectionFactory.remove( conn,DBID ); }
523                        else {                  ConnectionFactory.close( conn,DBID );  }
524                        conn = null;
525                }
526        }
527
528        /**
529         * PreparedStatement に設定する引数配列をセットします。
530         *
531         * この段階では、commit も、PreparedStatementのclose も行いませんので、連続して、
532         * dbExecute( String[] ) メソッドを呼び出すことが可能です。
533         *
534         * @param   args オブジェクトの引数配列
535         */
536        public void dbExecute( final String[] args ) {
537                // System.out.println( StringUtil.array2csv( args ) );
538
539                boolean errFlag = true ;
540                try {
541                        for( int i=0; i<args.length; i++ ) {
542                                pstmt.setString( i+1,args[i] );
543                        }
544                        pstmt.execute();
545                        errFlag = false ;
546                }
547                catch (SQLException ex) {
548                        String errMsg = "データベース処理を実行できませんでした。" + HybsSystem.CR
549                                                + "ARGS=[" + StringUtil.array2csv( args ) + "]" + HybsSystem.CR
550                                                + "SQL=[" + tempSql + "]"
551                                                + ex.getMessage() + ":" + ex.getSQLState() ;
552                        throw new HybsSystemException( errMsg,ex );
553                }
554                finally {
555                        if( errFlag ) { errorFinally(); }
556                }
557        }
558
559        /**
560         * エラー発生時の処理
561         *
562         * PreparedStatement のクローズと、Connection の破棄を行います。
563         */
564        private void errorFinally() {
565                Closer.stmtClose( pstmt );
566                pstmt = null;
567                Closer.rollback( conn );
568                ConnectionFactory.remove( conn,DBID );
569                conn = null;
570        }
571}