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 * 167 * @return 後続処理の指示 168 */ 169 @Override 170 public int doEndTag() { 171 debugPrint(); // 4.0.0 (2005/02/28) 172 173 String label = HybsSystem.BR; // 検索しなかった場合。 174 if( check( command, COMMAND_LIST ) ) { 175 176 // 3.5.5.2 (2004/04/02) TaglibUtil.makeHTMLErrorTable メソッドを利用 177 String err = TaglibUtil.makeHTMLErrorTable( errMessage,getResource() ); 178 if( err != null && err.length() > 0 ) { 179 if( errCode >= ErrorMessage.NG ) { // 異常の場合 180 label = err; 181 } 182 setSessionAttribute( errMsgId,errMessage ); 183 } 184 else { 185 removeSessionAttribute( errMsgId ); 186 } 187 // 4.3.3.0 (2008/09/22) 検索結果を、"DB.ERR_CODE" キーでリクエストにセットする。 188 setRequestAttribute( "DB.ERR_CODE", String.valueOf( errCode ) ); 189 190 // 5.9.26.1 (2017/11/10) エラーメッセージをリクエスト変数で持つようにしておく 191 setRequestAttribute( "DB.ERR_MSG", label ); 192 193 // 3.6.0.8 (2004/11/19) トランザクションチェックを行います。 194 // 4.0.0.0 (2007/11/29) 入れ子if の統合 195 if( table != null && ! commitTableObject( tableId, table ) ) { 196 jspPrint( "PlsqlUpdateTag Query処理が割り込まれました。DBTableModel は登録しません。" ); 197 return (SKIP_PAGE); 198 } 199 } 200 201 // 5.9.26.1 (2017/11/10) dispErrorで表示をコントロール 202 if( dispError ) { 203 jspPrint( label ); 204 } 205 206// int rtnCode = EVAL_PAGE; 207// if( errCode >= ErrorMessage.NG ) { // 異常 208// rtnCode = SKIP_PAGE; 209// } 210// else { 211// rtnCode = EVAL_PAGE; 212// } 213 214 // 4.0.0 (2005/01/31) 処理時間集計 215 long dyTime = System.currentTimeMillis()-dyStart; 216 217 // 4.0.0 (2005/01/31) セキュリティチェック(データアクセス件数登録) 218 GUIInfo guiInfo = (GUIInfo) getSessionAttribute( HybsSystem.GUIINFO_KEY ); 219 executeCount = getParameterRows().length ; // 4.3.5.7 (2009/03/16) アクセス件数不具合対応。チェック行と仮定 220 if( guiInfo != null ) { guiInfo.addWriteCount( executeCount,dyTime,sql ); } 221 // 4.3.3.0 (2008/09/22) 属性 stopError の設定により、処理を中止するかを判断します。 222 int rtnCode = ( ( errCode >= ErrorMessage.NG ) && ( stopError ) ) ? SKIP_PAGE : EVAL_PAGE; 223 return( rtnCode ); 224 } 225 226 /** 227 * タグリブオブジェクトをリリースします。 228 * キャッシュされて再利用されるので、フィールドの初期設定を行います。 229 * 230 * @og.rev 2.0.0.4 (2002/09/27) カスタムタグの release() メソッドを、追加 231 * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。 232 * @og.rev 3.5.2.0 (2003/10/20) sysDBType 廃止。SYSARG は、システムパラメータ で定義します。 233 * @og.rev 5.5.5.2 (2012/08/10) isTableModelCommit追加 234 * 235 */ 236 @Override 237 protected void release2() { 238 super.release2(); 239 userDBType = null; 240 selectedAll = false; 241 isTableModelCommit = true; // 5.5.5.2 (2012/08/10) 242 } 243 244 /** 245 * Query を実行します。 246 * 247 * @og.rev 2.1.2.3 (2002/12/02) データベース更新時に、更新フラグをセットするように変更 248 * @og.rev 3.5.0.0 (2003/09/17) カラム名ではなく、カラム番号を先に求めておく方式に変更。 249 * @og.rev 3.5.2.0 (2003/10/20) 内部オブジェクトタイプ名を システムパラメータ で定義します。 250 * @og.rev 3.5.4.2 (2003/12/15) HTMLTableViewForm クラス名変更(⇒ ViewForm_HTMLTable) 251 * @og.rev 3.5.6.0 (2004/06/18) DBRowHeader のパッケージプライベート化に伴なう変更 252 * @og.rev 4.0.0.0 (2005/01/31) setArguments 廃止、Query#execute に、引数をすべて追加 253 * @og.rev 4.3.0.0 (2008/07/22) DBSysArgの引数に日付、PG、ユーザーIDを追加 254 * @og.rev 5.5.5.2 (2012/08/10) isTableModelCommitによるテーブルモデル確定処理のコントロール 255 * 256 * @param query オブジェクト 257 */ 258 @Override 259 protected void execute( final Query query ) { 260 try { 261 if( names == null ) { 262 String errMsg = "names 属性が、設定されていません。" + HybsSystem.CR 263 + sql + HybsSystem.CR ; 264 throw new HybsSystemException( errMsg ); // 3.5.5.4 (2004/04/15) 引数の並び順変更 265 } 266 else { 267 int[] rowNo = getParameterRows(); 268 int rowCount = rowNo.length ; 269 if( rowCount > 0 ) { 270 String[] nameArray = StringUtil.csv2Array( names ); 271 int[] clmNo = getTableColumnNo( nameArray ); // 3.5.0.0 272 273 String curdate = HybsSystem.getDate( "yyyyMMddHHmmss" ); // 4.3.0.0 274 String pgid = getGUIInfoAttri( "KEY" ); // 4.3.0.0 275 String userid = getUser().getAttribute( "ID" ); // 4.3.0.0 276 277 DBSysArg[] sysArg = new DBSysArg[rowCount]; 278 DBUserArg[] userArg = new DBUserArg[rowCount]; 279 for( int i=0; i<rowCount; i++ ) { 280 int row = rowNo[i]; 281 String cdkh = table.getModifyType( row ); 282// sysArg[i] = new DBSysArg( SYSARG,row,cdkh ); // 3.5.2.0 // 4.3.0.0 283 sysArg[i] = new DBSysArg( SYSARG,row,cdkh,curdate,pgid,userid ); 284 String[] values = getTableModelData( clmNo,row ); // 3.5.0.0 285 userArg[i] = new DBUserArg( userDBType,nameArray,values ); 286 } 287 query.execute( names,userDBType + "_ARRAY",sysArg,userArg ); 288 errCode = query.getErrorCode(); 289 errMessage = query.getErrorMessage(); 290 291 if( errCode < ErrorMessage.NG ) { // 異常以外の場合 292 query.commit(); 293 if( isTableModelCommit ) { // 5.5.5.2 (2012/08/10)) 294 for( int j=rowCount-1; j>=0; j-- ) { 295 int row = rowNo[j]; 296 if( DBTableModel.DELETE_TYPE.equals( table.getModifyType( row ) ) ) { 297 table.removeValue( row ); 298 } 299 else { 300 table.resetModify( row ); 301 } 302 } 303 } 304 } 305 else { 306 query.rollback(); 307 } 308 } 309 } 310 } 311 catch( HybsSystemException ex ) { 312 query.rollback(); 313 throw ex; 314 } 315 finally { 316 if( query != null ) { query.close(); } 317 } 318 } 319 320 /** 321 * カラム名配列(String[])より、対応するカラムNo配列(int[])を作成します。 322 * 323 * @og.rev 3.5.0.0 (2003/09/17) 新規追加 324 * 325 * @param nameArray カラム名配列 326 * 327 * @return カラムNo配列 328 */ 329 private int[] getTableColumnNo( final String[] nameArray ) { 330 int[] clmNo = new int[ nameArray.length ]; 331 for( int i=0; i<clmNo.length; i++ ) { 332 clmNo[i] = table.getColumnNo( nameArray[i] ); 333 } 334 return clmNo; 335 } 336 337 /** 338 * 指定の行番号の、カラムNo配列(int[])に対応した値の配列を返します。 339 * 340 * 表示データの HybsSystem.ROW_SEL_KEY を元に、選ばれた 行を 341 * 処理の対象とします。 342 * 343 * @og.rev 3.5.0.0 (2003/09/17) カラム名ではなく、カラム番号を受け取るように修正。 344 * 345 * @param clmNo カラムNo配列 346 * @param row 行番号 347 * 348 * @return 行番号とカラムNo配列に対応した、値の配列 349 */ 350 private String[] getTableModelData( final int[] clmNo,final int row ) { 351 String[] values = new String[ clmNo.length ]; 352 for( int i=0; i<values.length; i++ ) { 353 values[i] = table.getValue( row,clmNo[i] ) ; 354 // NUMBER タイプのキャストエラーを防ぐ為の対応 355 if( values[i] != null && values[i].length() == 0 ) { values[i] = null; } 356 } 357 return values; 358 } 359 360 /** 361 * 表示データの HybsSystem.ROW_SEL_KEY を元に、選ばれた 行を処理の対象とします。 362 * 363 * @og.rev 4.0.0.0 (2005/01/31) getParameterRows() を使用するように変更 364 * 365 * @return 選択行の配列 366 */ 367 @Override 368 protected int[] getParameterRows() { 369 final int[] rowNo ; 370 if( selectedAll ) { 371 int rowCnt = table.getRowCount(); // 3.5.5.7 (2004/05/10) 372 rowNo = new int[ rowCnt ]; 373 for( int i=0; i<rowCnt; i++ ) { 374 rowNo[i] = i; 375 } 376 } else { 377 rowNo = super.getParameterRows(); // 4.0.0 (2005/01/31) 378 } 379 return rowNo ; 380 } 381 382 /** 383 * 【TAG】Queryオブジェクトに渡す引数のタイプ定義(例:type名_ARRAY)。 384 * 385 * @og.tag 386 * ここでは、type 定義のPL/SQL名を指定します。 387 * 行を表す配列は、type名_ARRAY という名称です。 388 * 389 * @param type 定義のPL/SQL名 390 */ 391 public void setDbType( final String type ) { 392 userDBType = getRequestParameter( type ); 393 } 394 395 /** 396 * 【TAG】データを全件選択済みとして処理するかどうか[true/false]を指定します(初期値:false)。 397 * 398 * @og.tag 399 * 全てのデータを選択済みデータとして扱って処理します。 400 * 全件処理する場合に、(true/false)を指定します。 401 * 初期値は false です。 402 * 403 * @param all データを全件選択済み [true:全件選択済み/false:通常] 404 */ 405 public void setSelectedAll( final String all ) { 406 selectedAll = nval( getRequestParameter( all ),selectedAll ); 407 } 408 409 /** 410 * 【TAG】Query を発行する為のクラスIDを指定します({@og.doc03Link queryType 初期値:JDBCPLSQL})。 411 * 412 * @og.tag 413 * 引数指定のINSERT/UPDATE文を実行する場合の、queryType 属性を使用します。 414 * このタグでは、execute( String ,String , DBSysArg[] , DBUserArg[] )を実行します。 415 * 代表的なクラスとして、"JDBCPLSQL" が標準で用意されています。 416 * 417 * タグにより使用できる/出来ないがありますが、これは、org.opengion.hayabusa.db 418 * 以下の Query_**** クラスの **** を与えます。 419 * これらは、Query インターフェースを継承したサブクラスです。 420 * {@og.doc03Link queryType Query_**** クラス} 421 * 422 * @og.rev 3.5.4.2 (2003/12/15) JavaDocコメント用にメソッド追加。 423 * 424 * @param id Query を発行する為の実クラス ID 425 * @see org.opengion.hayabusa.db.Query Queryのサブクラス 426 * @see org.opengion.hayabusa.db.Query#execute( String ,String , DBSysArg[] , DBUserArg[] ) 427 */ 428 @Override 429 public void setQueryType( final String id ) { 430 super.setQueryType( nval( id,"JDBCPLSQL" ) ); 431 } 432 433 /** 434 * 【TAG】テーブルモデルに対する確定処理を行うかどうかを指定します(初期値:true)。 435 * 436 * @og.tag 437 * PlsqlUpdateタグで、エラーがなかった場合は通常、テーブルモデルの改廃に従って処理が行われます。 438 * (改廃Dについては削除処理を行い、その他については改廃を元に戻す) 439 * 440 * このパラメータをfalseに指定すると、テーブルモデルに対する処理を行いません。 441 * これは、例えばPL/SQLでエラーチェックのみを行いたい場合に有効です。 442 * 初期値はtrue(処理を行う)です。 443 * 444 * @og.rev 5.5.5.2 (2012/08/10) 新規作成 445 * 446 * @param flag テーブルモデルに対する処理を行うかどうか 447 */ 448 public void setTableModelCommit( final String flag ) { 449 isTableModelCommit = nval( getRequestParameter( flag ),isTableModelCommit ); 450 } 451 452 /** 453 * このオブジェクトの文字列表現を返します。 454 * 基本的にデバッグ目的に使用します。 455 * 456 * @return このクラスの文字列表現 457 */ 458 @Override 459 public String toString() { 460 return org.opengion.fukurou.util.ToString.title( this.getClass().getName() ) 461 .println( "VERSION" ,VERSION ) 462 .println( "selectedAll" ,selectedAll ) 463 .fixForm().toString() 464 + HybsSystem.CR 465 + super.toString() ; 466 } 467}