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 018import org.opengion.hayabusa.common.HybsSystem; 019import org.opengion.hayabusa.common.HybsSystemException; 020import org.opengion.hayabusa.db.DBTableModel; 021import org.opengion.hayabusa.db.Query; 022import org.opengion.hayabusa.db.DBSysArg; 023import org.opengion.hayabusa.db.DBUserArg; 024import org.opengion.hayabusa.resource.GUIInfo; 025 026import org.opengion.fukurou.util.ErrorMessage; 027import org.opengion.fukurou.util.StringUtil ; 028import static org.opengion.fukurou.util.StringUtil.nval ; 029 030/** 031 * PLSQLをCALLしてデータベースにアクセスするタグです。 032 * queryType = "JDBCPLSQL" が、標準で用意されています。 033 * queryType と 実際のJavaクラスとの関連付けは、システムリソースの Query_JDBCPLSQL 属性です。 034 * 035 * DBTableModel内のデータを 配列でPL/SQLに渡してDB登録します。 036 * 037 * ※ このタグは、Transaction タグの対象です。 038 * 039 * @og.formSample 040 * ●形式:<og:plsqlUpdate command="…" names="…" dbType="…" queryType="JDBCPLSQL" >{plsql(?,?,?,?,?)} <og:plsqlUpdate> 041 * ●body:あり(EVAL_BODY_BUFFERED:BODYを評価し、{@XXXX} を解析します) 042 * 043 * ●Tag定義: 044 * <og:plsqlUpdate 045 * queryType 【TAG】Query を発行する為のクラスIDを指定します({@og.doc03Link queryType 初期値:JDBCPLSQL}) 046 * command 【TAG】コマンド(NEW,RENEW)をセットします(PlsqlUpdateTag,UpdateTag の場合は、ENTRY) 047 * scope 【TAG】キャッシュする場合のスコープ[request/page/session/applicaton]を指定します(初期値:session) 048 * maxRowCount 【TAG】(通常は使いません)データの最大読み込み件数を指定します (初期値:DB_MAX_ROW_COUNT[=1000])(0:[無制限]) 049 * skipRowCount 【TAG】(通常は使いません)データの読み始めの初期値を指定します 050 * notfoundMsg 【TAG】検索結果がゼロ件の場合に表示するメッセージリソースIDを指定します(初期値:MSG0077[対象データはありませんでした]) 051 * names 【TAG】PL/SQLを利用する場合の引数にセットすべき データの名称をCSV形式で複数指定します 052 * dbType 【TAG】Queryオブジェクトに渡す引数のタイプ定義(例:type名_ARRAY) 053 * selectedAll 【TAG】データを全件選択済みとして処理するかどうか[true/false]を指定します(初期値:false) 054 * tableId 【TAG】(通常は使いません)結果のDBTableModelを、sessionに登録するときのキーを指定します 055 * dbid 【TAG】(通常は使いません)Queryオブジェクトを作成する時のDB接続IDを指定します 056 * stopError 【TAG】PLSQL/SQL処理エラーの時に処理を中止するかどうか[true/false]を設定します(初期値:true) 057 * dispError 【TAG】エラー時にメッセージを表示するか[true/false]を設定します。通常はstopErrorと併用(初期値:true) 058 * tableModelCommit 【TAG】テーブルモデルの確定処理を行うかどうか[true/false]を設定します(初期値:true) 059 * debug 【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false) 060 * > ... Body ... 061 * </og:plsqlUpdate> 062 * 063 * ●使用例 064 * ・引数/プロシジャーを他のJSPから渡す場合 065 * 【copy.jsp】 066 * <og:hidden name="names" value="UNIQ,USRID,ECNO,EDBN" /> 067 * <og:hidden name="SQL" value="{ call RKP0271E.RK0271E( ?,?,?,?,? ) }" /> 068 * 【entry.jsp】 069 * <og:plsqlUpdate 070 * command = "{@command}" 071 * names = "{@names}" →PL/SQLに渡す引数(配列)のカラム名 072 * dbType = "RK0271ARG" →PL/SQLに渡す引数(配列)の定義ファイル名 073 * queryType = "JDBCPLSQL" > 074 * {@SQL} →CALLするPL/SQL 075 * </og:plsqlUpdate> 076 * 077 * ・引数/プロシジャーを直接書く場合 078 * 【entry.jsp】 079 * <og:plsqlUpdate 080 * command = "{@command}" 081 * names = "UNIQ,USRID,ECNO,EDBN" →PL/SQLに渡す引数(配列)のカラム名 082 * dbType = "RK0271ARG" →PL/SQLに渡す引数(配列)の定義ファイル名 083 * queryType = "JDBCPLSQL" > 084 * { call RKP0271E.RK0271E( ?,?,?,?,? )} →CALLするPL/SQL 085 * </og:plsqlUpdate> 086 * 087 * <<参考>> 088 * ・RKP0271E.RK0271E( ?,?,?,?,? )の「?」の意味 089 * (RKP0271E.spc)------------------------------------------------------------ 090 * CREATE OR REPLACE PACKAGE RKP0271E AS 091 * PROCEDURE RK0271E( 092 * P_KEKKA OUT NUMBER -- 1個目の「?」⇒結果 0:正常 1:警告 2:異常 093 * ,P_ERRMSGS OUT ERR_MSG_ARRAY -- 2個目の「?」⇒エラーメッセージ配列 094 * ,P_NAMES IN VARCHAR2 -- 3個目の「?」⇒カラム名チェック用文字列 095 * ,P_SYSARGS IN SYSARG_ARRAY -- 4個目の「?」⇒登録条件配列(改廃(A:追加/C:変更/D:削除)等がセットされます) 096 * ,P_RK0271 IN RK0271ARG_ARRAY -- 5個目の「?」⇒登録データ配列 097 * 098 * ・RK0271ARGの定義の仕方 099 * (RK0271ARG.sql)------------------------------------------------------------ 100 * DROP TYPE RK0271ARG_ARRAY; 101 * CREATE OR REPLACE TYPE RK0271ARG AS OBJECT 102 * ( 103 * UNIQ VARCHAR2(11) 104 * ,USRID VARCHAR2(5) 105 * ,ECNO VARCHAR(7) 106 * ,EDBN VARCHAR(2) 107 * ) ; 108 * / 109 * CREATE OR REPLACE TYPE RK0271ARG_ARRAY AS VARRAY(100) OF RK0271ARG; 110 * / 111 * 112 * @og.group DB登録 113 * 114 * @version 4.0 115 * @author Kazuhiko Hasegawa 116 * @since JDK5.0, 117 */ 118public class PlsqlUpdateTag extends QueryTag { 119 //* このプログラムのVERSION文字列を設定します。 {@value} */ 120 private static final String VERSION = "5.5.5.2 (2012/08/10)" ; 121 122 private static final long serialVersionUID = 555220120810L ; 123 124 /** command 引数に渡す事の出来る コマンド 登録{@value} */ 125 public static final String CMD_ENTRY = "ENTRY" ; 126 /** command 引数に渡す事の出来る コマンド リスト */ 127 private static final String COMMAND_LIST = CMD_ENTRY; 128 129 /** 引数のタイプ定義 */ 130 protected String userDBType = null; 131 132 // 3.5.2.0 (2003/10/20) 内部オブジェクトタイプ名を システムパラメータ で定義します。 133 private static final String SYSARG = "SYSARG"; 134 private boolean selectedAll = false; 135 136 private boolean isTableModelCommit = true; // 5.5.5.2 (2012/08/10) 137 138 /** 139 * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。 140 * 141 * @return 後続処理の指示 142 */ 143 @Override 144 public int doStartTag() { 145 dyStart = System.currentTimeMillis(); 146 147 table = (DBTableModel)getObject( tableId ); 148 if( table == null || table.getRowCount() == 0 || 149 ! check( command, COMMAND_LIST ) ) { return(SKIP_BODY); } 150 151 startQueryTransaction( tableId ); // 3.6.0.8 (2004/11/19) 152 return( EVAL_BODY_BUFFERED ); // Body を評価する。( extends BodyTagSupport 時) 153 } 154 155 /** 156 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。 157 * 158 * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。 159 * @og.rev 3.5.5.2 (2004/04/02) TaglibUtil.makeHTMLErrorTable メソッドを利用 160 * @og.rev 3.6.0.8 (2004/11/19) DBTableModel をセーブする時に、トランザクションチェックを行います。 161 * @og.rev 3.6.1.0 (2005/01/05) オーバーフロー時と登録件数の表示をコメントします。 162 * @og.rev 4.3.3.0 (2008/09/22) 検索結果を、"DB.ERR_CODE" キーでリクエストにセットする。 163 * @og.rev 4.3.3.0 (2008/09/22) 属性 stopError の設定により、JSP処理を中止するかどうかを制御します。 164 * @og.rev 4.3.5.7 (2009/03/22) アクセスカウント不具合対応 165 * @og.rev 5.9.26.1 (2017/11/10) dispError対応 166 * @og.rev 5.10.2.2 (2018/08/24) 一部のエラーをjspPrintから標準のErrorMessageに変更 167 * 168 * @return 後続処理の指示 169 */ 170 @Override 171 public int doEndTag() { 172 debugPrint(); // 4.0.0 (2005/02/28) 173 174 String label = HybsSystem.BR; // 検索しなかった場合。 175 if( check( command, COMMAND_LIST ) ) { 176 // 5.10.2.2 (2018/08/24) 6.9.9.0 「ERR0041:検索処理中に割り込みの検索要求がありました」エラーを、標準のErrorMessageに追加するようにします。 177 if( table != null && ! commitTableObject( tableId, table ) ) { 178 if( errMessage == null ) { errMessage = new ErrorMessage( "PlsqlUpdateTag Query Error!" ); } 179 // ERR0041:検索処理中に割り込みの検索要求がありました。処理されません。 180 errMessage.addMessage( 0,ErrorMessage.NG,"ERR0041" ); 181 errCode = ErrorMessage.NG; 182 } 183 184 // 3.5.5.2 (2004/04/02) TaglibUtil.makeHTMLErrorTable メソッドを利用 185 String err = TaglibUtil.makeHTMLErrorTable( errMessage,getResource() ); 186 if( err != null && err.length() > 0 ) { 187 if( errCode >= ErrorMessage.NG ) { // 異常の場合 188 label = err; 189 } 190 setSessionAttribute( errMsgId,errMessage ); 191 } 192 else { 193 removeSessionAttribute( errMsgId ); 194 } 195 // 4.3.3.0 (2008/09/22) 検索結果を、"DB.ERR_CODE" キーでリクエストにセットする。 196 setRequestAttribute( "DB.ERR_CODE", String.valueOf( errCode ) ); 197 198 // 5.9.26.1 (2017/11/10) エラーメッセージをリクエスト変数で持つようにしておく 199 setRequestAttribute( "DB.ERR_MSG", label ); 200 201// // 5.10.2.2 (2018/08/24) 6.9.9.0 202// // 3.6.0.8 (2004/11/19) トランザクションチェックを行います。 203// // 4.0.0.0 (2007/11/29) 入れ子if の統合 204// if( table != null && ! commitTableObject( tableId, table ) ) { 205// jspPrint( "PlsqlUpdateTag Query処理が割り込まれました。DBTableModel は登録しません。" ); 206// return (SKIP_PAGE); 207// } 208 } 209 210 // 5.9.26.1 (2017/11/10) dispErrorで表示をコントロール 211 if( dispError ) { 212 jspPrint( label ); 213 } 214 215// int rtnCode = EVAL_PAGE; 216// if( errCode >= ErrorMessage.NG ) { // 異常 217// rtnCode = SKIP_PAGE; 218// } 219// else { 220// rtnCode = EVAL_PAGE; 221// } 222 223 // 4.0.0 (2005/01/31) 処理時間集計 224 long dyTime = System.currentTimeMillis()-dyStart; 225 226 // 4.0.0 (2005/01/31) セキュリティチェック(データアクセス件数登録) 227 GUIInfo guiInfo = (GUIInfo) getSessionAttribute( HybsSystem.GUIINFO_KEY ); 228 executeCount = getParameterRows().length ; // 4.3.5.7 (2009/03/16) アクセス件数不具合対応。チェック行と仮定 229 if( guiInfo != null ) { guiInfo.addWriteCount( executeCount,dyTime,sql ); } 230 // 4.3.3.0 (2008/09/22) 属性 stopError の設定により、処理を中止するかを判断します。 231 int rtnCode = ( ( errCode >= ErrorMessage.NG ) && ( stopError ) ) ? SKIP_PAGE : EVAL_PAGE; 232 return( rtnCode ); 233 } 234 235 /** 236 * タグリブオブジェクトをリリースします。 237 * キャッシュされて再利用されるので、フィールドの初期設定を行います。 238 * 239 * @og.rev 2.0.0.4 (2002/09/27) カスタムタグの release() メソッドを、追加 240 * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。 241 * @og.rev 3.5.2.0 (2003/10/20) sysDBType 廃止。SYSARG は、システムパラメータ で定義します。 242 * @og.rev 5.5.5.2 (2012/08/10) isTableModelCommit追加 243 * 244 */ 245 @Override 246 protected void release2() { 247 super.release2(); 248 userDBType = null; 249 selectedAll = false; 250 isTableModelCommit = true; // 5.5.5.2 (2012/08/10) 251 } 252 253 /** 254 * Query を実行します。 255 * 256 * @og.rev 2.1.2.3 (2002/12/02) データベース更新時に、更新フラグをセットするように変更 257 * @og.rev 3.5.0.0 (2003/09/17) カラム名ではなく、カラム番号を先に求めておく方式に変更。 258 * @og.rev 3.5.2.0 (2003/10/20) 内部オブジェクトタイプ名を システムパラメータ で定義します。 259 * @og.rev 3.5.4.2 (2003/12/15) HTMLTableViewForm クラス名変更(⇒ ViewForm_HTMLTable) 260 * @og.rev 3.5.6.0 (2004/06/18) DBRowHeader のパッケージプライベート化に伴なう変更 261 * @og.rev 4.0.0.0 (2005/01/31) setArguments 廃止、Query#execute に、引数をすべて追加 262 * @og.rev 4.3.0.0 (2008/07/22) DBSysArgの引数に日付、PG、ユーザーIDを追加 263 * @og.rev 5.5.5.2 (2012/08/10) isTableModelCommitによるテーブルモデル確定処理のコントロール 264 * 265 * @param query オブジェクト 266 */ 267 @Override 268 protected void execute( final Query query ) { 269 try { 270 if( names == null ) { 271 String errMsg = "names 属性が、設定されていません。" + HybsSystem.CR 272 + sql + HybsSystem.CR ; 273 throw new HybsSystemException( errMsg ); // 3.5.5.4 (2004/04/15) 引数の並び順変更 274 } 275 else { 276 int[] rowNo = getParameterRows(); 277 int rowCount = rowNo.length ; 278 if( rowCount > 0 ) { 279 String[] nameArray = StringUtil.csv2Array( names ); 280 int[] clmNo = getTableColumnNo( nameArray ); // 3.5.0.0 281 282 String curdate = HybsSystem.getDate( "yyyyMMddHHmmss" ); // 4.3.0.0 283 String pgid = getGUIInfoAttri( "KEY" ); // 4.3.0.0 284 String userid = getUser().getAttribute( "ID" ); // 4.3.0.0 285 286 DBSysArg[] sysArg = new DBSysArg[rowCount]; 287 DBUserArg[] userArg = new DBUserArg[rowCount]; 288 for( int i=0; i<rowCount; i++ ) { 289 int row = rowNo[i]; 290 String cdkh = table.getModifyType( row ); 291// sysArg[i] = new DBSysArg( SYSARG,row,cdkh ); // 3.5.2.0 // 4.3.0.0 292 sysArg[i] = new DBSysArg( SYSARG,row,cdkh,curdate,pgid,userid ); 293 String[] values = getTableModelData( clmNo,row ); // 3.5.0.0 294 userArg[i] = new DBUserArg( userDBType,nameArray,values ); 295 } 296 query.execute( names,userDBType + "_ARRAY",sysArg,userArg ); 297 errCode = query.getErrorCode(); 298 errMessage = query.getErrorMessage(); 299 300 if( errCode < ErrorMessage.NG ) { // 異常以外の場合 301 query.commit(); 302 if( isTableModelCommit ) { // 5.5.5.2 (2012/08/10)) 303 for( int j=rowCount-1; j>=0; j-- ) { 304 int row = rowNo[j]; 305 if( DBTableModel.DELETE_TYPE.equals( table.getModifyType( row ) ) ) { 306 table.removeValue( row ); 307 } 308 else { 309 table.resetModify( row ); 310 } 311 } 312 } 313 } 314 else { 315 query.rollback(); 316 } 317 } 318 } 319 } 320 catch( HybsSystemException ex ) { 321 query.rollback(); 322 throw ex; 323 } 324 finally { 325 if( query != null ) { query.close(); } 326 } 327 } 328 329 /** 330 * カラム名配列(String[])より、対応するカラムNo配列(int[])を作成します。 331 * 332 * @og.rev 3.5.0.0 (2003/09/17) 新規追加 333 * 334 * @param nameArray カラム名配列 335 * 336 * @return カラムNo配列 337 */ 338 private int[] getTableColumnNo( final String[] nameArray ) { 339 int[] clmNo = new int[ nameArray.length ]; 340 for( int i=0; i<clmNo.length; i++ ) { 341 clmNo[i] = table.getColumnNo( nameArray[i] ); 342 } 343 return clmNo; 344 } 345 346 /** 347 * 指定の行番号の、カラムNo配列(int[])に対応した値の配列を返します。 348 * 349 * 表示データの HybsSystem.ROW_SEL_KEY を元に、選ばれた 行を 350 * 処理の対象とします。 351 * 352 * @og.rev 3.5.0.0 (2003/09/17) カラム名ではなく、カラム番号を受け取るように修正。 353 * 354 * @param clmNo カラムNo配列 355 * @param row 行番号 356 * 357 * @return 行番号とカラムNo配列に対応した、値の配列 358 */ 359 private String[] getTableModelData( final int[] clmNo,final int row ) { 360 String[] values = new String[ clmNo.length ]; 361 for( int i=0; i<values.length; i++ ) { 362 values[i] = table.getValue( row,clmNo[i] ) ; 363 // NUMBER タイプのキャストエラーを防ぐ為の対応 364 if( values[i] != null && values[i].length() == 0 ) { values[i] = null; } 365 } 366 return values; 367 } 368 369 /** 370 * 表示データの HybsSystem.ROW_SEL_KEY を元に、選ばれた 行を処理の対象とします。 371 * 372 * @og.rev 4.0.0.0 (2005/01/31) getParameterRows() を使用するように変更 373 * 374 * @return 選択行の配列 375 */ 376 @Override 377 protected int[] getParameterRows() { 378 final int[] rowNo ; 379 if( selectedAll ) { 380 int rowCnt = table.getRowCount(); // 3.5.5.7 (2004/05/10) 381 rowNo = new int[ rowCnt ]; 382 for( int i=0; i<rowCnt; i++ ) { 383 rowNo[i] = i; 384 } 385 } else { 386 rowNo = super.getParameterRows(); // 4.0.0 (2005/01/31) 387 } 388 return rowNo ; 389 } 390 391 /** 392 * 【TAG】Queryオブジェクトに渡す引数のタイプ定義(例:type名_ARRAY)。 393 * 394 * @og.tag 395 * ここでは、type 定義のPL/SQL名を指定します。 396 * 行を表す配列は、type名_ARRAY という名称です。 397 * 398 * @param type 定義のPL/SQL名 399 */ 400 public void setDbType( final String type ) { 401 userDBType = getRequestParameter( type ); 402 } 403 404 /** 405 * 【TAG】データを全件選択済みとして処理するかどうか[true/false]を指定します(初期値:false)。 406 * 407 * @og.tag 408 * 全てのデータを選択済みデータとして扱って処理します。 409 * 全件処理する場合に、(true/false)を指定します。 410 * 初期値は false です。 411 * 412 * @param all データを全件選択済み [true:全件選択済み/false:通常] 413 */ 414 public void setSelectedAll( final String all ) { 415 selectedAll = nval( getRequestParameter( all ),selectedAll ); 416 } 417 418 /** 419 * 【TAG】Query を発行する為のクラスIDを指定します({@og.doc03Link queryType 初期値:JDBCPLSQL})。 420 * 421 * @og.tag 422 * 引数指定のINSERT/UPDATE文を実行する場合の、queryType 属性を使用します。 423 * このタグでは、execute( String ,String , DBSysArg[] , DBUserArg[] )を実行します。 424 * 代表的なクラスとして、"JDBCPLSQL" が標準で用意されています。 425 * 426 * タグにより使用できる/出来ないがありますが、これは、org.opengion.hayabusa.db 427 * 以下の Query_**** クラスの **** を与えます。 428 * これらは、Query インターフェースを継承したサブクラスです。 429 * {@og.doc03Link queryType Query_**** クラス} 430 * 431 * @og.rev 3.5.4.2 (2003/12/15) JavaDocコメント用にメソッド追加。 432 * 433 * @param id Query を発行する為の実クラス ID 434 * @see org.opengion.hayabusa.db.Query Queryのサブクラス 435 * @see org.opengion.hayabusa.db.Query#execute( String ,String , DBSysArg[] , DBUserArg[] ) 436 */ 437 @Override 438 public void setQueryType( final String id ) { 439 super.setQueryType( nval( id,"JDBCPLSQL" ) ); 440 } 441 442 /** 443 * 【TAG】テーブルモデルに対する確定処理を行うかどうかを指定します(初期値:true)。 444 * 445 * @og.tag 446 * PlsqlUpdateタグで、エラーがなかった場合は通常、テーブルモデルの改廃に従って処理が行われます。 447 * (改廃Dについては削除処理を行い、その他については改廃を元に戻す) 448 * 449 * このパラメータをfalseに指定すると、テーブルモデルに対する処理を行いません。 450 * これは、例えばPL/SQLでエラーチェックのみを行いたい場合に有効です。 451 * 初期値はtrue(処理を行う)です。 452 * 453 * @og.rev 5.5.5.2 (2012/08/10) 新規作成 454 * 455 * @param flag テーブルモデルに対する処理を行うかどうか 456 */ 457 public void setTableModelCommit( final String flag ) { 458 isTableModelCommit = nval( getRequestParameter( flag ),isTableModelCommit ); 459 } 460 461 /** 462 * このオブジェクトの文字列表現を返します。 463 * 基本的にデバッグ目的に使用します。 464 * 465 * @return このクラスの文字列表現 466 */ 467 @Override 468 public String toString() { 469 return org.opengion.fukurou.util.ToString.title( this.getClass().getName() ) 470 .println( "VERSION" ,VERSION ) 471 .println( "selectedAll" ,selectedAll ) 472 .fixForm().toString() 473 + HybsSystem.CR 474 + super.toString() ; 475 } 476}