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