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 = "7.0.6.1 (2019/10/11)" ; 202 private static final long serialVersionUID = 706120191011L ; 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 * @og.rev 7.0.6.1 (2019/10/11) useParamMetaData ではなく、MetaDataが存在しているかどうかで判定します。 398 */ 399 private void execute() { 400 Statement stmt = null; // 検索に使用するステートメント 401 ResultSetValue rsv = null; // 検索に使用 402 PreparedStatement pstmt2 = null; // INSERT/UPDATE/DELETE に使用するステートメント 403 PreparedStatement pstmt3 = null; // MERGE時に使用する INSERT 用ステートメント 404 ParameterMetaData pMeta2 = null; 405 ParameterMetaData pMeta3 = null; 406 407 final boolean useParamMetaData = ConnectionFactory.useParameterMetaData( dbid2 ); 408 409 String sql2 = null ; 410 String sql3 = null ; 411 final StringBuilder val2Buf = new StringBuilder( BUFFER_MIDDLE ); // 6.9.0.2 (2018/02/13) デバッグ情報用 412 final StringBuilder val3Buf = new StringBuilder( BUFFER_MIDDLE ); // 6.9.0.2 (2018/02/13) デバッグ情報用 413 414 // Transaction でAutoCloseableを使用したtry-with-resources構築に対応。 415 try( final Transaction tran = getTransaction() ) { 416 // errMessage = new ErrorMessage( "DBCopyTag Database Error!" ); 417 418 final Connection conn = tran.getConnection( dbid ); 419 stmt = conn.createStatement(); 420 if( fetchSize > 0 ) { stmt.setFetchSize( fetchSize ); } 421 final ResultSet resSer = stmt.executeQuery( selSQL ); // useDelete で使うため 422 rsv = new ResultSetValue( resSer ); // ResultSet を引数に、インスタンス作成 423 424 if( rsv.getColumnCount() == 0 ) { return; } // 6.9.9.1 (2018/08/27) SELECT検索できなかった場合 425 426 // names2を、指定しない場合は、names または、SELECT文のすべてのカラムが、同一名として処理されます。 427 // 6.9.0.2 (2018/02/13) カラム名が、"*" の場合の対応 428// if( StringUtil.isNull( query2.getNames() ) ) { // nullチェックは、すでに終了している。 429 if( "*".equals( query2.getNames() ) ) { 430 final String names2 = String.join( "," , rsv.getNames() ); // SELECT文の名前配列から、CSV文字列を作成 431 query2.setNames( names2 ); 432 } 433 434 switch( action ) { 435 case "INSERT" : sql2 = query2.getInsertSQL(); break; 436 case "UPDATE" : sql2 = query2.getUpdateSQL(); break; 437 case "DELETE" : sql2 = query2.getDeleteSQL(); break; 438 case "MERGE" : sql2 = query2.getUpdateSQL(); 439 sql3 = query2.getInsertSQL(); break; 440 default : break; 441 } 442 443 if( StringUtil.isNull( sql2 ) ) { 444 final String errMsg = "更新用QUERY が作成できませんでした。 " 445 + " action=[" + action + "]" 446 + " query2=[" + sql2 + "]" ; 447 errMessage.addMessage( errMsg ); 448 throw new HybsSystemException( errMsg ); 449 } 450 451 final String[] prmNms2 = query2.getParamNames( false ); // 登録QUERYの、変数設定されているカラム名配列。 452 final int[] clmNos2 = rsv.getColumnNos( prmNms2,true ); // 変数設定カラムのカラム番号。無ければ、Exception 453 454 int[] clmNos3 = null; // MERGE のときの変数設定カラム 455 456 final boolean useMerge = "MERGE".equals( action ); 457 458 final Connection conn2 = tran.getConnection( dbid2 ); 459 pstmt2 = conn2.prepareStatement( sql2 ); 460 pMeta2 = useParamMetaData ? pstmt2.getParameterMetaData() : null ; 461 if( useMerge ) { 462 final Connection conn3 = tran.getConnection( dbid2 ); 463 pstmt3 = conn3.prepareStatement( sql3 ); 464 pMeta3 = useParamMetaData ? pstmt3.getParameterMetaData() : null ; 465 466 final String[] prmNms3 = query2.getParamNames( true ); // MERGE のときの INSERT時なので、true を指定。 467 clmNos3 = rsv.getColumnNos( prmNms3,true ); // 変数設定カラムのカラム番号。無ければ、Exception 468 } 469 470 while( rsv.next() ) { 471 try { 472 selCnt++; // 検索件数の加算 473 val2Buf.setLength(0); // 初期化 474 final String[] vals = rsv.getValues(); 475 for( int no=0; no<clmNos2.length; no++ ) { 476 final int cno = clmNos2[no]; // 変数設定カラムに該当するアドレス。 477 final String val = vals[cno]; 478 // final String val = nval(vals[cno],""); // 7.0.6.1 (2019/10/11) where条件の is null 対策(仮) 479 val2Buf.append( val ).append( ',' ); // valueのCSV形式 480// if( useParamMetaData ) { 481 if( pMeta2 != null ) { // 7.0.6.1 (2019/10/11) ParameterMetaDataの有無で判定 482 final int type = pMeta2.getParameterType( no+1 ); 483 if( val == null || val.isEmpty() ) { 484 pstmt2.setNull( no+1, type ); // where条件がnull の場合、うまく検索できない 485 } 486 else { 487 pstmt2.setObject( no+1,val,type ); 488 } 489 } 490 else { 491 pstmt2.setObject( no+1,val ); 492 } 493 } 494 495 int cnt = pstmt2.executeUpdate(); // 更新件数 496 if( useMerge && cnt == 0 ) { // マージアクションで、更新が0件の場合は、INSERTを行う。 497 val3Buf.setLength(0); // 初期化 498 for( int no=0; no<clmNos3.length; no++ ) { 499 final int cno = clmNos3[no]; // 変数設定カラムに該当するアドレス。 500 final String val = vals[cno]; 501 // final String val = nval(vals[cno],""); // 7.0.6.1 (2019/10/11) where条件の is null 対策(仮) 502 val3Buf.append( val ).append( ',' ); // valueのCSV形式 503// if( useParamMetaData ) { 504 if( pMeta3 != null ) { // 7.0.6.1 (2019/10/11) ParameterMetaDataの有無で判定 505 final int type = pMeta3.getParameterType( no+1 ); 506 if( val == null || val.isEmpty() ) { 507 pstmt3.setNull( no+1, type ); // where条件がnull の場合、うまく検索できない 508 } 509 else { 510 pstmt3.setObject( no+1,val,type ); 511 } 512 } 513 else { 514 pstmt3.setObject( no+1,val ); 515 } 516 } 517 cnt = pstmt3.executeUpdate(); // 追加件数 518 } 519 520 upCnt += cnt; // 更新件数の加算 521 if( useDelete ) { resSer.deleteRow(); } // 途中でエラーになった場合は、ResultSet の削除は行いません。 522 } 523 catch( final SQLException ex ) { 524 errMessage.addMessage( selCnt,ErrorMessage.NG,ex.getSQLState(),ex.getMessage() ); 525 if( stopError ) { 526 tran.rollback(); // stopError=false の場合、最後まで処理され、commit() されています。 527 throw ex; // SELECTループ中のSQLExceptionは、stopErrorの判定を行う。 528 } 529 } 530 } 531 532 tran.commit(); 533 } 534 catch( final SQLException ex ) { 535 // 6.9.0.2 (2018/02/13) デバッグ情報 536 final String errMsg = new StringBuilder( BUFFER_MIDDLE ) 537 .append( "更新処理実行中にエラーが発生しました。action=[" ) 538 .append( action ).append( ']' ).append( CR ) 539 .append( " query =[" ).append( selSQL ).append( ']' ).append( CR ) 540 .append( " query2=[" ).append( sql2 ).append( ']' ).append( CR ) 541 .append( " query3=[" ).append( sql3 ).append( ']' ).append( CR ) 542 .append( " value2=[" ).append( val2Buf ).append( ']' ).append( CR ) 543 .append( " value3=[" ).append( val3Buf ).append( ']' ).append( CR ) 544 .toString(); 545 546// final String errMsg = "更新処理実行中にエラーが発生しました。action=[" + action + "]" + CR 547// + " query=[" + selSQL + "]" + CR 548// + " query2=[" + sql2 + "]"; 549 550 errMessage.addMessage( ex ); 551 // errMessage.addMessage( errMsg ); 552 errMessage.addMessage( -1,ErrorMessage.NG,ex.getSQLState(),errMsg ); 553 throw new HybsSystemException( errMsg,ex ); 554 } 555 finally { 556// rsv.close(); 557 Closer.autoClose( rsv ); // 6.9.8.0 (2018/05/28) FindBugs: null 値を例外経路で利用している可能性がある 558 Closer.stmtClose( stmt ); 559 Closer.stmtClose( pstmt2 ); 560 Closer.stmtClose( pstmt3 ); 561 } 562 563 // 変数の関係で、こちらにもって来ました(データアクセス件数登録) 564 final long dyTime = System.currentTimeMillis()-dyStart; 565 final GUIInfo guiInfo = (GUIInfo)getSessionAttribute( HybsSystem.GUIINFO_KEY ); 566 if( guiInfo != null ) { 567 guiInfo.addReadCount( selCnt,dyTime,selSQL ); 568 guiInfo.addWriteCount( upCnt,dyTime,sql2 ); 569 } 570 } 571 572 /** 573 * 【TAG】実行方法を指定します[INSERT/UPDATE/DELETE/MERGE] (初期値:INSERT)。 574 * 575 * @og.tag 576 * 指定できるアクションは、追加(INSERT)、更新(UPDATE)、削除(DELETE)、マージ(MERGE)です。 577 * マージ以外は、お馴染みのSQL処理です。 578 * マージは、条件にしたがって、UPDATEを行い、更新件数が、0件の場合に、INSERTを行う、複合処理です。 579 * 初期値は、INSERT です。 580 * 581 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 582 * 583 * @param action アクション [INSERT/UPDATE/DELETE/MERGE] 584 */ 585 public void setAction( final String action ) { 586 this.action = nval( getRequestParameter( action ),this.action ); 587 } 588 589 /** 590 * 【TAG】(jdbcオプション)検索した元のデータを削除するかどうか[true:削除する/false:なにもしない]を指定します(初期値:false)。 591 * 592 * @og.tag 593 * アクションで指定した処理とともに、検索元のデータを削除するかどうかを指定します。 594 * 例えば、action="INSERT" で、useDelete="true" を指定すると、 ResultSet#deleteRow() を実行して、 595 * 検索元のデータを削除し、更新先にINSERT するため見かけ上、データ移動することになります。 596 * stopError="false" (エラー時でも処理を継続する)にした場合、検索元のデータ削除は、 597 * エラー行については、実行されません。ただし、UPDATE,DELETE 等で、対象データが 598 * 存在しない場合は、エラーと判断しないため、検索元のデータを削除します。 599 * 初期値は、false です。 600 * ※ ResultSet#deleteRow() をサポートしない場合もあるため、仕様の有無は、対象DBをご確認ください。 601 * 602 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 603 * 604 * @param useDel 検索した元のデータを削除するかどうか 605 */ 606 public void setUseDelete( final String useDel ) { 607 useDelete = nval( getRequestParameter( useDel ),useDelete ); 608 } 609 610 /** 611 * 【TAG】(通常は使いません)データの最大読み込み件数を指定します(初期値:0:[無制限])。 612 * 613 * @og.tag 614 * 検索処理の最大件数を指定します。 615 * このタグでは、検索都度、更新するため、メモリ等の負荷は、DBTableModel を使用する 616 * 通常の検索より少なくてすみます。 617 * 初期値は、0(無制限=実際は、Integer.MAX_VALUE)です。 618 * 619 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 620 * 621 * @param count 最大件数 622 */ 623 public void setMaxRowCount( final String count ) { 624 maxRowCount = nval( getRequestParameter( count ),maxRowCount ); 625 if( maxRowCount == 0 ) { maxRowCount = Integer.MAX_VALUE ; } 626 } 627 628// /** 629// * 【TAG】検索結果を画面上に表示するメッセージリソースIDを指定します 630// * (初期値:VIEW_DISPLAY_MSG[={@og.value SystemData#VIEW_DISPLAY_MSG}])。 631// * 632// * @og.tag 633// * ここでは、検索結果の件数や登録された件数をまず出力し、 634// * その次に、ここで指定したメッセージをリソースから取得して表示します。 635// * 件数を表示させる場合は、displayMsg = "MSG0033"[ 件検索しました] をセットしてください。 636// * 表示させたくない場合は, displayMsg = "" をセットしてください。 637// * (初期値:システム定数のVIEW_DISPLAY_MSG[={@og.value SystemData#VIEW_DISPLAY_MSG}])。 638// * 639// * @og.rev 6.8.6.0 (2018/01/19) 新規作成 640// * @og.rev 6.9.1.0 (2018/02/26) displayMsg,overflowMsg,notfoundMsg は未使用のため、削除 641// * 642// * @param id 表示メッセージID 643// * @see org.opengion.hayabusa.common.SystemData#VIEW_DISPLAY_MSG 644// */ 645// public void setDisplayMsg( final String id ) { 646// final String ids = getRequestParameter( id ); 647// if( ids != null ) { displayMsg = ids; } 648// } 649 650// /** 651// * 【TAG】検索データが最大検索数をオーバーした場合に表示するメッセージリソースIDを指定します 652// * (初期値:MSG0007[検索結果が、制限行数を超えましたので、残りはカットされました])。 653// * 654// * @og.tag 655// * 検索結果が、maxRowCount で設定された値より多い場合、何らかのデータは検索されず 656// * 切り捨てられたことになります。 657// * ここでは、displayMsg を表示した後、必要に応じて、このメッセージを表示します。 658// * 表示させたくない場合は, overflowMsg = "" をセットしてください。 659// * 初期値は、MSG0007[検索結果が、制限行数を超えましたので、残りはカットされました]です。 660// * 661// * @og.rev 6.8.6.0 (2018/01/19) 新規作成 662// * @og.rev 6.9.1.0 (2018/02/26) displayMsg,overflowMsg,notfoundMsg は未使用のため、削除 663// * 664// * @param id オーバー時メッセージID 665// */ 666// public void setOverflowMsg( final String id ) { 667// final String ids = getRequestParameter( id ); 668// if( ids != null ) { overflowMsg = ids; } 669// } 670 671// /** 672// * 【TAG】検索結果がゼロ件の場合に表示するメッセージリソースIDを指定します(初期値:MSG0077[対象データはありませんでした])。 673// * 674// * @og.tag 675// * ここでは、検索結果がゼロ件の場合のみ、特別なメッセージを表示させます。 676// * 従来は、displayMsg と兼用で、『0 件検索しました』という表示でしたが、 677// * displayMsg の初期表示は、OFF になりましたので、ゼロ件の場合のみ別に表示させます。 678// * 表示させたくない場合は, notfoundMsg = "" をセットしてください。 679// * 初期値は、MSG0077[対象データはありませんでした]です。 680// * 681// * @og.rev 6.8.6.0 (2018/01/19) 新規作成 682// * @og.rev 6.9.1.0 (2018/02/26) displayMsg,overflowMsg,notfoundMsg は未使用のため、削除 683// * 684// * @param id ゼロ件メッセージID 685// */ 686// public void setNotfoundMsg( final String id ) { 687// final String ids = getRequestParameter( id ); 688// if( ids != null ) { notfoundMsg = ids; } 689// } 690 691 /** 692 * 【TAG】検索結果が0件のとき処理を停止するかどうか[true/false]を指定します(初期値:false[続行する])。 693 * 694 * @og.tag 695 * 初期値は、false(続行する)です。 696 * 697 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 698 * 699 * @param cmd 0件時停止可否 [true:処理を中止する/false:続行する] 700 */ 701 public void setStopZero( final String cmd ) { 702 stopZero = nval( getRequestParameter( cmd ),stopZero ); 703 } 704 705 /** 706 * 【TAG】(通常は使いません)検索する対象のDB接続IDを指定します(初期値:null)。 707 * 708 * @og.tag 709 * 検索側のSELECT文を実行するDB接続IDを指定します。 710 * これは、システムリソースで、DEFAULT_DB_URL 等で指定している データベース接続先 711 * 情報に、XX_DB_URL を定義することで、 dbid="XX" とすると、この 接続先を使用して 712 * データベースにアクセスできます。 713 * 初期値は、Default(=null) です。 714 * 715 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 716 * 717 * @param id データベース接続ID 718 */ 719 public void setDbid( final String id ) { 720 dbid = nval( getRequestParameter( id ),dbid ); 721 } 722 723 /** 724 * 【TAG】検索する対象のテーブル名を指定します(初期値:null)。 725 * 726 * @og.tag 727 * 検索は、この table名を検索するか、BODYに記述された SQL 文を実行します。 728 * 単独検索の場合(JOIN等を行わない場合)に、使用します。 729 * 730 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 731 * 732 * @param table テーブル名 733 */ 734 public void setTable( final String table ) { 735 query.setTable( getRequestParameter( table ) ); 736 } 737 738 /** 739 * 【TAG】検索する対象のカラム名をCSV形式で複数指定します(初期値:*)。 740 * 741 * @og.tag 742 * 複数ある場合は、CSV形式で渡します。 743 * BODYにSELECT文を記述した場合は、names 属性は不要です。 744 * 記述した場合は、SELECTしたカラムから、names属性に指定されたカラムだけを 745 * SELECT対象にします。 746 * 検索元の names と、登録先の、names2 が、対応関係になります。 747 * 初期値は、指定のカラムすべて(*)です。 748 * 749 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 750 * 751 * @param names 引数の名称 (CSV形式) 752 */ 753 public void setNames( final String names ) { 754 query.setNames( getRequestParameter( names ) ); 755 } 756 757 /** 758 * 【TAG】検索する対象を特定するキー条件(where句)を指定します。 759 * 760 * @og.tag 761 * 検索するSELECT文のwhere 句を指定します。通常の WHERE 句の書き方と同じで、 762 * {@XXXX} などが使えます。 763 * 複雑な場合は、BODY に記述してください。where タグや、andタグ等を使って、 764 * 通常のquery タグで指定する方法を、そのまま使います。 765 * 766 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 767 * 768 * @param where 検索条件 (where句) 769 */ 770 public void setWhere( final String where ) { 771 query.setWhere( getRequestParameter( where.replaceAll("="," ") ) ); 772 773// query.setWhere( getRequestParameter( where ) ); 774 } 775 776 /** 777 * 【TAG】検索する対象の検索順(order by句)を指定します。 778 * 779 * @og.tag 780 * 検索するSELECT文のorder by 句を指定します。通常の order by 句の書き方と同じで、 781 * {@XXXX} などが使えます。 782 * 783 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 784 * 785 * @param orderBy 検索条件 (order By句) 786 */ 787 public void setOrderBy( final String orderBy ) { 788 query.setOrderBy( getRequestParameter( orderBy ) ); 789 } 790 791 /** 792 * 【TAG】登録する対象のDB接続IDを指定します(初期値:null)。 793 * 794 * @og.tag 795 * 登録側のINSERT/UPDATE/DELETE文を実行するDB接続IDを指定します。 796 * これは、システムリソースで、DEFAULT_DB_URL 等で指定している データベース接続先 797 * 情報に、XX_DB_URL を定義することで、 dbid="XX" とすると、この 接続先を使用して 798 * データベースにアクセスできます。 799 * 初期値は、Default(=null) です。 800 * 801 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 802 * 803 * @param id データベース接続ID 804 */ 805 public void setDbid2( final String id ) { 806 dbid2 = nval( getRequestParameter( id ),dbid2 ); 807 } 808 809 /** 810 * 【TAG】登録する対象のテーブル名を指定します(初期値:null)。 811 * 812 * @og.tag 813 * 登録は、この table名を使用します。 814 * table2 を指定しない場合は、table と同じテーブルが使用されます。 815 * その場合は、必ず、table が指定されます。 816 * 817 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 818 * 819 * @param table テーブル名 820 */ 821 public void setTable2( final String table ) { 822 query2.setTable( getRequestParameter( table) ); 823 } 824 825 /** 826 * 【TAG】登録する対象のカラム名をCSV形式で複数指定します(初期値:null)。 827 * 828 * @og.tag 829 * 登録する対象のカラム名は、検索したカラム名の順番に割り当てられます。 830 * 例えば、names 属性に、a1,b1,c1 と指定した場合、names2 に、A2,B2,C2 と指定すれば、 831 * 順番に、a1→A2 , b1→B2 , c1→C2 に割り当てられます。 832 * BODY にSELECT文を記述した場合も、names2 を指定すれば、指定のカラムの順番に割り当てます。 833 * これは、SELECT 側と、INSERT/UPDATE 側のカラム名が異なる場合に、検索側に、別名(as 別名)を 834 * 指定する必要がありません。 835 * 指定しない場合(初期値)は、names または、SELECT文のすべてのカラムが、同一名として処理されます。 836 * 837 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 838 * 839 * @param names 引数の名称 (CSV形式) 840 */ 841 public void setNames2( final String names ) { 842 query2.setNames( getRequestParameter( names) ); 843 } 844 845 /** 846 * 【TAG】登録対象外のカラム名をCSV形式で複数指定します(初期値:null)。 847 * 848 * @og.tag 849 * names2 の逆で、登録対象から省くカラム名を指定します。 850 * table 指定や、select * from で、カラム名を大量に指定したい場合、names2 で 851 * 指定するより、除外するカラム名を指定するほうが、少なく(判りやすく)なる 852 * 場合があります。 853 * 854 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 855 * 856 * @param omitNames 登録対象外のカラム列 (CSV形式) 857 */ 858 public void setOmitNames2( final String omitNames ) { 859 query2.setOmitNames( getRequestParameter( omitNames ) ); 860 } 861 862 /** 863 * 【TAG】登録する対象を特定するキー条件(where句)を指定します。 864 * 865 * @og.tag 866 * 登録するUPDATE/DELETE文のwhere 句を指定します。通常の{@XXXX} のほかに、 867 * [検索カラム名] も使用できます。これは、検索側の where 属性と異なります。 868 * ただし、複雑な where 条件は使えませんので、できるだけ、検索側で調整して置いてください。 869 * action="UPDATE/DELETE/MERGE" でのみ有効です。 870 * 871 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 872 * 873 * @param where 検索条件 (where句) 874 */ 875 public void setWhere2( final String where ) { 876 query2.setWhere( getRequestParameter( where) ); 877 } 878 879 /** 880 * 【TAG】登録する対象を特定するキー条件(where句)をCSV形式で複数指定します。 881 * 882 * @og.tag 883 * 生成するUPDATEのwhere 句を指定する方法として、複数のカラム名をCSV指定し、内部で 884 * KEY=[KEY] 文字列を作成します。 885 * ここでは、カラム名は、データベースのカラム名と同じで、かつ、検索側にも 886 * 同じカラムのデータが存在していること、という条件付きとします。 887 * また、where 条件との併用を行いますが、こちらの条件が先に使用され、where 条件は、 888 * and を付けて、文字列結合されます。 889 * 例: CLM,SYSTEM_ID,KBSAKU ⇒ CLM=[CLM] and SYSTEM_ID=[SYSTEM_ID] and KBSAKU=[KBSAKU] 890 * 891 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 892 * 893 * @param names 登録条件カラム (where句)作成のためのカラム名(CSV形式) 894 */ 895 public void setWhereNames2( final String names ) { 896 query2.setWhereNames( getRequestParameter( names) ); 897 } 898 899 /** 900 * 【TAG】設定値を固定値と置き換える対象となるカラム名をCSV形式で複数指定します。 901 * 902 * @og.tag 903 * names 属性のカラムや table 属性より、INSERT/UPDATE文を作成する場合 904 * 外部から指定した固定値を指定するための、カラム名をCSV形式(CSV)で複数指定します。 905 * 906 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 907 * 908 * @param keys 固定値カラム (CSV形式) 909 * @see #setConstVals2( String ) 910 */ 911 public void setConstKeys2( final String keys ) { 912 query2.setConstKeys( getRequestParameter( keys ) ); 913 } 914 915 /** 916 * 【TAG】設定値を固定値と置き換える対象となる設定値をCSV形式で複数指定します。 917 * 918 * @og.tag 919 * names 属性のカラムや table 属性より、INSERT/UPDATE文を作成する場合 920 * 外部から指定した固定値を指定するための、カラム名に対応する設定値をCSV形式(CSV)で 921 * 複数指定します。ここで指定する設定値は、constKeys2 属性と対応させます。 922 * 923 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 924 * 925 * @param vals 設定値(CSV形式) 926 * @see #setConstKeys2( String ) 927 */ 928 public void setConstVals2( final String vals ) { 929 query2.setConstVals( getRequestParameter( vals ) ); 930 } 931 932 /** 933 * 【TAG】リクエスト情報の シングルクォート(') 存在チェックを実施するかどうか[true/false]を設定します 934 * (初期値:USE_SQL_INJECTION_CHECK[={@og.value SystemData#USE_SQL_INJECTION_CHECK}])。 935 * 936 * @og.tag 937 * SQLインジェクション対策の一つとして、暫定的ではありますが、SQLのパラメータに 938 * 渡す文字列にシングルクォート(') を許さない設定にすれば、ある程度は防止できます。 939 * 数字タイプの引数には、 or 5=5 などのシングルクォートを使用しないコードを埋めても、 940 * 数字チェックで検出可能です。文字タイプの場合は、必ず (')をはずして、 941 * ' or 'A' like 'A のような形式になる為、(')チェックだけでも有効です。 942 * (') が含まれていたエラーにする(true)/かノーチェックか(false)を指定します。 943 * 初期値は、SystemData#USE_SQL_INJECTION_CHECK です。 944 * 945 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 946 * 947 * @param flag クォートチェック [true:する/それ以外:しない] 948 */ 949 public void setQuotCheck( final String flag ) { 950 quotCheck = nval( getRequestParameter( flag ),quotCheck ); 951 } 952 953 /** 954 * 【TAG】登録処理エラーの時に処理を中止するかどうか[true/false]を設定します(初期値:true)。 955 * 956 * @og.tag 957 * false(中止しない)に設定する場合、後続処理では、{@DB.ERR_CODE}の値により、 958 * 異常/正常判断を行いますが、処理は、継続されます。 959 * ちなみに、更新/削除処理で、対象データが存在しない場合(0件更新や、0件削除)は、エラーでは 960 * ありません。 961 * 初期値は、true(中止する)です。 962 * 963 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 964 * 965 * @param flag エラー時処理中止 [true:中止する/false:中止しない] 966 */ 967 public void setStopError( final String flag ) { 968 stopError = nval( getRequestParameter( flag ),stopError ); 969 } 970 971 /** 972 * 【TAG】エラー時にメッセージを表示するか[true/false]を設定します。通常はstopErrorと併用(初期値:true)。 973 * 974 * @og.tag 975 * false(表示しない)に設定する場合、後続処理では、{@DB.ERR_MSG}の値により、 976 * 本来表示されるはずだったメッセージを取得可能です。 977 * stopErrorと併用して、JSON形式でエラーを返す場合等に利用します。 978 * 初期値は、true(表示する)です。 979 * ※false指定の場合は件数等も表示されなくなります。 980 * 981 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 982 * 983 * @param flag [true:表示する/false:表示しない] 984 */ 985 public void setDispError( final String flag ) { 986 dispError = nval( getRequestParameter( flag ),dispError ); 987 } 988 989 /** 990 * 【TAG】(通常は使いません)データのフェッチサイズを指定します 991 * (初期値:DB_FETCH_SIZE[={@og.value org.opengion.fukurou.system.HybsConst#DB_FETCH_SIZE}])。 992 * 993 * @og.tag 994 * より多くの行が必要なときに、データベースから取り出す必要がある行数に 995 * ついてのヒントを JDBC ドライバに提供します。 996 * 指定された行数は、この Statement を使って作成された結果セットにだけ影響します。 997 * 指定された値が 0 の場合、ヒントは無視されます。 998 * (初期値:システム定数のDB_FETCH_SIZE[={@og.value org.opengion.fukurou.system.HybsConst#DB_FETCH_SIZE}])。 999 * 1000 * @param size フェッチ行数 1001 */ 1002 public void setFetchSize( final String size ) { 1003 fetchSize = nval( getRequestParameter( size ),fetchSize ); 1004 } 1005 1006 /** 1007 * このオブジェクトの文字列表現を返します。 1008 * 基本的にデバッグ目的に使用します。 1009 * 1010 * @og.rev 6.8.6.0 (2018/01/19) 新規作成 1011 * 1012 * @return このクラスの文字列表現 1013 */ 1014 @Override 1015 public String toString() { 1016 return selSQL == null ? "" 1017 : selSQL.replaceAll( "[\\\t]+"," " ).replaceAll( "[\\s]+\\\n","\\\n" ) ; 1018 // 連続するTABをスペースに 連続する空白文字と改行を改行のみに 1019 } 1020}