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.io.IOException;
019import java.io.Reader;
020import java.sql.CallableStatement;
021import java.sql.Clob;
022import java.sql.Connection;
023import java.sql.PreparedStatement;
024import java.sql.ParameterMetaData;
025import java.sql.ResultSet;
026import java.sql.ResultSetMetaData;
027import java.sql.SQLException;
028import java.sql.Types;
029import java.util.ArrayList;
030import java.util.Locale;
031
032import org.opengion.fukurou.util.ApplicationInfo;
033import org.opengion.fukurou.util.Closer;
034import org.opengion.fukurou.util.StringUtil;
035import org.opengion.fukurou.util.HybsDateUtil;
036
037/**
038 * データベース関連の便利なメソッドを集めた簡易ユーティリティークラスです。
039 * 全てのメソッドは、static メソッドになっています。
040 *
041 * @og.rev 2.1.1.1 (2002/11/15) Serializable インターフェースを削除する。
042 * @og.rev 4.0.0.0 (2007/10/16) DBアクセス関係のメソッドのみをパッケージ移動(hayabusa/db ⇒ fukurou/db)
043 * @og.group DB/Shell制御
044 *
045 * @version  4.0
046 * @author   Kazuhiko Hasegawa
047 * @since    JDK5.0,
048 */
049public final class DBUtil {
050
051        /** システム依存の改行記号をセットします。4.0.0.0(2007/10/17) */
052        private static final String CR = System.getProperty( "line.separator" );
053
054        /**
055         * インスタンスを作らないので、コンストラクタは、private に設定します。
056         */
057        private DBUtil() {}
058
059        /**
060         * 初期データベースに接続して、Queryを実行します(互換性確保のため残しています)。
061         *
062         * ステートメントと引数により、Prepared クエリーの検索のみ実行します。
063         * 結果は,すべて文字列に変換されて格納されます。
064         *
065         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
066         * @og.rev 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更
067         * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応します。
068         * @og.rev 5.3.7.0 (2011/07/01) TransactionReal の引数変更
069         * @og.rev 5.3.8.0 (2011/08/01) TransactionReal と close() 処理をセットで実行する。
070         *
071         * @param   stmt ステートメント文字列
072         * @param   args オブジェクトの引数配列
073         * @param   appInfo アプリ情報オブジェクト
074         *
075         * @return  検索結果の配列
076         */
077        public static String[][] dbExecute( final String stmt ,final String[] args ,final ApplicationInfo appInfo ) {
078                Transaction tran = new TransactionReal( appInfo );                      // 5.3.7.0 (2011/07/01) 引数変更
079
080                // 5.3.8.0 (2011/08/01) TransactionReal と close() 処理をセットで実行する。
081                try {
082                        return dbExecute( stmt, args, tran, null, false );
083                }
084                finally {
085                        // エラー発生時は、tran.rollback() が呼ばれているため、close()時にエラーフラグをセットする必要はない。
086                        tran.close();
087                }
088        }
089
090        /**
091         * 初期データベースに接続して、Queryを実行します(Transaction 対応)。
092         *
093         * ステートメントと引数により、Prepared クエリーの検索のみ実行します。
094         * 結果は,すべて文字列に変換されて格納されます。
095         * ここでは、Transactionオブジェクトから、Connection を取り出して使用します。
096         *
097         * @og.rev 5.1.9.0 (2010/08/01) 新規作成 Transaction 対応
098         *
099         * @param   stmt ステートメント文字列
100         * @param   args オブジェクトの引数配列
101         * @param   tran Transactionオブジェクト
102         *
103         * @return  検索結果の配列
104         */
105        public static String[][] dbExecute( final String stmt ,final String[] args ,final Transaction tran ) {
106                return dbExecute( stmt, args, tran, null, false );
107        }
108
109        /**
110         * 検索するデータベースを指定して、Queryを実行します(互換性確保のため残しています)。
111         *
112         * ステートメントと引数により、Prepared クエリーの検索のみ実行します。
113         * 結果は,すべて文字列に変換されて格納されます。
114         * 追加:検索以外のSQLも実行できます。結果は、null を返します。
115         *
116         * @og.rev 3.0.0.0 (2002/12/25) 検索のみのクエリーから、何でもありのクエリーに変更
117         * @og.rev 2.3.1.3 (2003/01/28) Open Cursor が、大量に残る件の対応。ResultSet を close()
118         * @og.rev 3.8.0.8 (2005/10/03) エラーメッセージの出力順をメッセージ+Queryに変更します。
119         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
120         * @og.rev 4.0.0.1 (2007/12/03) try ~ catch ~ finally をきちんと行う。
121         * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応します。
122         * @og.rev 5.3.7.0 (2011/07/01) TransactionReal の引数変更
123         * @og.rev 5.3.8.0 (2011/08/01) TransactionReal と close() 処理をセットで実行する。
124         *
125         * @param   stmt ステートメント文字列
126         * @param   args オブジェクトの引数配列
127         * @param   appInfo アプリ情報オブジェクト
128         * @param   dbid 接続先ID
129         *
130         * @return  検索結果の配列
131         */
132        public static String[][] dbExecute( final String stmt ,final String[] args, final ApplicationInfo appInfo, final String dbid ) {
133                Transaction tran = new TransactionReal( appInfo );                      // 5.3.7.0 (2011/07/01) 引数変更
134
135                // 5.3.8.0 (2011/08/01) TransactionReal と close() 処理をセットで実行する。
136                try {
137                        return dbExecute( stmt, args, tran, dbid, false  );
138                }
139                finally {
140                        // エラー発生時は、tran.rollback() が呼ばれているため、close()時にエラーフラグをセットする必要はない。
141                        tran.close();
142                }
143        }
144
145        /**
146         * 検索するデータベースを指定して、Queryを実行します(Transaction 対応)。
147         *
148         * ステートメントと引数により、Prepared クエリーの検索のみ実行します。
149         * 結果は,すべて文字列に変換されて格納されます。
150         * 追加:検索以外のSQLも実行できます。結果は、null を返します。
151         * ここでは、Transactionオブジェクトから、Connection を取り出して使用します。
152         *
153         * @og.rev 5.1.9.0 (2010/08/01) 新規作成 Transaction 対応
154         *
155         * @param   stmt ステートメント文字列
156         * @param   args オブジェクトの引数配列
157         * @param   tran Transactionオブジェクト
158         * @param   dbid 接続先ID
159         *
160         * @return  検索結果の配列
161         */
162        public static String[][] dbExecute( final String stmt ,final String[] args, final Transaction tran , final String dbid ) {
163                return dbExecute( stmt, args, tran, dbid, false );
164        }
165
166        /**
167         * 検索するデータベースを指定して、Queryを実行します(互換性確保のため残しています)。
168         *
169         * ステートメントと引数により、Prepared クエリーの検索のみ実行します。
170         * 結果は,すべて文字列に変換されて格納されます。
171         * 追加:検索以外のSQLも実行できます。結果は、null を返します。
172         *
173         * @og.rev 4.3.7.0 (2009/06/01) 新規作成
174         * @og.rev 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す。(PostgreSQL対応)
175         * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応します。
176         * @og.rev 5.3.7.0 (2011/07/01) TransactionReal の引数変更
177         * @og.rev 5.3.8.0 (2011/08/01) TransactionReal と close() 処理をセットで実行する。
178         *
179         * @param   stmt ステートメント文字列
180         * @param   args オブジェクトの引数配列
181         * @param   appInfo アプリ情報オブジェクト
182         * @param   dbid 接続先ID
183         * @param   useHeader 1行目にヘッダーを含めるか
184         *
185         * @return  検索結果の配列
186         */
187        public static String[][] dbExecute( final String stmt ,final String[] args, final ApplicationInfo appInfo, final String dbid, final boolean useHeader ) {
188                Transaction tran = new TransactionReal( appInfo );                      // 5.3.7.0 (2011/07/01) 引数変更
189
190                // 5.3.8.0 (2011/08/01) TransactionReal と close() 処理をセットで実行する。
191                try {
192                        return dbExecute( stmt, args, tran, dbid, useHeader );
193                }
194                finally {
195                        // エラー発生時は、tran.rollback() が呼ばれているため、close()時にエラーフラグをセットする必要はない。
196                        tran.close();
197                }
198        }
199
200        /**
201         * 検索するデータベースを指定して、Queryを実行します(Transaction 対応)。
202         *
203         * ステートメントと引数により、Prepared クエリーの検索のみ実行します。
204         * 結果は,すべて文字列に変換されて格納されます。
205         * 追加:検索以外のSQLも実行できます。結果は、null を返します。
206         *
207         * @og.rev 5.1.9.0 (2010/08/01) 新規作成 Transaction 対応
208         * @og.rev 5.3.8.0 (2011/08/01) Transaction を引数で受け取った場合は、close() しない。
209         * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData を ConnectionFactory経由で取得。(PostgreSQL対応)、setNull 対応
210         *
211         * @param   stmt ステートメント文字列
212         * @param   args オブジェクトの引数配列
213         * @param   tran Transactionオブジェクト
214         * @param   dbid 接続先ID
215         * @param   useHeader 1行目にヘッダーを含めるか
216         *
217         * @return  検索結果の配列
218         */
219        public static String[][] dbExecute( final String stmt ,final String[] args, final Transaction tran, final String dbid, final boolean useHeader ) {
220                PreparedStatement pstmt = null;
221                ResultSet resultSet = null;
222                String[][] rtn = null;
223                try {
224                        Connection conn = tran.getConnection( dbid );                           // 5.1.9.0 (2010/08/01) Transaction 対応
225                        pstmt = conn.prepareStatement( stmt );
226                        if( args != null ) {
227                                // 5.1.1.0 (2009/11/11) setObject に ParameterMetaData の getParameterType を渡す。(PostgreSQL対応)
228                                boolean useParamMetaData = ConnectionFactory.useParameterMetaData( dbid );      // 5.3.8.0 (2011/08/01)
229                                if( useParamMetaData ) {
230                                        ParameterMetaData pMeta = pstmt.getParameterMetaData();
231                                        for( int i=0; i<args.length; i++ ) {
232                                                int type = pMeta.getParameterType( i+1 );
233                                                // 5.3.8.0 (2011/08/01) setNull 対応
234                                                String val = args[i];
235                                                if( val == null || val.isEmpty() ) {
236                                                        pstmt.setNull( i+1, type );
237                                                }
238                                                else {
239                                                        pstmt.setObject( i+1, val, type );
240                                                }
241                                        }
242                                }
243                                else {
244                                        for( int i=0; i<args.length; i++ ) {
245                                                pstmt.setObject( i+1,args[i] );
246                                        }
247                                }
248                        }
249                        boolean status = pstmt.execute();
250                        if( status ) {
251                                resultSet = pstmt.getResultSet();
252                                rtn = DBUtil.resultToArray( resultSet,useHeader ); // 4.3.7.0 (2009/06/01)
253                        }
254                        else {
255                                tran.commit();                  // 5.1.9.0 (2010/08/01) Transaction 対応
256                        }
257                }
258                catch ( SQLException ex ) {
259                        tran.rollback();                        // 5.1.9.0 (2010/08/01) Transaction 対応
260                        String errMsg = ex.getMessage() + ":" + ex.getSQLState() + CR
261                                                + "SQL=[" + stmt + "]" + CR
262                                                + "ARG=[" + StringUtil.array2csv( args ) + "]" + CR
263                                                + "DBID=[" + dbid + "]" + CR;
264                        throw new RuntimeException( errMsg,ex );
265                }
266                finally {
267                        Closer.resultClose( resultSet );
268                        Closer.stmtClose( pstmt );
269                }
270                return rtn;
271        }
272
273        /**
274         * 初期データベースに接続して、CallableStatement(PL/SQL)を実行します(互換性確保のため残しています)。
275         * ステートメントと引数により、CallableStatement クエリーを実行します。
276         * 結果は,ステータスとエラーメッセージを返します。便宜的に、String配列に
277         * 設定して返します。
278         * ステートメント文字列には、 { call PLSQL( ?,?,?・・・ ) } となります。
279         * 第一引数、第二引数は、OUT属性で、結果(STATUS)とエラー時の内容(ERR_CODE)を返します。
280         * 第三引数以降の ? には、オブジェクトの引数配列 が順に割り当てられます。
281         *
282         * @og.rev 3.8.0.0 (2005/06/07) 新規追加
283         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
284         * @og.rev 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更
285         * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応します。
286         * @og.rev 5.3.7.0 (2011/07/01) TransactionReal の引数変更
287         * @og.rev 5.3.8.0 (2011/08/01) TransactionReal と close() 処理をセットで実行する。
288         *
289         * @param   stmt ステートメント文字列
290         * @param   args オブジェクトの引数配列
291         * @param   appInfo アプリ情報オブジェクト
292         *
293         * @return  実行結果([0]=ステータス、[1]=エラーメッセージ
294         */
295        public static String[] dbCallExecute( final String stmt ,final String[] args, final ApplicationInfo appInfo ) {
296                Transaction tran = new TransactionReal( appInfo );                      // 5.3.7.0 (2011/07/01) 引数変更
297
298                // 5.3.8.0 (2011/08/01) TransactionReal と close() 処理をセットで実行する。
299                try {
300                        return dbCallExecute( stmt ,args, tran, null );
301                }
302                finally {
303                        // エラー発生時は、tran.rollback() が呼ばれているため、close()時にエラーフラグをセットする必要はない。
304                        tran.close();
305                }
306        }
307
308        /**
309         * 初期データベースに接続して、CallableStatement(PL/SQL)を実行します(Transaction 対応)。
310         * ステートメントと引数により、CallableStatement クエリーを実行します。
311         * 結果は,ステータスとエラーメッセージを返します。便宜的に、String配列に
312         * 設定して返します。
313         * ステートメント文字列には、 { call PLSQL( ?,?,?・・・ ) } となります。
314         * 第一引数、第二引数は、OUT属性で、結果(STATUS)とエラー時の内容(ERR_CODE)を返します。
315         * 第三引数以降の ? には、オブジェクトの引数配列 が順に割り当てられます。
316         *
317         * @og.rev 5.1.9.0 (2010/08/01) 新規作成 Transaction 対応
318         *
319         * @param   stmt ステートメント文字列
320         * @param   args オブジェクトの引数配列
321         * @param   tran Transactionオブジェクト
322         *
323         * @return  実行結果([0]=ステータス、[1]=エラーメッセージ
324         */
325        public static String[] dbCallExecute( final String stmt ,final String[] args, final Transaction tran ) {
326                return dbCallExecute( stmt ,args, tran, null );
327        }
328
329        /**
330         * 検索するデータベースを指定して、CallableStatement(PL/SQL)を実行します(互換性確保のため残しています)。
331         * ステートメントと引数により、CallableStatement クエリーを実行します。
332         * 結果は,ステータスとエラーメッセージを返します。便宜的に、String配列に
333         * 設定して返します。
334         * ステートメント文字列には、 { call PLSQL( ?,?,?・・・ ) } となります。
335         * 第一引数、第二引数は、OUT属性で、結果(STATUS)とエラー時の内容(ERR_CODE)を返します。
336         * 第三引数以降の ? には、オブジェクトの引数配列 が順に割り当てられます。
337         * 検索するデータベースは、DEFAULT です。
338         *
339         * @og.rev 3.8.0.0 (2005/06/07) 新規追加
340         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
341         * @og.rev 4.0.0.1 (2007/12/03) try ~ catch ~ finally をきちんと行う。
342         * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応します。
343         * @og.rev 5.3.7.0 (2011/07/01) TransactionReal の引数変更
344         * @og.rev 5.3.8.0 (2011/08/01) TransactionReal と close() 処理をセットで実行する。
345         *
346         * @param   stmt ステートメント文字列
347         * @param   args オブジェクトの引数配列
348         * @param   appInfo アプリ情報オブジェクト
349         * @param   dbid 接続先ID
350         *
351         * @return  実行結果([0]=ステータス、[1]=エラーメッセージ
352         */
353        public static String[] dbCallExecute( final String stmt ,final String[] args, final ApplicationInfo appInfo ,final String dbid ) {
354                Transaction tran = new TransactionReal( appInfo );                      // 5.3.7.0 (2011/07/01) 引数変更
355
356                // 5.3.8.0 (2011/08/01) TransactionReal と close() 処理をセットで実行する。
357                try {
358                        return dbCallExecute( stmt ,args, tran, dbid );
359                }
360                finally {
361                        // エラー発生時は、tran.rollback() が呼ばれているため、close()時にエラーフラグをセットする必要はない。
362                        tran.close();
363                }
364        }
365
366        /**
367         * 検索するデータベースを指定して、CallableStatement(PL/SQL)を実行します(Transaction 対応)。
368         * ステートメントと引数により、CallableStatement クエリーを実行します。
369         * 結果は,ステータスとエラーメッセージを返します。便宜的に、String配列に
370         * 設定して返します。
371         * ステートメント文字列には、 { call PLSQL( ?,?,?・・・ ) } となります。
372         * 第一引数、第二引数は、OUT属性で、結果(STATUS)とエラー時の内容(ERR_CODE)を返します。
373         * 第三引数以降の ? には、オブジェクトの引数配列 が順に割り当てられます。
374         * 検索するデータベースは、DEFAULT です。
375         *
376         * @og.rev 5.1.9.0 (2010/08/01) 新規作成 Transaction 対応
377         * @og.rev 5.3.8.0 (2011/08/01) Transaction を引数で受け取った場合は、close() しない。
378         *
379         * @param   stmt ステートメント文字列
380         * @param   args オブジェクトの引数配列
381         * @param   tran Transactionオブジェクト
382         * @param   dbid 接続先ID
383         *
384         * @return  実行結果([0]=ステータス、[1]=エラーメッセージ
385         */
386        public static String[] dbCallExecute( final String stmt ,final String[] args, final Transaction tran ,final String dbid ) {
387                CallableStatement callStmt = null ;
388
389                String[] rtn = new String[2] ;
390
391                try {
392                        Connection conn = tran.getConnection( dbid );                           // 5.1.9.0 (2010/08/01) Transaction 対応
393                        callStmt = conn.prepareCall( stmt );
394
395                        callStmt.registerOutParameter( 1, Types.INTEGER );
396                        callStmt.registerOutParameter( 2, Types.VARCHAR );
397                        if( args != null ) {
398                                for( int i=0; i<args.length; i++ ) {
399                                        callStmt.setObject( i+3,args[i] );
400                                }
401                        }
402                        callStmt.execute();
403
404                        rtn[0] = String.valueOf( callStmt.getInt(1) );  // 結果ステータス
405                        rtn[1] = callStmt.getString(2);                                 // 内容(エラーメッセージ)
406
407                        tran.commit();                                  // 5.1.9.0 (2010/08/01) Transaction 対応
408                }
409                catch ( SQLException ex ) {
410                        tran.rollback();                                // 5.1.9.0 (2010/08/01) Transaction 対応
411                        String errMsg = ex.getMessage() + ":" + ex.getSQLState() + CR
412                                                + "SQL=[" + stmt + "]" + CR
413                                                + "ARG=[" + StringUtil.array2csv( args ) + "]" + CR
414                                                + "DBID=[" + dbid + "]" + CR;
415                        throw new RuntimeException( errMsg,ex );
416                }
417                finally {
418                        Closer.stmtClose( callStmt );
419                        // 5.3.8.0 (2011/08/01) Transaction を引数で受け取った場合は、close() しない。
420                }
421                return rtn;
422        }
423
424        /**
425         * SQL文の実行結果において、データの件数を取得します(互換性確保のため残しています)。
426         * ステートメントと引数により、Prepared クエリーの検索を実行します。
427         * 結果は、件数を数値で返します。
428         * あくまで、存在チェックに必要な処理のみ行っているため、通常の検索より高速です。
429         *
430         * @og.rev 3.5.0.0 (2003/09/17) 新規作成
431         * @og.rev 3.8.0.8 (2005/10/03) エラーメッセージの出力順をメッセージ+Queryに変更します。
432         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
433         * @og.rev 4.0.0.1 (2007/12/03) try ~ catch ~ finally をきちんと行う。
434         * @og.rev 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す。(PostgreSQL対応)
435         * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応します。
436         * @og.rev 5.3.7.0 (2011/07/01) TransactionReal の引数変更
437         * @og.rev 5.3.8.0 (2011/08/01) TransactionReal と close() 処理をセットで実行する。
438         *
439         * @param   stmt ステートメント文字列
440         * @param   args オブジェクトの引数配列
441         * @param   appInfo アプリ情報オブジェクト
442         * @param   dbid 接続先ID
443         *
444         * @return  検索結果(データの件数)
445         */
446        public static int dbExist( final String stmt ,final String[] args, final ApplicationInfo appInfo , final String dbid ) {
447                Transaction tran = new TransactionReal( appInfo );                      // 5.3.7.0 (2011/07/01) 引数変更
448
449                // 5.3.8.0 (2011/08/01) TransactionReal と close() 処理をセットで実行する。
450                try {
451                        return dbExist( stmt ,args, tran , dbid );
452                }
453                finally {
454                        // エラー発生時は、tran.rollback() が呼ばれているため、close()時にエラーフラグをセットする必要はない。
455                        tran.close();
456                }
457        }
458
459        /**
460         * SQL文の実行結果において、データの件数を取得します(Transaction 対応)。
461         * ステートメントと引数により、Prepared クエリーの検索を実行します。
462         * 結果は、件数を数値で返します。
463         * あくまで、存在チェックに必要な処理のみ行っているため、通常の検索より高速です。
464         *
465         * @og.rev 5.1.9.0 (2010/08/01) 新規作成 Transaction 対応
466         * @og.rev 5.3.8.0 (2011/08/01) Transaction を引数で受け取った場合は、close() しない。
467         * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData を ConnectionFactory経由で取得。(PostgreSQL対応)、setNull 対応
468         *
469         * @param   stmt ステートメント文字列
470         * @param   args オブジェクトの引数配列
471         * @param   tran Transactionオブジェクト
472         * @param   dbid 接続先ID
473         *
474         * @return  検索結果(データの件数)
475         */
476        public static int dbExist( final String stmt ,final String[] args, final Transaction tran , final String dbid ) {
477                PreparedStatement pstmt = null;
478                ResultSet resultSet = null;
479                int rtnCnt = -1;
480
481                try {
482                        Connection conn = tran.getConnection( dbid );                           // 5.1.9.0 (2010/08/01) Transaction 対応
483                        pstmt = conn.prepareStatement( stmt );
484                        if( args != null ) {
485                                // 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す。(PostgreSQL対応)
486                                boolean useParamMetaData = ConnectionFactory.useParameterMetaData( dbid );      // 5.3.8.0 (2011/08/01)
487                                if( useParamMetaData ) {
488                                        ParameterMetaData pMeta = pstmt.getParameterMetaData();
489                                        for( int i=0; i<args.length; i++ ) {
490                                                int type = pMeta.getParameterType( i+1 );
491                                                // 5.3.8.0 (2011/08/01) setNull 対応
492                                                String val = args[i];
493                                                if( val == null || val.isEmpty() ) {
494                                                        pstmt.setNull( i+1, type );
495                                                }
496                                                else {
497                                                        pstmt.setObject( i+1, val, type );
498                                                }
499                                        }
500                                }
501                                else {
502                                        for( int i=0; i<args.length; i++ ) {
503                                                pstmt.setObject( i+1,args[i] );
504                                        }
505                                }
506                        }
507
508                        resultSet = pstmt.executeQuery();
509                        if( resultSet.next() ) {
510                                rtnCnt = resultSet.getInt(1);
511                        }
512                }
513                catch ( SQLException ex ) {
514                        String errMsg = ex.getMessage() + ":" + ex.getSQLState() + CR
515                                                + "SQL=[" + stmt + "]" + CR
516                                                + "ARG=[" + StringUtil.array2csv( args ) + "]" + CR
517                                                + "DBID=[" + dbid + "]" + CR;
518                        throw new RuntimeException( errMsg,ex );                // 3.5.5.4 (2004/04/15) 引数の並び順変更
519                }
520                finally {
521                        Closer.resultClose( resultSet );
522                        Closer.stmtClose( pstmt );
523                }
524                return rtnCnt;
525        }
526
527        /**
528         * ResultSet より、結果の文字列配列を作成します。
529         *
530         * 結果は,すべて文字列に変換されて格納されます。
531         * 移動したメソッドで使われているのでこれも移動
532         *
533         * @og.rev 3.1.0.0 (2003/03/20) Vector を使用している箇所で、非同期でも構わない箇所を、ArrayList に置換え。
534         * @og.rev 3.8.0.8 (2005/10/03) エラーメッセージの出力順をメッセージ+Queryに変更します。
535         * @og.rev 4.0.0.0 (2005/01/31) private ⇒ public , ヘッダー情報の取得有無フラグの追加
536         * @og.rev 5.6.7.0 (2013/07/27) CLOB 対応
537         *
538         * @param   resultSet ResultSetオブジェクト
539         * @param   useHeader true:ヘッダーを第一行に含める/false:含めない
540         *
541         * @return  ResultSetの検索結果配列
542         */
543        public static String[][] resultToArray( final ResultSet resultSet,final boolean useHeader ) {
544                ArrayList<String[]> data = new ArrayList<String[]>();
545                try {
546                        ResultSetMetaData metaData  = resultSet.getMetaData();
547                        int numberOfColumns =  metaData.getColumnCount();
548
549                        String[] columnNames = new String[numberOfColumns];
550                        // 5.6.7.0 (2013/07/27) CLOB 対応 
551                        int[] type = new int[numberOfColumns];
552                        boolean useClob = false;                                                                                // そもそも、CLOB系のカラムがあるかどうか
553                        for( int i = 0; i < numberOfColumns; i++ ) {
554                                int tp = metaData.getColumnType( i+1 );
555                                type[i] = tp ;
556                                if( tp == Types.CLOB || tp == Types.ROWID || tp == Types.TIMESTAMP ) { useClob = true; }
557                                if( useHeader ) {
558                                        columnNames[i] = metaData.getColumnLabel(i+1).toUpperCase(Locale.JAPAN) ;
559                                }
560                        }
561
562                        if( useHeader ) { data.add( columnNames ); }
563
564                        // 5.6.7.0 (2013/07/27) CLOB 対応。ついでに、ループカウンタを、0からに変更します。
565                        while( resultSet.next() ) {
566                                String[] columnValues = new String[numberOfColumns];
567                                for( int i = 0; i < numberOfColumns; i++ ) {
568                                        Object obj = resultSet.getObject(i+1);
569                                        if( obj == null ) {
570                                                columnValues[i] = "";
571                                        }
572                                        else if( useClob ) {
573                                                columnValues[i] = getValue( resultSet, i ,type[i] );
574                                        }
575                                        else {
576                                                columnValues[i] = String.valueOf( obj );
577                                        }
578                                }
579                                data.add( columnValues );
580                        }
581                }
582                catch ( SQLException ex ) {
583                        String errMsg = "処理結果を実行できませんでした。"
584                                                + CR + ex.getMessage() ;
585                        throw new RuntimeException( errMsg,ex );                // 3.5.5.4 (2004/04/15) 引数の並び順変更
586                }
587
588                int size = data.size();
589                String[][] rtn = new String[size][];
590                for( int i=0; i<size; i++ ) {
591                        rtn[i] = data.get(i);
592                }
593
594                return rtn;
595        }
596
597        /**
598         * 検索結果オブジェクトから値を取り出します。
599         *
600         * @og.rev 5.3.6.0 (2011/06/01) 集計機能対応によりメソッド化
601         * @og.rev 5.5.5.4 (2012/08/18) if文をcase文に置き換え。
602         * @og.rev 5.5.5.4 (2012/08/18) TIMESTAMP の処理を追加。
603         * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します。
604         *
605         * @param res 検索結果オブジェクト
606         * @param col カラム(0から始まる値。このメソッドの内部で、+1しています)
607         * @param type データタイプ(java.sql.Types.XXXX)
608         *
609         * @return 値
610         * @throws SQLException データベースアクセスエラー
611         */
612        public static String getValue( final ResultSet res, final int col, final int type ) throws SQLException {
613                String val = null;
614
615                Object obj = res.getObject(col+1);
616                if( obj == null ) {
617                        val = "";
618                }
619                else {
620                        switch( type ) {
621                                case Types.CLOB :               val = getClobData( (Clob)obj ) ;        break;
622                                case Types.ROWID:               val = res.getString(col+1);                     break;
623                                case Types.TIMESTAMP :  val = HybsDateUtil.getDate( ((java.sql.Timestamp)obj).getTime() , "yyyyMMddHHmmss" );   break;
624                                default :                               val = String.valueOf( obj );
625                        }
626                }
627
628                return val;
629        }
630
631        /**
632         * コネクションオブジェクトからデータベースのProductNameを取り出します。
633         * ProductName は、小文字化して返します。
634         * また、処理エラーが発生した場合は、"none" を返します。
635         * ここでは、SQLException は、発生させません。
636         *
637         * @og.rev 5.6.7.0 (2013/07/27) 新規追加
638         * @og.rev 5.6.7.4 (2013/08/30) ProductNameの小文字化対応
639         *
640         * @param conn コネクションオブジェクト
641         *
642         * @return データベースのProductName
643         */
644        public static String getProductName( final Connection conn ) {
645                String dbName ;
646                try {
647                        dbName = conn.getMetaData().getDatabaseProductName().toLowerCase( Locale.JAPAN );       // 5.6.7.4 (2013/08/30)
648                }
649                catch( SQLException ex ) {
650                        dbName = "none";
651                }
652                return dbName ;
653        }
654
655        /**
656         * Clob オブジェクトから文字列を取り出します。
657         *
658         * @og.rev 5.3.6.0 (2011/06/01) 新規作成
659         *
660         * @param       clobData Clobオブジェクト
661         *
662         * @return      Clobオブジェクトから取り出した文字列
663         * @throws      SQLException データベースアクセスエラー
664         */
665        private static String getClobData( final Clob clobData ) throws SQLException {
666                if( clobData == null ) { return ""; }
667
668                Reader reader = null;
669                StringBuilder buf = new StringBuilder( 10000 );
670
671                try {
672                        reader = clobData.getCharacterStream();
673                        char[] ch = new char[10000];
674                        int  len ;
675                        while( (len = reader.read( ch )) >= 0 ) {
676                                buf.append( ch,0,len );
677                        }
678                }
679                catch( IOException ex ) {
680                        String errMsg = "CLOBデータの読み込みに失敗しました。";
681                        throw new RuntimeException( errMsg,ex );
682                }
683                finally {
684                        Closer.ioClose( reader );
685                }
686                return buf.toString();
687        }
688}