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.taglib; 017 018// import java.util.Locale; 019import java.util.Set; 020import java.sql.Connection; 021import java.sql.ResultSet; 022import java.sql.Statement; 023import java.sql.PreparedStatement; 024import java.sql.SQLException; 025import java.sql.ParameterMetaData; 026 027import org.opengion.hayabusa.common.HybsSystem; 028import org.opengion.hayabusa.common.HybsSystemException; 029import org.opengion.hayabusa.resource.GUIInfo; 030import org.opengion.fukurou.system.Closer ; 031import org.opengion.fukurou.util.ErrorMessage; 032import org.opengion.fukurou.util.StringUtil; 033import org.opengion.fukurou.util.ArraySet; 034import org.opengion.fukurou.db.Transaction; 035import org.opengion.fukurou.db.QueryMaker; 036import org.opengion.fukurou.db.ResultSetValue; 037import org.opengion.fukurou.db.ConnectionFactory; 038 039import static org.opengion.fukurou.util.StringUtil.nval; 040import static org.opengion.fukurou.system.HybsConst.DB_FETCH_SIZE; // 6.9.4.1 (2018/04/09) 041 042/** 043 * データベースのデータコピー/移動/更新/削除を行うタグです。 044 * 045 * <pre> 046 * 検索結果のデータを、action に応じた方法で、処理します。 047 * SELECT文は、BODY部に記述することも可能です。 048 * BODY にSELECT文を記述しない場合は、names と、table から、SELECT文を作成します。 049 * names2 は、INSERTやUPDATE の カラム名で、SELECT文の先頭から順に適用します。 050 * WHERE条件は、SELECT結果を利用できますが、必ず、names2 のカラムか、そうでないならば、 051 * それ以降に記述してください。 052 * 053 * このタグは、DBTableModel を経由せず、直接、接続元から接続先へデータ処理を行います。 054 * 接続元の1レコード単位に、接続先に対して、処理を実行します。 055 * よって、大量データ処理が可能ですが、まとめ処理を行っていない分、時間が掛かります。 056 * 057 * 用途としては、WORKテーブルへのデータコピーや、BKUPテーブルへのコピーが考えられ 058 * ますが、それらは、select insert などの直接的な処理のほうが良いです。 059 * ここでは、別ユーザーや、別インスタンス、または、別データベース(ORACLEから、MySQLへ)など、 060 * dbid違いのテーブルへのデータ処理用途を、想定しています。 061 * なので、複雑な処理や、PL/SQL等のデータベース独自処理は行えません。 062 * SELECT文は、直接記述できるため、データベース固有の関数や、構文を記載可能ですが、 063 * INSERT,UPDATE,DELETE 文は、基本的に共通構文であり、WHERE条件等も、一般的は範囲に 064 * とどめてください。 065 * 066 * SELECTカラムとINSERTカラムが異なる場合は、name 指定と、name2 指定のカラムが対応します。 067 * 追加、更新先のカラム名に変更して置いてください。 068 * BODY部にSELECT文を記述した場合は、カラム順が、name 順となり、name2 と対応されます。 069 * constKeys,constVals も、更新先のカラム名で指定します。 070 * 処理の途中でエラー(例えば、ユニークキー制約等)になった場合は、stopError属性の 071 * 値に応じて処理を継続するかどうかを決定します。 072 * stopError="true" が初期値なので、エラー時点で、処理を中断します。 073 * 074 * action="INSERT" 075 * SELECT結果を、table2 に、INSERT します。where2,whereNames2 は使用しません。 076 * name2 を使用しない場合は、name と同じカラム配列で、INSERT します。 077 * stopError="false"(エラー時も継続する) とした場合、SELECT結果は、最後まで 078 * INSERTを試みます。 079 * 080 * action="UPDATE" 081 * SELECT結果を、table2 に、where2,whereNames2 に基づいて UPDATE します。 082 * SELECTには、更新で使用する where条件となるカラムを含める必要があります。 083 * 更新するカラムは、name2 で指定することになります。 084 * 更新対象が存在しなかった場合は、エラーとは判定していません。 085 * 086 * action="DELETE" 087 * SELECT結果を、table2 に、where2,whereNames2 に基づいて table2 のデータを 削除 します。 088 * SELECTには、削除で使用する where条件となるカラムを含める必要があります。 089 * 削除対象が存在しなかった場合は、エラーとは判定していません。 090 * 091 * action="MERGE" 092 * SELECT結果を、table2 に、where2,whereNames2 に基づいて UPDATE/INSERT します。 093 * SELECTには、更新で使用する where条件となるカラムを含める必要があります。 094 * 更新するカラムは、name2 で指定することになります。 095 * 更新対象が存在しなかった場合は、INSERT になります。 096 * (つまり、更新を一度試みて、更新件数が、0件の場合に、INSERTします。) 097 * INSERTするカラムは、SELECTしたすべてのカラムが対象になります。 098 * 099 * useDelete="true" を指定すると、検索元のデータを削除します。 100 * INSERT 時に指定すれば、MOVE と同じ効果になります。 101 * stopError="false" (エラー時でも処理を継続する)にした場合、検索元のデータ削除は、 102 * エラー行については、実行されません。ただし、UPDATE,DELETE 等で、対象データが 103 * 存在しない場合は、エラーと判断しないため、検索元のデータを削除します。 104 * 105 * SystemData の USE_SQL_INJECTION_CHECK が true か、quotCheck 属性が true の場合は、 106 * SQLインジェクション対策用のシングルクォートチェックを行います。リクエスト引数に 107 * シングルクォート(')が含まれると、エラーになります。 108 * 109 * DBLastSql はセットされません。 110 * つまり、このタグでSELECTされたデータを、ファイル出力することはできません。 111 * 112 * 実行後にリクエストパラメータに以下の値がセットされます。 113 * DB.COUNT : 検索結果の件数 114 * DB.UPCOUNT : 追加/更新/削除結果の件数 115 * DB.ERR_CODE : 検索結果のエラーコード(複数合った場合は、最後のエラーコード) 116 * 117 * ※ このタグは、Transaction タグの対象です。 118 * 119 * @og.formSample 120 * ●形式: 121 * ・<og:dbCopy action="INSERT" table="TEST_A" table2="TEST_B" /> 122 * TEST_A のすべてカラム、データを、TEST_B にコピーします。 123 * 124 * ・<og:dbCopy action="UPDATE" names2="A2,B2" table2="TEST_B" where2="C2=[c1]" > 125 * select a1,b1,c1 from TEST_A where d1='XXX' order by a1 126 * </og:dbCopy> 127 * TEST_A のa1→A2 , b1→B2 カラムに、WHERE条件 TEST_B.C2 が、TEST_A.c1 に一致するデータのみ 更新します。 128 * 129 * ●body:あり(EVAL_BODY_BUFFERED:BODYを評価し、{@XXXX} を解析します) 130 * 131 * ●Tag定義: 132 * <og:dbCopy 133 * action 【TAG】実行方法[INSERT/UPDATE/DELETE/MERGE]を指定します(初期値:INSERT)。 134 * useDelete 【TAG】(jdbcオプション)検索した元のデータを削除するかどうか[true:削除する/false:なにもしない]を指定します(初期値:false)。 135 * maxRowCount 【TAG】(通常は使いません)データの最大読み込み件数を指定します (初期値:0:[無制限]) 136 * stopZero 【TAG】検索結果が0件のとき処理を続行するかどうか[true/false]を指定します(初期値:false[続行する]) 137 * dbid 【TAG】検索する対象のDB接続IDを指定します(初期値:null) 138 * table 【TAG】検索する対象のテーブル名を指定します 139 * names 【TAG】検索する対象のカラム名をCSV形式で複数指定します(初期値:*) 140 * where 【TAG】検索する対象を特定するキー条件(where句)を指定します 141 * orderBy 【TAG】検索する対象の検索順(order by句)を指定します 142 * dbid2 【TAG】登録する対象のDB接続IDを指定します(初期値:null) 143 * table2 【TAG】登録する対象のテーブル名を指定します 144 * names2 【TAG】登録する対象のカラム名をCSV形式で複数指定します 145 * omitNames2 【TAG】登録する対象外のカラム名をCSV形式で複数指定します 146 * where2 【TAG】登録する対象を特定するキー条件(where句)を指定します 147 * whereNames2 【TAG】登録する対象を特定するキー条件(where句)をCSV形式で複数指定します 148 * constKeys2 【TAG】設定値を固定値と置き換える対象となるカラム名をCSV形式で複数指定します 149 * constVals2 【TAG】設定値を固定値と置き換える対象となる設定値をCSV形式で複数指定します 150 * quotCheck 【TAG】リクエスト情報の シングルクォート(') 存在チェックを実施するかどうか[true/false]を設定します (初期値:USE_SQL_INJECTION_CHECK[=true]) 151 * stopError 【TAG】登録処理エラーの時に処理を中止するかどうか[true/false]を設定します(初期値:true) 152 * dispError 【TAG】エラー時にメッセージを表示するか[true/false]を設定します。通常はstopErrorと併用(初期値:true) 153 * fetchSize 【TAG】(通常は使いません)データのフェッチサイズを指定します(初期値:DB_FETCH_SIZE[={@og.value org.opengion.fukurou.system.HybsConst#DB_FETCH_SIZE}]) 154 * caseKey 【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null) 155 * caseVal 【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null) 156 * caseNN 【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない) 157 * caseNull 【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない) 158 * caseIf 【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない) 159 * debug 【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false) 160 * > ... Body ... 161 * </og:dbCopy> 162 * 163 * ●使用例 164 * ・<og:dbCopy action="INSERT" names2="A2,B2,C2" table2="TEST_B" > 165 * select a1,b1,c1 from TEST_A where d1='XXX' order by a1 166 * </og:dbCopy> 167 * TEST_A のa1→A2 , b1→B2 , c1→C2 カラムに、追加します。 168 * 169 * ・<og:dbCopy action="INSERT" names="a1,b1,c1" table="TEST_A" names2="A2,B2,C2" table2="TEST_B" /> 170 * TEST_A のa1→A2 , b1→B2 , c1→C2 カラムに、追加します。 (先の例と同じ処理) 171 * 172 * ・<og:dbCopy action="INSERT" table="TEST_A" where="d1='1'" dbid="LOCAL" dbid2="OTHER" > 173 * 接続先:LOCAL の TEST_A の 全カラムのd1='1' のレコードを、接続先:OTHER のTEST_A に追加します。 174 * 接続先違い(ユーザー、やデータベース違い)へのINSERTです。 175 * table2 を指定しない場合は、table と同じとみなされます。 176 * 177 * ・<og:dbCopy action="INSERT" table="TEST_A" where="d1='1'" dbid="LOCAL" dbid2="OTHER" stopError="false" useDelete="true" > 178 * 接続先:LOCAL の TEST_A の 全カラムのd1='1' のレコードを、接続先:OTHER のTEST_A に移動します。 179 * 接続先違い(ユーザー、やデータベース違い)への移動です。 180 * 先のINSERT が成功したレコードは削除され、最後まで処理が行われます。 181 * INSERTが失敗(つまり、接続先:OTHER にすでに、ユニークレコードが存在する場合など)時の、検索元のレコードは 182 * 削除されません。 183 * 184 * ・<og:dbCopy action="MERGE" table="TEST_A" where="d1='1'" dbid="LOCAL" names2="a1,b1,c1" dbid2="OTHER" where="ukey=[ukey]" stopError="false" useDelete="true" > 185 * 接続先:LOCAL の TEST_A の 全カラムのd1='1' のレコードを、接続先:OTHER のTEST_A に移動します。 186 * 接続先:OTHER に、移動先.ukey=[移動元ukey] のデータがあれば、name2="a1,b1,c1" カラムだけ、UPDATE を行い、 187 * 更新件数が、0件の場合は、検索したすべてのカラムで、INSERT を行います。 188 * </pre> 189 * 190 * @og.group DB検索 191 * @og.group DB登録 192 * 193 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 194 * 195 * @version 6.8.6.0 (2018/01/19) 196 * @author Kazuhiko Hasegawa 197 * @since JDK8.0, 198 */ 199public class DBCopyTag extends CommonTagSupport { 200 /** このプログラムのVERSION文字列を設定します。 {@value} */ 201 private static final String VERSION = "6.9.9.1 (2018/08/27)" ; 202 private static final long serialVersionUID = 699120180827L ; 203 204 /** action 引数に渡す事の出来る アクションコマンド 追加する {@value} */ 205 public static final String ACT_INSERT = "INSERT" ; 206 /** action 引数に渡す事の出来る アクションコマンド 更新する {@value} */ 207 public static final String ACT_UPDATE = "UPDATE" ; 208 /** action 引数に渡す事の出来る アクションコマンド 削除する {@value} */ 209 public static final String ACT_DELETE = "DELETE" ; 210 /** action 引数に渡す事の出来る アクションコマンド マージする {@value} */ 211 public static final String ACT_MERGE = "MERGE" ; 212 213// /** 6.9.3.0 (2018/03/26) データ検索時のフェッチサイズ {@value} */ 214// private static final int DB_FETCH_SIZE = HybsSystem.sysInt( "DB_FETCH_SIZE" ) ; 215 216// /** fetchSize の初期値 {@value} */ 217// public static final int FETCH_SIZE = 1000 ; // 6.9.3.0 (2018/03/26) 初期値を100→1000 に変更 218 219 private static final Set<String> ACTION_SET = new ArraySet<>( ACT_INSERT , ACT_UPDATE , ACT_DELETE , ACT_MERGE ); 220 221 /** エラーメッセージID {@value} */ 222 private static final String ERR_MSG_ID = HybsSystem.ERR_MSG_KEY; 223 224 // 6.9.8.0 (2018/05/28) FindBugs:直列化可能クラスの非 transient で非直列化可能なインスタンスフィールド 225 private transient QueryMaker query = new QueryMaker(); // 検索元のSELECTのSQL文 226 private transient QueryMaker query2 = new QueryMaker(); // 登録先のSQL文 227 228 private transient ErrorMessage errMessage ; 229 230 private String action = ACT_INSERT; // 実行方法[INSERT/UPDATE/DELETE/MERGE]を指定します(初期値:INSERT)。 231 private boolean useDelete ; // (jdbcオプション)検索した元のデータを削除するかどうか[true:削除する/false:なにもしない]を指定します(初期値:false)。 232 private int maxRowCount ; // データの最大読み込み件数を指定します (初期値:0:[無制限]) 233// private String displayMsg = HybsSystem.sys( "VIEW_DISPLAY_MSG" ); // 検索結果を画面上に表示するメッセージリソースIDを指定します (初期値:VIEW_DISPLAY_MSG[=]) 234// private String overflowMsg = "MSG0007"; // 検索結果が、制限行数を超えましたので、残りはカットされました。 235// private String notfoundMsg = "MSG0077"; // 対象データはありませんでした。 236 private boolean stopZero ; // 検索結果が0件のとき処理を続行するかどうか[true/false]を指定します(初期値:false[続行する]) 237 private String dbid ; // 検索する対象のDB接続IDを指定します(初期値:null) 238 private String dbid2 ; // 登録する対象のDB接続IDを指定します(初期値:null) 239 private boolean quotCheck = HybsSystem.sysBool( "USE_SQL_INJECTION_CHECK" ); // シングルクォート(') 存在チェックを実施するかどうか[true/false] 240 // (初期値:USE_SQL_INJECTION_CHECK[=true]) 241 private boolean stopError = true; // 登録処理エラーの時に処理を中止するかどうか[true/false]を設定します(初期値:true) 242 private boolean dispError = true; // エラー時にメッセージを表示するか[true/false]を設定します。通常はstopErrorと併用(初期値:true) 243 private int fetchSize = DB_FETCH_SIZE ; // フェッチする行数(初期値を100→HybsConst.DB_FETCH_SIZE に変更) 244 245// private int errCode = ErrorMessage.OK; // 処理結果のエラーコード(複数合った場合は、最後のエラーコード) 246 private long dyStart ; // 実行時間測定用のDIV要素を出力します。 247 248 private String selSQL ; // 検索元のSELECT文を一時保管する変数。 249 private int selCnt ; // DB.COUNT : 検索結果の件数 250 private int upCnt ; // DB.UPCOUNT : 追加/更新/削除結果の件数 251 252 /** 253 * デフォルトコンストラクター 254 * 255 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 256 */ 257 public DBCopyTag() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 258 259 /** 260 * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。 261 * 262 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 263 * 264 * @return 後続処理の指示 265 */ 266 @Override 267 public int doStartTag() { 268 useXssCheck( false ); // XSS対策:チェックしません。 269 270 if( useTag() && check( action, ACTION_SET ) ) { 271 dyStart = System.currentTimeMillis(); 272 // removeSessionAttribute( ERR_MSG_ID ); // 先に、エラーデータを削除しておきます。 273 274 errMessage = new ErrorMessage( "DBCopyTag Database Error!" ); 275 setSessionAttribute( ERR_MSG_ID,errMessage ); 276 277 query.setQueryType( "SELECT" ); // 検索元のQUERYタイプ(SELECT固定) 278 query2.setQueryType( action ); // 登録先のQUERYタイプ(actionと同じ) 279 280 // 初期設定 281 // table2 を指定しない場合は、table と同じテーブルが使用されます。 282 if( StringUtil.isNull( query2.getTable() ) ) { query2.setTable( query.getTable() ); } 283 284 // names2を、指定しない場合は、names または、SELECT文のすべてのカラムが、同一名として処理されます。 285 if( StringUtil.isNull( query2.getNames() ) ) { query2.setNames( query.getNames() ); } 286 287 return EVAL_BODY_BUFFERED ; // Body を評価する。( extends BodyTagSupport 時) 288 } 289 return SKIP_BODY ; // Body を評価しない 290 } 291 292 /** 293 * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。 294 * 295 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 296 * 297 * @return 後続処理の指示(SKIP_BODY) 298 */ 299 @Override 300 public int doAfterBody() { 301 debugPrint(); 302 303 useQuotCheck( quotCheck ); // SQLインジェクション対策 304 305 selSQL = getBodyString(); 306 307 return SKIP_BODY ; 308 } 309 310 /** 311 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。 312 * 313 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 314 * 315 * @return 後続処理の指示 316 */ 317 @Override 318 public int doEndTag() { 319 debugPrint(); 320 321 if( useTag() && check( action, ACTION_SET ) ) { 322 if( StringUtil.isNull( selSQL ) ) { 323 selSQL = query.getSelectSQL(); // この段階で、SELECT文の整合性チェックが行われます。 324 } 325 326 execute(); // 実際の処理 327 328 final int errCode = errMessage.getKekka(); 329 330 setRequestAttribute( "DB.COUNT" , String.valueOf( selCnt ) ); // DB.COUNT : 検索結果の件数 331 setRequestAttribute( "DB.UPCOUNT" , String.valueOf( upCnt ) ); // DB.UPCOUNT : 追加/更新/削除結果の件数 332 setRequestAttribute( "DB.ERR_CODE" , String.valueOf( errCode ) ); // 検索結果のエラーコード(複数合った場合は、最後のエラーコード) 333 334 final int rtnCode ; 335 if( errCode >= ErrorMessage.NG ) { // 異常 336 setSessionAttribute( ERR_MSG_ID,errMessage ); 337 338 final String err = TaglibUtil.makeHTMLErrorTable( errMessage,getResource() ); 339 // エラーメッセージをリクエスト変数で持つようにしておく 340 setRequestAttribute( "DB.ERR_MSG", err ); 341 342 if( dispError ) { jspPrint( err ); } // dispErrorで表示をコントロール 343 344 rtnCode = stopError ? SKIP_PAGE : EVAL_PAGE ; 345 } 346 else { 347 removeSessionAttribute( ERR_MSG_ID ); // 問題なければ、エラーデータを削除しておきます。 348 // 件数0件かつ stopZero = true 349 rtnCode = selCnt == 0 && stopZero ? SKIP_PAGE : EVAL_PAGE ; 350 } 351 return rtnCode ; 352 } 353 354 return EVAL_PAGE ; 355 } 356 357 /** 358 * タグリブオブジェクトをリリースします。 359 * キャッシュされて再利用されるので、フィールドの初期設定を行います。 360 * 361 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 362 * @og.rev 6.9.1.0 (2018/02/26) displayMsg,overflowMsg,notfoundMsg は未使用のため、削除 363 * 364 */ 365 @Override 366 protected void release2() { 367 super.release2(); 368 errMessage = null; 369 selSQL = null; // 検索SELECT文 370 action = ACT_INSERT; // 実行方法[INSERT/UPDATE/DELETE/MERGE]を指定します(初期値:INSERT)。 371 useDelete = false; // (jdbcオプション)検索した元のデータを削除するかどうか[true:削除する/false:なにもしない]を指定します(初期値:false)。 372 maxRowCount = 0; // データの最大読み込み件数を指定します (初期値:0:[無制限]) 373// displayMsg = HybsSystem.sys( "VIEW_DISPLAY_MSG" ); // 検索結果を画面上に表示するメッセージリソースIDを指定します (初期値:VIEW_DISPLAY_MSG[=]) 374// overflowMsg = "MSG0007"; // 検索結果が、制限行数を超えましたので、残りはカットされました。 375// notfoundMsg = "MSG0077"; // 対象データはありませんでした。 376 stopZero = false; // 検索結果が0件のとき処理を続行するかどうか[true/false]を指定します(初期値:false[続行する]) 377 dbid = null; // 検索する対象のDB接続IDを指定します(初期値:null) 378 query = new QueryMaker(); // 検索元のSELECTのSQL文 379 dbid2 = null; // 登録する対象のDB接続IDを指定します(初期値:null) 380 query2 = new QueryMaker(); // 登録先のSQL文 381 quotCheck = HybsSystem.sysBool( "USE_SQL_INJECTION_CHECK" ); 382 stopError = true; // 登録処理エラーの時に処理を中止するかどうか[true/false]を設定します(初期値:true) 383 dispError = true; // エラー時にメッセージを表示するか[true/false]を設定します。通常はstopErrorと併用(初期値:true) 384 fetchSize = DB_FETCH_SIZE ; // フェッチする行数(初期値を100→HybsConst.DB_FETCH_SIZE に変更) 385// errCode = ErrorMessage.OK; // 処理結果のエラーコード(複数合った場合は、最後のエラーコード) 386 dyStart = 0L; // 実行時間測定用のDIV要素を出力します。 387 selCnt = 0; // DB.COUNT : 検索結果の件数 388 upCnt = 0; // DB.UPCOUNT : 追加/更新/削除結果の件数 389 } 390 391 /** 392 * Query を実行します。 393 * 394 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 395 * @og.rev 6.9.0.2 (2018/02/13) カラム名が、"*" の場合の対応 396 * @og.rev 6.9.9.1 (2018/08/27) SELECT検索できなかった場合 397 */ 398 private void execute() { 399 Statement stmt = null; // 検索に使用するステートメント 400 ResultSetValue rsv = null; // 検索に使用 401 PreparedStatement pstmt2 = null; // INSERT/UPDATE/DELETE に使用するステートメント 402 PreparedStatement pstmt3 = null; // MERGE時に使用する INSERT 用ステートメント 403 ParameterMetaData pMeta2 = null; 404 ParameterMetaData pMeta3 = null; 405 406 final boolean useParamMetaData = ConnectionFactory.useParameterMetaData( dbid2 ); 407 408 String sql2 = null ; 409 String sql3 = null ; 410 final StringBuilder val2Buf = new StringBuilder( BUFFER_MIDDLE ); // 6.9.0.2 (2018/02/13) デバッグ情報用 411 final StringBuilder val3Buf = new StringBuilder( BUFFER_MIDDLE ); // 6.9.0.2 (2018/02/13) デバッグ情報用 412 413 // Transaction でAutoCloseableを使用したtry-with-resources構築に対応。 414 try( final Transaction tran = getTransaction() ) { 415 // errMessage = new ErrorMessage( "DBCopyTag Database Error!" ); 416 417 final Connection conn = tran.getConnection( dbid ); 418 stmt = conn.createStatement(); 419 if( fetchSize > 0 ) { stmt.setFetchSize( fetchSize ); } 420 final ResultSet resSer = stmt.executeQuery( selSQL ); // useDelete で使うため 421 rsv = new ResultSetValue( resSer ); // ResultSet を引数に、インスタンス作成 422 423 if( rsv.getColumnCount() == 0 ) { return; } // 6.9.9.1 (2018/08/27) SELECT検索できなかった場合 424 425 // names2を、指定しない場合は、names または、SELECT文のすべてのカラムが、同一名として処理されます。 426 // 6.9.0.2 (2018/02/13) カラム名が、"*" の場合の対応 427// if( StringUtil.isNull( query2.getNames() ) ) { // nullチェックは、すでに終了している。 428 if( "*".equals( query2.getNames() ) ) { 429 final String names2 = String.join( "," , rsv.getNames() ); // SELECT文の名前配列から、CSV文字列を作成 430 query2.setNames( names2 ); 431 } 432 433 switch( action ) { 434 case "INSERT" : sql2 = query2.getInsertSQL(); break; 435 case "UPDATE" : sql2 = query2.getUpdateSQL(); break; 436 case "DELETE" : sql2 = query2.getDeleteSQL(); break; 437 case "MERGE" : sql2 = query2.getUpdateSQL(); 438 sql3 = query2.getInsertSQL(); break; 439 default : break; 440 } 441 442 if( StringUtil.isNull( sql2 ) ) { 443 final String errMsg = "更新用QUERY が作成できませんでした。 " 444 + " action=[" + action + "]" 445 + " query2=[" + sql2 + "]" ; 446 errMessage.addMessage( errMsg ); 447 throw new HybsSystemException( errMsg ); 448 } 449 450 final String[] prmNms2 = query2.getParamNames( false ); // 登録QUERYの、変数設定されているカラム名配列。 451 final int[] clmNos2 = rsv.getColumnNos( prmNms2,true ); // 変数設定カラムのカラム番号。無ければ、Exception 452 453 int[] clmNos3 = null; // MERGE のときの変数設定カラム 454 455 final boolean useMerge = "MERGE".equals( action ); 456 457 final Connection conn2 = tran.getConnection( dbid2 ); 458 pstmt2 = conn2.prepareStatement( sql2 ); 459 pMeta2 = useParamMetaData ? pstmt2.getParameterMetaData() : null ; 460 if( useMerge ) { 461 final Connection conn3 = tran.getConnection( dbid2 ); 462 pstmt3 = conn3.prepareStatement( sql3 ); 463 pMeta3 = useParamMetaData ? pstmt3.getParameterMetaData() : null ; 464 465 final String[] prmNms3 = query2.getParamNames( true ); // MERGE のときの INSERT時なので、true を指定。 466 clmNos3 = rsv.getColumnNos( prmNms3,true ); // 変数設定カラムのカラム番号。無ければ、Exception 467 } 468 469 while( rsv.next() ) { 470 try { 471 selCnt++; // 検索件数の加算 472 val2Buf.setLength(0); // 初期化 473 final String[] vals = rsv.getValues(); 474 for( int no=0; no<clmNos2.length; no++ ) { 475 final int cno = clmNos2[no]; // 変数設定カラムに該当するアドレス。 476 final String val = vals[cno]; 477 val2Buf.append( val ).append( ',' ); // valueのCSV形式 478 if( useParamMetaData ) { 479 final int type = pMeta2.getParameterType( no+1 ); 480 if( val == null || val.isEmpty() ) { 481 pstmt2.setNull( no+1, type ); 482 } 483 else { 484 pstmt2.setObject( no+1,val,type ); 485 } 486 } 487 else { 488 // pstmt2.setString( no+1,vals[cno] ); 489 pstmt2.setObject( no+1,val ); 490 } 491 } 492 493 int cnt = pstmt2.executeUpdate(); // 更新件数 494 if( useMerge && cnt == 0 ) { // マージアクションで、更新が0件の場合は、INSERTを行う。 495 val3Buf.setLength(0); // 初期化 496 for( int no=0; no<clmNos3.length; no++ ) { 497 final int cno = clmNos3[no]; // 変数設定カラムに該当するアドレス。 498 final String val = vals[cno]; 499 val3Buf.append( val ).append( ',' ); // valueのCSV形式 500 if( useParamMetaData ) { 501 final int type = pMeta3.getParameterType( no+1 ); 502 if( val == null || val.isEmpty() ) { 503 pstmt3.setNull( no+1, type ); 504 } 505 else { 506 pstmt3.setObject( no+1,val,type ); 507 } 508 } 509 else { 510 // pstmt3.setString( no+1,vals[cno] ); 511 pstmt3.setObject( no+1,val ); 512 } 513 } 514 cnt = pstmt3.executeUpdate(); // 追加件数 515 } 516 517 upCnt += cnt; // 更新件数の加算 518 if( useDelete ) { resSer.deleteRow(); } // 途中でエラーになった場合は、ResultSet の削除は行いません。 519 } 520 catch( final SQLException ex ) { 521 errMessage.addMessage( selCnt,ErrorMessage.NG,ex.getSQLState(),ex.getMessage() ); 522 if( stopError ) { 523 tran.rollback(); // stopError=false の場合、最後まで処理され、commit() されています。 524 throw ex; // SELECTループ中のSQLExceptionは、stopErrorの判定を行う。 525 } 526 } 527 } 528 529 tran.commit(); 530 } 531 catch( final SQLException ex ) { 532 // 6.9.0.2 (2018/02/13) デバッグ情報 533 final String errMsg = new StringBuilder( BUFFER_MIDDLE ) 534 .append( "更新処理実行中にエラーが発生しました。action=[" ) 535 .append( action ).append( ']' ).append( CR ) 536 .append( " query =[" ).append( selSQL ).append( ']' ).append( CR ) 537 .append( " query2=[" ).append( sql2 ).append( ']' ).append( CR ) 538 .append( " query3=[" ).append( sql3 ).append( ']' ).append( CR ) 539 .append( " value2=[" ).append( val2Buf ).append( ']' ).append( CR ) 540 .append( " value3=[" ).append( val3Buf ).append( ']' ).append( CR ) 541 .toString(); 542 543// final String errMsg = "更新処理実行中にエラーが発生しました。action=[" + action + "]" + CR 544// + " query=[" + selSQL + "]" + CR 545// + " query2=[" + sql2 + "]"; 546 547 errMessage.addMessage( ex ); 548 // errMessage.addMessage( errMsg ); 549 errMessage.addMessage( -1,ErrorMessage.NG,ex.getSQLState(),errMsg ); 550 throw new HybsSystemException( errMsg,ex ); 551 } 552 finally { 553// rsv.close(); 554 Closer.autoClose( rsv ); // 6.9.8.0 (2018/05/28) FindBugs: null 値を例外経路で利用している可能性がある 555 Closer.stmtClose( stmt ); 556 Closer.stmtClose( pstmt2 ); 557 Closer.stmtClose( pstmt3 ); 558 } 559 560 // 変数の関係で、こちらにもって来ました(データアクセス件数登録) 561 final long dyTime = System.currentTimeMillis()-dyStart; 562 final GUIInfo guiInfo = (GUIInfo)getSessionAttribute( HybsSystem.GUIINFO_KEY ); 563 if( guiInfo != null ) { 564 guiInfo.addReadCount( selCnt,dyTime,selSQL ); 565 guiInfo.addWriteCount( upCnt,dyTime,sql2 ); 566 } 567 } 568 569 /** 570 * 【TAG】実行方法を指定します[INSERT/UPDATE/DELETE/MERGE] (初期値:INSERT)。 571 * 572 * @og.tag 573 * 指定できるアクションは、追加(INSERT)、更新(UPDATE)、削除(DELETE)、マージ(MERGE)です。 574 * マージ以外は、お馴染みのSQL処理です。 575 * マージは、条件にしたがって、UPDATEを行い、更新件数が、0件の場合に、INSERTを行う、複合処理です。 576 * 初期値は、INSERT です。 577 * 578 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 579 * 580 * @param action アクション [INSERT/UPDATE/DELETE/MERGE] 581 */ 582 public void setAction( final String action ) { 583 this.action = nval( getRequestParameter( action ),this.action ); 584 } 585 586 /** 587 * 【TAG】(jdbcオプション)検索した元のデータを削除するかどうか[true:削除する/false:なにもしない]を指定します(初期値:false)。 588 * 589 * @og.tag 590 * アクションで指定した処理とともに、検索元のデータを削除するかどうかを指定します。 591 * 例えば、action="INSERT" で、useDelete="true" を指定すると、 ResultSet#deleteRow() を実行して、 592 * 検索元のデータを削除し、更新先にINSERT するため見かけ上、データ移動することになります。 593 * stopError="false" (エラー時でも処理を継続する)にした場合、検索元のデータ削除は、 594 * エラー行については、実行されません。ただし、UPDATE,DELETE 等で、対象データが 595 * 存在しない場合は、エラーと判断しないため、検索元のデータを削除します。 596 * 初期値は、false です。 597 * ※ ResultSet#deleteRow() をサポートしない場合もあるため、仕様の有無は、対象DBをご確認ください。 598 * 599 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 600 * 601 * @param useDel 検索した元のデータを削除するかどうか 602 */ 603 public void setUseDelete( final String useDel ) { 604 useDelete = nval( getRequestParameter( useDel ),useDelete ); 605 } 606 607 /** 608 * 【TAG】(通常は使いません)データの最大読み込み件数を指定します(初期値:0:[無制限])。 609 * 610 * @og.tag 611 * 検索処理の最大件数を指定します。 612 * このタグでは、検索都度、更新するため、メモリ等の負荷は、DBTableModel を使用する 613 * 通常の検索より少なくてすみます。 614 * 初期値は、0(無制限=実際は、Integer.MAX_VALUE)です。 615 * 616 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 617 * 618 * @param count 最大件数 619 */ 620 public void setMaxRowCount( final String count ) { 621 maxRowCount = nval( getRequestParameter( count ),maxRowCount ); 622 if( maxRowCount == 0 ) { maxRowCount = Integer.MAX_VALUE ; } 623 } 624 625// /** 626// * 【TAG】検索結果を画面上に表示するメッセージリソースIDを指定します 627// * (初期値:VIEW_DISPLAY_MSG[={@og.value SystemData#VIEW_DISPLAY_MSG}])。 628// * 629// * @og.tag 630// * ここでは、検索結果の件数や登録された件数をまず出力し、 631// * その次に、ここで指定したメッセージをリソースから取得して表示します。 632// * 件数を表示させる場合は、displayMsg = "MSG0033"[ 件検索しました] をセットしてください。 633// * 表示させたくない場合は, displayMsg = "" をセットしてください。 634// * (初期値:システム定数のVIEW_DISPLAY_MSG[={@og.value SystemData#VIEW_DISPLAY_MSG}])。 635// * 636// * @og.rev 6.8.6.0 (2018/01/19) 新規作成 637// * @og.rev 6.9.1.0 (2018/02/26) displayMsg,overflowMsg,notfoundMsg は未使用のため、削除 638// * 639// * @param id 表示メッセージID 640// * @see org.opengion.hayabusa.common.SystemData#VIEW_DISPLAY_MSG 641// */ 642// public void setDisplayMsg( final String id ) { 643// final String ids = getRequestParameter( id ); 644// if( ids != null ) { displayMsg = ids; } 645// } 646 647// /** 648// * 【TAG】検索データが最大検索数をオーバーした場合に表示するメッセージリソースIDを指定します 649// * (初期値:MSG0007[検索結果が、制限行数を超えましたので、残りはカットされました])。 650// * 651// * @og.tag 652// * 検索結果が、maxRowCount で設定された値より多い場合、何らかのデータは検索されず 653// * 切り捨てられたことになります。 654// * ここでは、displayMsg を表示した後、必要に応じて、このメッセージを表示します。 655// * 表示させたくない場合は, overflowMsg = "" をセットしてください。 656// * 初期値は、MSG0007[検索結果が、制限行数を超えましたので、残りはカットされました]です。 657// * 658// * @og.rev 6.8.6.0 (2018/01/19) 新規作成 659// * @og.rev 6.9.1.0 (2018/02/26) displayMsg,overflowMsg,notfoundMsg は未使用のため、削除 660// * 661// * @param id オーバー時メッセージID 662// */ 663// public void setOverflowMsg( final String id ) { 664// final String ids = getRequestParameter( id ); 665// if( ids != null ) { overflowMsg = ids; } 666// } 667 668// /** 669// * 【TAG】検索結果がゼロ件の場合に表示するメッセージリソースIDを指定します(初期値:MSG0077[対象データはありませんでした])。 670// * 671// * @og.tag 672// * ここでは、検索結果がゼロ件の場合のみ、特別なメッセージを表示させます。 673// * 従来は、displayMsg と兼用で、『0 件検索しました』という表示でしたが、 674// * displayMsg の初期表示は、OFF になりましたので、ゼロ件の場合のみ別に表示させます。 675// * 表示させたくない場合は, notfoundMsg = "" をセットしてください。 676// * 初期値は、MSG0077[対象データはありませんでした]です。 677// * 678// * @og.rev 6.8.6.0 (2018/01/19) 新規作成 679// * @og.rev 6.9.1.0 (2018/02/26) displayMsg,overflowMsg,notfoundMsg は未使用のため、削除 680// * 681// * @param id ゼロ件メッセージID 682// */ 683// public void setNotfoundMsg( final String id ) { 684// final String ids = getRequestParameter( id ); 685// if( ids != null ) { notfoundMsg = ids; } 686// } 687 688 /** 689 * 【TAG】検索結果が0件のとき処理を停止するかどうか[true/false]を指定します(初期値:false[続行する])。 690 * 691 * @og.tag 692 * 初期値は、false(続行する)です。 693 * 694 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 695 * 696 * @param cmd 0件時停止可否 [true:処理を中止する/false:続行する] 697 */ 698 public void setStopZero( final String cmd ) { 699 stopZero = nval( getRequestParameter( cmd ),stopZero ); 700 } 701 702 /** 703 * 【TAG】(通常は使いません)検索する対象のDB接続IDを指定します(初期値:null)。 704 * 705 * @og.tag 706 * 検索側のSELECT文を実行するDB接続IDを指定します。 707 * これは、システムリソースで、DEFAULT_DB_URL 等で指定している データベース接続先 708 * 情報に、XX_DB_URL を定義することで、 dbid="XX" とすると、この 接続先を使用して 709 * データベースにアクセスできます。 710 * 初期値は、Default(=null) です。 711 * 712 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 713 * 714 * @param id データベース接続ID 715 */ 716 public void setDbid( final String id ) { 717 dbid = nval( getRequestParameter( id ),dbid ); 718 } 719 720 /** 721 * 【TAG】検索する対象のテーブル名を指定します(初期値:null)。 722 * 723 * @og.tag 724 * 検索は、この table名を検索するか、BODYに記述された SQL 文を実行します。 725 * 単独検索の場合(JOIN等を行わない場合)に、使用します。 726 * 727 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 728 * 729 * @param table テーブル名 730 */ 731 public void setTable( final String table ) { 732 query.setTable( getRequestParameter( table ) ); 733 } 734 735 /** 736 * 【TAG】検索する対象のカラム名をCSV形式で複数指定します(初期値:*)。 737 * 738 * @og.tag 739 * 複数ある場合は、CSV形式で渡します。 740 * BODYにSELECT文を記述した場合は、names 属性は不要です。 741 * 記述した場合は、SELECTしたカラムから、names属性に指定されたカラムだけを 742 * SELECT対象にします。 743 * 検索元の names と、登録先の、names2 が、対応関係になります。 744 * 初期値は、指定のカラムすべて(*)です。 745 * 746 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 747 * 748 * @param names 引数の名称 (CSV形式) 749 */ 750 public void setNames( final String names ) { 751 query.setNames( getRequestParameter( names ) ); 752 } 753 754 /** 755 * 【TAG】検索する対象を特定するキー条件(where句)を指定します。 756 * 757 * @og.tag 758 * 検索するSELECT文のwhere 句を指定します。通常の WHERE 句の書き方と同じで、 759 * {@XXXX} などが使えます。 760 * 複雑な場合は、BODY に記述してください。where タグや、andタグ等を使って、 761 * 通常のquery タグで指定する方法を、そのまま使います。 762 * 763 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 764 * 765 * @param where 検索条件 (where句) 766 */ 767 public void setWhere( final String where ) { 768 query.setWhere( getRequestParameter( where ) ); 769 } 770 771 /** 772 * 【TAG】検索する対象の検索順(order by句)を指定します。 773 * 774 * @og.tag 775 * 検索するSELECT文のorder by 句を指定します。通常の order by 句の書き方と同じで、 776 * {@XXXX} などが使えます。 777 * 778 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 779 * 780 * @param orderBy 検索条件 (order By句) 781 */ 782 public void setOrderBy( final String orderBy ) { 783 query.setOrderBy( getRequestParameter( orderBy ) ); 784 } 785 786 /** 787 * 【TAG】登録する対象のDB接続IDを指定します(初期値:null)。 788 * 789 * @og.tag 790 * 登録側のINSERT/UPDATE/DELETE文を実行するDB接続IDを指定します。 791 * これは、システムリソースで、DEFAULT_DB_URL 等で指定している データベース接続先 792 * 情報に、XX_DB_URL を定義することで、 dbid="XX" とすると、この 接続先を使用して 793 * データベースにアクセスできます。 794 * 初期値は、Default(=null) です。 795 * 796 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 797 * 798 * @param id データベース接続ID 799 */ 800 public void setDbid2( final String id ) { 801 dbid2 = nval( getRequestParameter( id ),dbid2 ); 802 } 803 804 /** 805 * 【TAG】登録する対象のテーブル名を指定します(初期値:null)。 806 * 807 * @og.tag 808 * 登録は、この table名を使用します。 809 * table2 を指定しない場合は、table と同じテーブルが使用されます。 810 * その場合は、必ず、table が指定されます。 811 * 812 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 813 * 814 * @param table テーブル名 815 */ 816 public void setTable2( final String table ) { 817 query2.setTable( getRequestParameter( table) ); 818 } 819 820 /** 821 * 【TAG】登録する対象のカラム名をCSV形式で複数指定します(初期値:null)。 822 * 823 * @og.tag 824 * 登録する対象のカラム名は、検索したカラム名の順番に割り当てられます。 825 * 例えば、names 属性に、a1,b1,c1 と指定した場合、names2 に、A2,B2,C2 と指定すれば、 826 * 順番に、a1→A2 , b1→B2 , c1→C2 に割り当てられます。 827 * BODY にSELECT文を記述した場合も、names2 を指定すれば、指定のカラムの順番に割り当てます。 828 * これは、SELECT 側と、INSERT/UPDATE 側のカラム名が異なる場合に、検索側に、別名(as 別名)を 829 * 指定する必要がありません。 830 * 指定しない場合(初期値)は、names または、SELECT文のすべてのカラムが、同一名として処理されます。 831 * 832 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 833 * 834 * @param names 引数の名称 (CSV形式) 835 */ 836 public void setNames2( final String names ) { 837 query2.setNames( getRequestParameter( names) ); 838 } 839 840 /** 841 * 【TAG】登録対象外のカラム名をCSV形式で複数指定します(初期値:null)。 842 * 843 * @og.tag 844 * names2 の逆で、登録対象から省くカラム名を指定します。 845 * table 指定や、select * from で、カラム名を大量に指定したい場合、names2 で 846 * 指定するより、除外するカラム名を指定するほうが、少なく(判りやすく)なる 847 * 場合があります。 848 * 849 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 850 * 851 * @param omitNames 登録対象外のカラム列 (CSV形式) 852 */ 853 public void setOmitNames2( final String omitNames ) { 854 query2.setOmitNames( getRequestParameter( omitNames ) ); 855 } 856 857 /** 858 * 【TAG】登録する対象を特定するキー条件(where句)を指定します。 859 * 860 * @og.tag 861 * 登録するUPDATE/DELETE文のwhere 句を指定します。通常の{@XXXX} のほかに、 862 * [検索カラム名] も使用できます。これは、検索側の where 属性と異なります。 863 * ただし、複雑な where 条件は使えませんので、できるだけ、検索側で調整して置いてください。 864 * action="UPDATE/DELETE/MERGE" でのみ有効です。 865 * 866 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 867 * 868 * @param where 検索条件 (where句) 869 */ 870 public void setWhere2( final String where ) { 871 query2.setWhere( getRequestParameter( where) ); 872 } 873 874 /** 875 * 【TAG】登録する対象を特定するキー条件(where句)をCSV形式で複数指定します。 876 * 877 * @og.tag 878 * 生成するUPDATEのwhere 句を指定する方法として、複数のカラム名をCSV指定し、内部で 879 * KEY=[KEY] 文字列を作成します。 880 * ここでは、カラム名は、データベースのカラム名と同じで、かつ、検索側にも 881 * 同じカラムのデータが存在していること、という条件付きとします。 882 * また、where 条件との併用を行いますが、こちらの条件が先に使用され、where 条件は、 883 * and を付けて、文字列結合されます。 884 * 例: CLM,SYSTEM_ID,KBSAKU ⇒ CLM=[CLM] and SYSTEM_ID=[SYSTEM_ID] and KBSAKU=[KBSAKU] 885 * 886 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 887 * 888 * @param names 登録条件カラム (where句)作成のためのカラム名(CSV形式) 889 */ 890 public void setWhereNames2( final String names ) { 891 query2.setWhereNames( getRequestParameter( names) ); 892 } 893 894 /** 895 * 【TAG】設定値を固定値と置き換える対象となるカラム名をCSV形式で複数指定します。 896 * 897 * @og.tag 898 * names 属性のカラムや table 属性より、INSERT/UPDATE文を作成する場合 899 * 外部から指定した固定値を指定するための、カラム名をCSV形式(CSV)で複数指定します。 900 * 901 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 902 * 903 * @param keys 固定値カラム (CSV形式) 904 * @see #setConstVals2( String ) 905 */ 906 public void setConstKeys2( final String keys ) { 907 query2.setConstKeys( getRequestParameter( keys ) ); 908 } 909 910 /** 911 * 【TAG】設定値を固定値と置き換える対象となる設定値をCSV形式で複数指定します。 912 * 913 * @og.tag 914 * names 属性のカラムや table 属性より、INSERT/UPDATE文を作成する場合 915 * 外部から指定した固定値を指定するための、カラム名に対応する設定値をCSV形式(CSV)で 916 * 複数指定します。ここで指定する設定値は、constKeys2 属性と対応させます。 917 * 918 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 919 * 920 * @param vals 設定値(CSV形式) 921 * @see #setConstKeys2( String ) 922 */ 923 public void setConstVals2( final String vals ) { 924 query2.setConstVals( getRequestParameter( vals ) ); 925 } 926 927 /** 928 * 【TAG】リクエスト情報の シングルクォート(') 存在チェックを実施するかどうか[true/false]を設定します 929 * (初期値:USE_SQL_INJECTION_CHECK[={@og.value SystemData#USE_SQL_INJECTION_CHECK}])。 930 * 931 * @og.tag 932 * SQLインジェクション対策の一つとして、暫定的ではありますが、SQLのパラメータに 933 * 渡す文字列にシングルクォート(') を許さない設定にすれば、ある程度は防止できます。 934 * 数字タイプの引数には、 or 5=5 などのシングルクォートを使用しないコードを埋めても、 935 * 数字チェックで検出可能です。文字タイプの場合は、必ず (')をはずして、 936 * ' or 'A' like 'A のような形式になる為、(')チェックだけでも有効です。 937 * (') が含まれていたエラーにする(true)/かノーチェックか(false)を指定します。 938 * 初期値は、SystemData#USE_SQL_INJECTION_CHECK です。 939 * 940 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 941 * 942 * @param flag クォートチェック [true:する/それ以外:しない] 943 */ 944 public void setQuotCheck( final String flag ) { 945 quotCheck = nval( getRequestParameter( flag ),quotCheck ); 946 } 947 948 /** 949 * 【TAG】登録処理エラーの時に処理を中止するかどうか[true/false]を設定します(初期値:true)。 950 * 951 * @og.tag 952 * false(中止しない)に設定する場合、後続処理では、{@DB.ERR_CODE}の値により、 953 * 異常/正常判断を行いますが、処理は、継続されます。 954 * ちなみに、更新/削除処理で、対象データが存在しない場合(0件更新や、0件削除)は、エラーでは 955 * ありません。 956 * 初期値は、true(中止する)です。 957 * 958 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 959 * 960 * @param flag エラー時処理中止 [true:中止する/false:中止しない] 961 */ 962 public void setStopError( final String flag ) { 963 stopError = nval( getRequestParameter( flag ),stopError ); 964 } 965 966 /** 967 * 【TAG】エラー時にメッセージを表示するか[true/false]を設定します。通常はstopErrorと併用(初期値:true)。 968 * 969 * @og.tag 970 * false(表示しない)に設定する場合、後続処理では、{@DB.ERR_MSG}の値により、 971 * 本来表示されるはずだったメッセージを取得可能です。 972 * stopErrorと併用して、JSON形式でエラーを返す場合等に利用します。 973 * 初期値は、true(表示する)です。 974 * ※false指定の場合は件数等も表示されなくなります。 975 * 976 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 977 * 978 * @param flag [true:表示する/false:表示しない] 979 */ 980 public void setDispError( final String flag ) { 981 dispError = nval( getRequestParameter( flag ),dispError ); 982 } 983 984 /** 985 * 【TAG】(通常は使いません)データのフェッチサイズを指定します 986 * (初期値:DB_FETCH_SIZE[={@og.value org.opengion.fukurou.system.HybsConst#DB_FETCH_SIZE}])。 987 * 988 * @og.tag 989 * より多くの行が必要なときに、データベースから取り出す必要がある行数に 990 * ついてのヒントを JDBC ドライバに提供します。 991 * 指定された行数は、この Statement を使って作成された結果セットにだけ影響します。 992 * 指定された値が 0 の場合、ヒントは無視されます。 993 * (初期値:システム定数のDB_FETCH_SIZE[={@og.value org.opengion.fukurou.system.HybsConst#DB_FETCH_SIZE}])。 994 * 995 * @param size フェッチ行数 996 */ 997 public void setFetchSize( final String size ) { 998 fetchSize = nval( getRequestParameter( size ),fetchSize ); 999 } 1000 1001 /** 1002 * このオブジェクトの文字列表現を返します。 1003 * 基本的にデバッグ目的に使用します。 1004 * 1005 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 1006 * 1007 * @return このクラスの文字列表現 1008 */ 1009 @Override 1010 public String toString() { 1011 return selSQL == null ? "" 1012 : selSQL.replaceAll( "[\\\t]+"," " ).replaceAll( "[\\s]+\\\n","\\\n" ) ; 1013 // 連続するTABをスペースに 連続する空白文字と改行を改行のみに 1014 } 1015}