001/*
002 * Copyright (c) 2009 The openGion Project.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013 * either express or implied. See the License for the specific language
014 * governing permissions and limitations under the License.
015 */
016package org.opengion.hayabusa.db;
017
018import java.sql.Connection;
019import java.sql.ResultSet;
020import java.sql.SQLException;
021
022import org.opengion.fukurou.system.HybsConst ;                          // 6.1.0.0 (2014/12/26)
023import org.opengion.fukurou.system.ThrowUtil;                           // 6.4.2.0 (2016/01/29)
024import org.opengion.fukurou.util.ErrorMessage;
025import org.opengion.hayabusa.common.HybsSystem;
026import org.opengion.hayabusa.common.HybsSystemException;
027import org.opengion.hayabusa.resource.ResourceManager;
028
029/**
030 * Query インターフェースを継承した Query の実装クラスです。
031 * クエリークラスにステートメントを与えて execute()することにより内部に DBTableModel を
032 * 作成します。
033 * このクラスは、Abstract クラスのため、実装は個々のサブクラスで行います。
034 * 唯一実装する必要があるのは、execute() メソッドだけです。
035 *
036 * @og.group DB検索
037 * @og.group DB登録
038 *
039 * @version  4.0
040 * @author       Kazuhiko Hasegawa
041 * @since    JDK5.0,
042 */
043public class AbstractQuery implements Query {
044        /** システムの改行コードを設定します。*/
045        protected static final String CR                 = HybsConst.CR;                        // 6.1.0.0 (2014/12/26) refactoring
046        /** StringBilderなどの初期値を設定します。   {@value} */
047        protected static final int BUFFER_MIDDLE = HybsConst.BUFFER_MIDDLE;     // 6.1.0.0 (2014/12/26) refactoring
048
049        private Connection              connection      ;
050        private int                     rtnCode         = ErrorMessage.OK;
051        private ErrorMessage    errMessage      ;
052        private ResourceManager resource        ;
053
054        private DBTableModel table                      ;
055        private String           stmtString             ;
056        private int              executeCount   = -1 ;
057        private int              skipRowCount   ;
058        private int              maxRowCount    = HybsSystem.sysInt( "DB_MAX_ROW_COUNT" ) ;
059        private boolean          updateFlag             = true ;
060        private DBEditConfig config                     ;               // 5.3.6.0 (2011/06/01)
061
062        // 5.1.9.0 (2010/08/01) DB_RETRY_COUNT,DB_RETRY_TIME 廃止
063        /** データ検索時の最大処理制限時間 */
064        protected static final int DB_MAX_QUERY_TIMEOUT = HybsSystem.sysInt( "DB_MAX_QUERY_TIMEOUT" ) ;
065
066        /** 6.9.3.0 (2018/03/26) データ検索時のフェッチサイズ {@value} */
067        protected static final int DB_FETCH_SIZE                = HybsConst.DB_FETCH_SIZE;      // 6.9.4.1 (2018/04/09)
068
069//      /** データ検索時のフェッチサイズを設定します。 */
070//      protected static final int DB_FETCH_SIZE  = HybsConst.DB_FETCH_SIZE;    // 6.9.4.1 (2018/04/09)
071
072        // 3.5.2.0 (2003/10/20) 内部オブジェクトタイプ名を システムパラメータ で定義します。
073        /** 内部オブジェクトタイプ名  {@value} */
074        public static final String ARG_ARRAY            = "ARG_ARRAY" ;
075        /** 内部オブジェクトタイプ名  {@value} */
076        public static final String SYSARG_ARRAY         = "SYSARG_ARRAY" ;
077        /** 内部オブジェクトタイプ名  {@value} */
078        public static final String ERR_MSG                      = "ERR_MSG" ;
079        /** 内部オブジェクトタイプ名  {@value} */
080        public static final String ERR_MSG_ARRAY        = "ERR_MSG_ARRAY" ;
081
082        private String  updQuery                ;                       // 7.2.9.1 (2020/10/23)
083        private String  insQuery                ;                       // 7.2.9.1 (2020/10/23)
084        private String  selQuery                ;                       // 7.4.1.0 (2021/04/23)
085
086        /**
087         * デフォルトコンストラクター
088         *
089         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
090         */
091        protected AbstractQuery() { super(); }          // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
092
093        /**
094         * Connectionオブジェクトを外部から設定します。
095         *
096         * 通常は、Transaction と 接続先(DBID) を使用して作成した Connection を渡します。
097         * このクラスでは、Connection の close() や、ConnectionFactory への返却なども
098         * 行いません。それらは、外部処理(通常は、Transactionオブジェクト)で行います。
099         *
100         * Connection には、null は登録できません。
101         *
102         * @og.rev 6.3.6.1 (2015/08/28) 新規追加
103         *
104         * @param       conn    Connectionオブジェクト
105         */
106        @Override       // Query
107        public void setConnection( final Connection conn ) {
108                if( conn == null ) {
109                        final String errMsg = "Connection に null は指定できません。" + CR ;
110                        throw new HybsSystemException( errMsg );
111                }
112                connection = conn;
113        }
114
115        /**
116         * ステートメント文字列をセットします。
117         *
118         * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
119         *
120         * @param       stmt ステートメント文字列
121         *
122         */
123        @Override       // Query
124        public void setStatement( final String stmt ) {
125                this.stmtString = stmt.trim();
126        }
127
128        /**
129         * ステートメント文字列を取り出します。
130         *
131         * @return       ステートメント文字列
132         *
133         */
134        @Override       // Query
135        public String getStatement() {
136                return stmtString;
137        }
138
139        /**
140         * ステートメント文字列(UPDATE,INSERT)をセットします。
141         *
142         * @og.rev 7.2.9.1 (2020/10/23) TableUpdateParamTag のマージ(UPDATE,INSERT)対応
143         * @og.rev 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない
144         *
145         * @param   update UPDATEステートメント文字列
146         * @param   insert INSERTステートメント文字列
147         * @param   select SELECTステートメント文字列(あれば何もしない、なければINSERT処理の判定用)
148         */
149//      public void setMergeStatement( final String update , final String insert ) {
150        @Override       // Query
151        public void setMergeStatement( final String update , final String insert , final String select ) {
152                updQuery = update;
153                insQuery = insert;
154                selQuery = select;
155        }
156
157        /**
158         * ステートメント文字列(UPDATE,INSERT,SELECT)を取り出します。
159         *
160         * @og.rev 7.2.9.1 (2020/10/23) TableUpdateParamTag のマージ(UPDATE,INSERT)対応
161         * @og.rev 7.2.9.3 (2020/11/06) 早い段階でエラーにしておきます。
162         * @og.rev 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない
163         *
164         * @return  ステートメント文字列の配列(UPDATE,INSERTの順番)
165         */
166        @Override       // Query
167        public String[] getMergeStatement() {
168        //      if( updQuery == null || insQuery == null ) {
169        //              final String errMsg = "Merge処理を行うには、INSERTとUPDATEの両方のQUERYが必要です。" + CR
170        //                      + " updQuery=" + updQuery + CR
171        //                      + " insQuery=" + insQuery + CR
172        //                      + " query   =" + stmtString ;
173        //
174        //              throw new UnsupportedOperationException( errMsg );
175        //      }
176
177//              return new String[] { updQuery,insQuery } ;
178                return new String[] { updQuery,insQuery,selQuery } ;
179        }
180
181        /**
182         * 引数配列付のクエリーを実行します。
183         * 処理自体は、#execute() と同様に、各サブクラスの実装に依存します。
184         * これは、PreparedQuery で使用する引数を配列でセットするものです。
185         * select * from emp where deptno = ? and job = ? などの PreparedQuery や
186         * { call xxxx( ?,?,? ) } などの CallableStatement の ? 部分の引数を
187         * 順番にセットしていきます。
188         * ※ このクラスでは実装されていません。
189         *
190         * @og.rev 6.1.1.0 (2015/01/17) 引数配列を可変引数にして、execute() を含めて定義します。
191         *
192         * @param       args オブジェクトの引数配列(可変長引数)
193         */
194        @Override       // Query
195        public void execute( final String... args ) {                   // 6.1.1.0 (2015/01/17) refactoring
196                final String errMsg = "このクラスでは実装されていません。execute( String... )";
197                throw new UnsupportedOperationException( errMsg );
198        }
199
200        /**
201         * 引数配列付のクエリーを実行します。
202         * 処理自体は、#execute() と同様に、各サブクラスの実装に依存します。
203         * これは、PreparedQuery で使用する引数を配列でセットするものです。
204         * select * from emp where deptno = ? and job = ? などの PreparedQuery の
205         * ? 部分の引数を
206         * 順番にセットしていきます。
207         * ※ このクラスでは実装されていません。
208         *
209         * @og.rev 4.0.0.0 (2005/01/31) 新規追加
210         *
211         * @param   keys オブジェクトのキー配列
212         * @param   args オブジェクトの引数配列(可変長引数)
213         */
214        @Override       // Query
215        public void execute( final String[] keys, final String... args ) {                      // 6.1.1.0 (2015/01/17) refactoring
216                final String errMsg = "このクラスでは実装されていません。execute( String[],String... )";
217                throw new UnsupportedOperationException( errMsg );
218        }
219
220        /**
221         * 引数配列付のクエリーを実行します。
222         * 処理自体は、#execute() と同様に、各サブクラスの実装に依存します。
223         * これは、PreparedQuery で使用する引数を配列でセットするものです。
224         * select * from emp where deptno = ? and job = ? などの PreparedQuery の
225         * ? 部分の引数を
226         * 順番にセットしていきます。
227         * ※ このクラスでは実装されていません。
228         *
229         * @og.rev 4.0.0.0 (2005/01/31) 引数をすべて受け取って実行するメソッドを標準メソッドとして追加
230         *
231         * @param       names           カラム名(CSV形式)
232         * @param       dbArrayType     アレイタイプ名称
233         * @param       sysArg          DBSysArg配列
234         * @param       userArg         DBUserArg配列
235         */
236        @Override       // Query
237        public void execute( final String names,final String dbArrayType,
238                                                final DBSysArg[] sysArg,final DBUserArg[] userArg ) {
239                final String errMsg = "このクラスでは実装されていません。execute( String,String,DBSysArg[],DBUserArg[] )";
240                throw new UnsupportedOperationException( errMsg );
241        }
242
243        /**
244         * 引数配列付のクエリーを実行します。
245         * 処理自体は、#execute() と同様に、各サブクラスの実装に依存します。
246         * これは、PreparedQuery で使用する引数を配列でセットするものです。
247         * select * from emp where deptno = ? and job = ? などの PreparedQuery の
248         * [カラム名] 部分の引数を、DBTableModelから順番にセットしていきます。
249         * ※ このクラスでは実装されていません。
250         *
251         * @param   rowNo 選択された行番号配列(登録する対象行)
252         * @param   table DBTableModelオブジェクト(登録する元データ)
253         */
254        @Override       // Query
255        public void execute( final int[] rowNo, final DBTableModel table ) {
256                final String errMsg = "このクラスでは実装されていません。execute( final int[] rowNo, final DBTableModel table )";
257                throw new UnsupportedOperationException( errMsg );
258        }
259
260        /**
261         * クエリーの実行結果件数をセットします。
262         * 初期値は -1 です。(クエリーが失敗した場合や、CallableStatement の呼び出し等で
263         * 実行件数が明確でない場合の戻り値)。
264         *
265         * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
266         *
267         * @param       executeCount 実行結果件数
268         */
269        protected void setExecuteCount( final int executeCount ) {
270                this.executeCount = executeCount;
271        }
272
273        /**
274         * クエリーの実行結果を返します。
275         * クエリーが失敗した場合や、CallableStatement の呼び出し等で実行件数が明確でない
276         * 場合は、-1 が返されます。
277         *
278         * @return      実行結果件数
279         */
280        @Override       // Query
281        public int getExecuteCount() {
282                return executeCount;
283        }
284
285        /**
286         * DBTableModel をセットします。
287         * なお、検索系実行前に setDBTableModel() でテーブルをセットしていたとしても
288         * そのオブジェクトは破棄されて、新しい DBTableModel が生成されます。
289         *
290         * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
291         *
292         * @param       table DBTableModelオブジェクト
293         */
294        protected void setDBTableModel( final DBTableModel table ) {
295                this.table = table;
296        }
297
298        /**
299         * 実行結果の DBTableModel を返します。
300         *
301         * @return      DBTableModelオブジェクト
302         */
303        @Override       // Query
304        public DBTableModel getDBTableModel() {
305                return table;
306        }
307
308        /**
309         * データベースの最大検索件数を返します。
310         *              (初期値:DB_MAX_ROW_COUNT[={@og.value SystemData#DB_MAX_ROW_COUNT}])。
311         * データベース自体の検索は、指定されたSQLの全件を検索しますが、
312         * DBTableModelのデータとして登録する最大件数をこの値に設定します。0は無制限です。
313         * サーバーのメモリ資源と応答時間の確保の為です。
314         *
315         * @return      最大検索件数
316         */
317        @Override       // Query
318        public int getMaxRowCount() {
319                return maxRowCount;
320        }
321
322        /**
323         * データベースの最大検索件数をセットします。
324         * データベース自体の検索は、指定されたSQLの全件を検索しますが、
325         * DBTableModelのデータとして登録する最大件数をこの値に設定します。
326         * サーバーのメモリ資源と応答時間の確保の為です。
327         * ゼロ、または、負の値を設定すると、無制限(Integer.MAX_VALUE)になります。
328         *
329         * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
330         * @og.rev 4.0.0.0 (2005/08/31) ゼロ、または、負の値は、無制限(Integer.MAX_VALUE)にする。
331         *
332         * @param       maxRowCount 最大検索件数
333         */
334        @Override       // Query
335        public void setMaxRowCount( final int maxRowCount ) {
336                this.maxRowCount = maxRowCount > 0 ? maxRowCount : Integer.MAX_VALUE ;
337        }
338
339        /**
340         * データベースの検索スキップ件数を返します。
341         * データベース自体の検索は、指定されたSQLの全件を検索しますが、
342         * DBTableModelのデータとしては、スキップ件数分は登録されません。
343         * サーバーのメモリ資源と応答時間の確保の為です。
344         *
345         * @return      最大検索件数
346         */
347        @Override       // Query
348        public int getSkipRowCount() {
349                return skipRowCount;
350        }
351
352        /**
353         * データベースの検索スキップ件数をセットします。
354         * データベース自体の検索は、指定されたSQLの全件を検索しますが、
355         * DBTableModelのデータとしては、スキップ件数分は登録されません。
356         * サーバーのメモリ資源と応答時間の確保の為です。
357         *
358         * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
359         *
360         * @param       skipRowCount スキップ件数
361         */
362        @Override       // Query
363        public void setSkipRowCount( final int skipRowCount ) {
364                this.skipRowCount = skipRowCount;
365        }
366
367        /**
368         * アップデートフラグをセットします。
369         * これは、Query で更新処理の SQL 文を実行したときにセットされます。
370         * 更新処理が実行:true / 検索処理のみ:false をセットします。
371         * このメソッドを呼び出さない場合は、デフォルト:true  です。
372         *
373         * @og.rev 2.1.2.3 (2002/12/02) データベース更新時に、更新フラグをセットするように変更
374         * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
375         *
376         * @param       up      アップデートされたかどうか[true:更新処理/false:検索処理]
377         */
378        protected void setUpdateFlag( final boolean up ) {
379                updateFlag = up;
380        }
381
382        /**
383         * アップデートフラグを取得します。
384         * これは、Query で更新処理の SQL 文を実行したときに true にセットされます。
385         * 更新処理が実行:true / 検索処理のみ:false を取得できます。
386         *
387         * @og.rev 2.1.2.3 (2002/12/02) データベース更新時に、更新フラグをセットするように変更
388         * @og.rev 4.0.0.0 (2007/07/20) メソッド名変更( getUpdateFlag() ⇒ isUpdate() )
389         *
390         * @return       アップデートされたかどうか[true:更新処理/false:検索処理]
391         */
392        @Override       // Query
393        public boolean isUpdate() {
394                return updateFlag ;
395        }
396
397        /**
398         * リソースマネージャーをセットします。
399         * これは、言語(ロケール)に応じた DBColumn をあらかじめ設定しておく為に
400         * 必要です。
401         * リソースマネージャーが設定されていない、または、所定のキーの DBColumn が
402         * リソースに存在しない場合は、内部で DBColumn オブジェクトを作成します。
403         *
404         * @og.rev 4.0.0.0 (2005/01/31) lang ⇒ ResourceManager へ変更
405         *
406         * @param       resource リソースマネージャー
407         */
408        @Override       // Query
409        public void setResourceManager( final ResourceManager resource ) {
410                this.resource = resource;
411        }
412
413        /**
414         * エラーコード を取得します。
415         * エラーコード は、ErrorMessage クラスで規定されているコードです。
416         *
417         * @return   エラーコード
418         */
419        @Override       // Query
420        public int getErrorCode() {
421                return rtnCode;
422        }
423
424        /**
425         * エラーコード をセットします。
426         * エラーコード は、ErrorMessage クラスで規定されているコードです。
427         *
428         * @param   cd エラーコード
429         */
430        protected void setErrorCode( final int cd ) {
431                rtnCode = cd;
432        }
433
434        /**
435         * エラーメッセージオブジェクト を取得します。
436         *
437         * @return   エラーメッセージオブジェクト
438         */
439        @Override       // Query
440        public ErrorMessage getErrorMessage() {
441                return errMessage;
442        }
443
444        /**
445         * エラーメッセージオブジェクト をセットします。
446         *
447         * @param   em エラーメッセージオブジェクト
448         */
449        protected void setErrorMessage( final ErrorMessage em ) {
450                errMessage = em;
451        }
452
453        /**
454         * 編集設定オブジェクトをセットします。
455         *
456         * @og.rev 5.3.6.0 (2011/06/01) 新規追加
457         *
458         * @param config 編集設定オブジェクト
459         */
460        @Override       // Query
461        public void setEditConfig( final DBEditConfig config ) {
462                this.config = config;
463        }
464
465        /**
466         * 編集設定オブジェクトを取得します。
467         *
468         * @og.rev 5.3.6.0 (2011/06/01) 新規追加
469         *
470         * @return 編集設定オブジェクト
471         */
472        protected DBEditConfig getEditConfig() {
473                return config;
474        }
475
476        //////////////////////////////////////////////////////////////////////////
477        //
478        //       継承時にサブクラスから使用するメソッド類( protected )
479        //
480        //////////////////////////////////////////////////////////////////////////
481
482        /**
483         * ResultSet を DBTableModelに割り当てます。
484         *
485         * 毎回、検索毎に DBTableModel にコピーするイメージです。
486         * ResulSet 以外のオブジェクトから、DBTableModelを作成する場合は、
487         * このメソッドをオーバーライドします。
488         *
489         * このメソッドは、execute からのみ呼び出されます。
490         * それ以外からは呼出し出来ません。
491         *
492         * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
493         * @og.rev 3.3.3.3 (2003/08/06) カラムのラベル名を、大文字に変換する。
494         * @og.rev 3.8.5.0 (2006/03/02) CLOB カラムかどうかを判定しCLOBの場合は、Clob オブジェクトから文字列を取り出します。
495         * @og.rev 3.8.8.8 (2007/05/11) ROWID対応(小数点対応 "0.3" が ".3" と表示される対策)
496         * @og.rev 4.0.0.0 (2006/01/31) CLOB カラムかどうかを判定しCLOBの場合は、ストリームから値を取り出します。
497         * @og.rev 5.3.6.0 (2011/06/01) DBTableModel作成処理をDBTableModelUtilに移動&集計機能対応
498         * @og.rev 6.3.6.1 (2015/08/28) close(),realClose() 廃止。Queryはキャッシュしません。
499         *
500         * @param       resultSet ResultSetオブジェクト
501         */
502        protected void createTableModel( final ResultSet resultSet ) {
503                try {
504                        if( config == null ) {
505                                table = DBTableModelUtil.makeDBTable( resultSet, getSkipRowCount(), maxRowCount, resource );
506                        }
507                        else {
508                                table = DBTableModelUtil.makeEditDBTable( resultSet, getSkipRowCount(), maxRowCount, resource, config );
509                        }
510
511                        setExecuteCount( table.getRowCount() );
512                }
513                catch( final SQLException ex ) {
514                        final String errMsg = "テーブルモデルを作成できませんでした。";
515                        throw new HybsSystemException( errMsg,ex );             // 3.5.5.4 (2004/04/15) 引数の並び順変更
516                }
517        }
518
519        /**
520         * ConnectionFactory.connection( String ); を利用して、Connection
521         * オブジェクトを取り出します。
522         *
523         * コネクションプールが一杯の場合は、即エラーになります。
524         *
525         * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
526         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為、ApplicationInfoオブジェクトを設定
527         * @og.rev 5.1.9.0 (2010/08/01) transaction 属性追加。
528         * @og.rev 6.3.6.1 (2015/08/28) transaction 属性廃止。内部のConnectionを返します。
529         *
530         * @return      コネクション
531         */
532        protected Connection getConnection() {
533                return connection;
534        }
535
536        /**
537         * この接続が、PreparedStatement#getParameterMetaData() を使用するかどうかを判定します。
538         *
539         * ConnectionFactory#useParameterMetaData(String) の結果を返します。(postgreSQL対応)
540         *
541         * ※ 暫定処理です。もっと、良い方法を考える必要があります。
542         *
543         * @og.rev 5.3.8.0 (2011/08/01) 新規追加
544         * @og.rev 6.3.6.1 (2015/08/28) 内部変数にconnIDが無くなったため、直接所得することになりました。
545         * @og.rev 6.4.2.0 (2016/01/29) ex.printStackTrace() を、ThrowUtil#ogStackTrace(Throwable) に置き換え。
546         *
547         * @return      使用する場合:true / その他:false
548         * @see org.opengion.fukurou.db.ConnectionFactory#useParameterMetaData(String)
549         */
550        protected boolean useParameterMetaData() {
551        //      return ConnectionFactory.useParameterMetaData( connID );
552                try {
553                        return "PostgreSQL".equalsIgnoreCase( connection.getMetaData().getDatabaseProductName() );
554                }
555                catch( final Throwable th ) {
556                        System.err.println( ThrowUtil.ogStackTrace( th ) );                             // 6.4.2.0 (2016/01/29)
557                }
558                return false ;
559        }
560
561        //////////////////////////////////////////////////////////////////////////
562        //
563        //       Object クラスのオーバーライド部分
564        //
565        //////////////////////////////////////////////////////////////////////////
566
567        /**
568         * オブジェクトの識別子として、最後のクエリーを返します。
569         *
570         * @return      最後のクエリー
571         * @og.rtnNotNull
572         */
573        @Override
574        public String toString() {
575                return  "LastQuery  :[" + getStatement() + "] ";
576        }
577}