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