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