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