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.DBConstValue; 021import org.opengion.fukurou.util.StringUtil ; 022import org.opengion.fukurou.util.ToString; // 6.1.1.0 (2015/01/17) 023 024import static org.opengion.fukurou.util.StringUtil.nval ; 025 026import java.util.Map; 027import java.util.LinkedHashMap; 028import java.util.Locale ; // 6.4.1.2 (2016/01/22) 029 030/** 031 * TableUpdateTag にパラメーターを渡す為のタグクラスです。 032 * 033 * 汎用的なデータベース登録処理を行えるタグ tableUpdate タグを新規作成します。 034 * これは、具体的なSLQを作成する tableUpdateParam タグと組み合わせて使用できます。 035 * tableUpdate タグは、queryType に JDBCTableUpdate を指定します。基本的にこれだけ 036 * です。tableUpdateParam では、sqlType に、INSERT,COPY,UPDATE,MODIFY,DELETE の 037 * どれかを指定する事で、SQL文のタイプを指定します。COPY,MODIFY は command と 038 * 関連を持たす為に追加しているタイプで、UPDATE,INSERT と同じ処理を行います。 039 * tableUpdateParam の table には、作成したい SQL のテーブルを指定します。 040 * where 属性は、検索結果の DBTableModel の更新時に使用する条件を指定します。 041 * 042 * @og.formSample 043 * ●形式:<og:tableUpdate command="{@command}" queryType="JDBCTableUpdate" > 044 * <og:tableUpdateParam 045 * sqlType = "{@sqlType}" // INSERT,COPY,UPDATE,MODIFY,DELETE,MERGE 046 * table = "{@TABLE_NAME}" // 処理対象のテーブル名 047 * names = "{@names}" // 処理対象のカラム名 048 * omitNames = "{@omitNames}" // 処理対象外のカラム名 049 * where = "{@where}" // 処理対象を特定するキー 050 * whereNames = "{@whereNames}" // 処理対象を特定するキー条件(where句)をCSV形式 051 * constKeys = "{@constKeys}" // 処理カラム名の中の固定情報カラム名 052 * constVals = "{@constVals}" // 処理カラム名の中の固定情報設定値 053 * asNames = "{@asNames}" // 別名を付けたカラム名(select A as B from TBL の B を指定) 054 * orgNames = "{@orgNames}" // tableの実際のカラム名(select A as B from TBL の A を指定) 055 * funcKeys = "{@funcKeys}" // 関数等を設定するカラム名 056 * funcVals = "{@funcVals}" // 関数等の設定値 057 * logicalDelete = "{@logicalDelete}" // sqlTypeがDELETEの場合にもUPDATE文を発行 058 * /> 059 * </og:tableUpdate> 060 * 061 * ●body:なし 062 * 063 * ●Tag定義: 064 * <og:tableUpdateParam 065 * sqlType ○【TAG】BODY部に書かれている SQLタイプを指定します(必須) 066 * table ○【TAG】処理対象のテーブル名を指定します(必須) 067 * names 【TAG】処理対象のカラム名をCSV形式で複数指定します 068 * omitNames 【TAG】処理対象外のカラム名をCSV形式で複数指定します 069 * where 【TAG】処理対象を特定するキー条件(where句)を指定します 070 * whereNames 【TAG】処理対象を特定するキー条件(where句)をCSV形式で複数指定します 071 * constKeys 【TAG】設定値を固定値と置き換える対象となるカラム名をCSV形式で複数指定します 072 * constVals 【TAG】設定値を固定値と置き換える対象となる設定値をCSV形式で複数指定します 073 * funcKeys 【TAG】関数等を設定するカラム名をCSV形式で複数指定します 074 * funcVals 【TAG】関数等の設定値をCSV形式で複数指定します 075 * asNames 【TAG】別名を付けたカラム名(select A as B from TBL の B を指定)をCSV形式で複数指定します 076 * orgNames 【TAG】tableの実際のカラム名(select A as B from TBL の A を指定)をCSV形式で複数指定します 077 * quotCheck 【TAG】リクエスト情報の シングルクォート(') 存在チェックを実施するかどうか[true/false]を設定します (初期値:USE_SQL_INJECTION_CHECK[=true]) 078 * constObjKey 【TAG】固定情報カラムの処理オブジェクトを特定するキーを設定します(初期値:SYSTEM_ID) 079 * logicalDelete 【TAG】sqlType="DELETE"の場合に論理削除(UPDATE)を行うかどうかを指定します(初期値:false) 080 * caseKey 【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null) 081 * caseVal 【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null) 082 * caseNN 【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない) 083 * caseNull 【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない) 084 * caseIf 【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない) 085 * debug 【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false) 086 * /> 087 * 088 * ●使用例 089 * ・【entry.jsp】 090 * <og:tableUpdate command="{@command}" queryType="JDBCTableUpdate" > 091 * <og:tableUpdateParam 092 * sqlType = "{@sqlType}" 093 * table = "{@MEM.TABLE_NAME}" 094 * where = "ROWID = [ROWID]" 095 * /> 096 * </og:tableUpdate> 097 * 098 * @og.rev 3.8.8.0 (2007/12/22) 新規作成 099 * @og.rev 4.1.2.0 (2008/03/12) 実装の大幅な修正 100 * @og.rev 7.2.9.1 (2020/10/23) TableUpdateParamTag のマージ(UPDATE,INSERT)対応 101 * @og.group DB登録 102 * 103 * @version 4.0 104 * @author Kazuhiko Hasegawa 105 * @since JDK5.0, 106 */ 107public class TableUpdateParamTag extends CommonTagSupport { 108 /** このプログラムのVERSION文字列を設定します。 {@value} */ 109 private static final String VERSION = "7.2.9.1 (2020/10/23)" ; 110 private static final long serialVersionUID = 729120201023L ; 111 112 /** sqlType属性に設定できる値 {@value} */ 113// public static final String SQL_TYPE = "|INSERT|COPY|UPDATE|MODIFY|DELETE|" ; 114 public static final String SQL_TYPE = "|INSERT|COPY|UPDATE|MODIFY|DELETE|MERGE|" ; // 7.2.9.1 (2020/10/23) 115 116 // 3.8.0.4 (2005/08/08) 印刷時に使用するシステムID 117 private static final String SYSTEM_ID =HybsSystem.sys( "SYSTEM_ID" ); 118 119 // 4.3.6.0 (2009/05/01) デフォルトで利用するconstObjのシステムリソース名 120 private static final String DEFAULT_CONST_OBJ = HybsSystem.sys( "DEFAULT_CONST_CLASS" ); 121 122 private String sqlType ; // INSERT,COPY,UPDATE,MODIFY,DELETE,MERGE 123 private String table ; // 処理対象のテーブル名 124 private String[] names ; // 処理対象のカラム名 125 private String omitNames = ",ROWID,ROWNUM,WRITABLE,"; // 処理対象外のカラム名 126 private String where ; // 処理対象を特定するキー 127 private String whereNames ; // 5.5.8.5 (2012/11/27) 処理対象を特定するCSV形式のカラム名 128 private String[] constKeys ; // 処理カラム名の中の固定情報カラム名 129 private String[] constVals ; // 処理カラム名の中の固定情報設定値 130 private String[] funcKeys ; // 5.5.1.9 (2012/04/19) 関数等を設定するカラム名 131 private String[] funcVals ; // 5.5.1.9 (2012/04/19) 関数等の設定値 132 private String[] asNames ; // 5.5.1.9 (2012/04/19) 別名を付けたカラム名(select A as B from TBL の B を指定) 133 private String[] orgNames ; // 5.5.1.9 (2012/04/19) tableの実際のカラム名(select A as B from TBL の A を指定) 134 private String constObjKey = SYSTEM_ID; // 固定情報カラムの処理オブジェクトを特定するキー 135 private boolean quotCheck = HybsSystem.sysBool( "USE_SQL_INJECTION_CHECK" ); 136 private boolean logicalDelete; // 4.3.7.0 (2009/06/01) sqlTypeがDELETEの場合にもUPDATE文を発行 137 138 /** 139 * デフォルトコンストラクター 140 * 141 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor. 142 */ 143 public TableUpdateParamTag() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 144 145 /** 146 * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。 147 * 148 * @og.rev 5.5.1.9 (2012/04/19) エラーチェックを先に行います。 149 * @og.rev 6.3.4.0 (2015/08/01) caseKey,caseVal,caseNN,caseNull,caseIf 属性対応 150 * @og.rev 7.0.1.0 (2018/10/15) XHTML → HTML5 対応(空要素の、"/>" 止めを、">" に変更します)。 151 * 152 * @return 後続処理の指示( SKIP_BODY ) 153 */ 154 @Override 155 public int doStartTag() { 156 if( !useTag() ) { return SKIP_BODY ; } // 6.3.4.0 (2015/08/01) 157 158 // constKeys,constVals の個数チェック 159 if( constKeys != null ) { 160 if( constVals == null || constKeys.length != constVals.length ) { 161// final String errMsg = "<b>constKeys と、constVals の個数が異なります。</b><br />" 162 final String errMsg = "<b>constKeys と、constVals の個数が異なります。</b><br>" // 7.0.1.0 (2018/10/15) 163 + " constKeys=[" + StringUtil.array2csv( constKeys ) + "]" 164 + " constVals=[" + StringUtil.array2csv( constVals ) + "]" ; 165 throw new HybsSystemException( errMsg ); 166 } 167 } 168 169 // funcKeys,funcVals の個数チェック 170 if( funcKeys != null ) { 171 if( funcVals == null || funcKeys.length != funcVals.length ) { 172// final String errMsg = "<b>funcKeys と、funcVals の個数が異なります。</b><br />" 173 final String errMsg = "<b>funcKeys と、funcVals の個数が異なります。</b><br>" // 7.0.1.0 (2018/10/15) 174 + " funcKeys=[" + StringUtil.array2csv( funcKeys ) + "]" 175 + " funcVals=[" + StringUtil.array2csv( funcVals ) + "]" ; 176 throw new HybsSystemException( errMsg ); 177 } 178 } 179 180 // asNames,orgNames の個数チェック 181 if( orgNames != null ) { 182 if( asNames == null || orgNames.length != asNames.length ) { 183// final String errMsg = "<b>orgNames と、asNames の個数が異なります。</b><br />" 184 final String errMsg = "<b>orgNames と、asNames の個数が異なります。</b><br>" // 7.0.1.0 (2018/10/15) 185 + " orgNames=[" + StringUtil.array2csv( orgNames ) + "]" 186 + " asNames=[" + StringUtil.array2csv( asNames ) + "]" ; 187 throw new HybsSystemException( errMsg ); 188 } 189 } 190 191 return SKIP_BODY ; // Body を評価しない 192 } 193 194 /** 195 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。 196 * 197 * @og.rev 4.3.7.0 (2009/06/01) 論理削除対応 198 * @og.rev 6.3.4.0 (2015/08/01) caseKey,caseVal,caseNN,caseNull,caseIf 属性対応 199 * @og.rev 7.2.9.1 (2020/10/23) TableUpdateParamTag のマージ(UPDATE,INSERT)対応 200 * 201 * @return 後続処理の指示 202 */ 203 @Override 204 public int doEndTag() { 205 debugPrint(); 206// if( !useTag() ) { return EVAL_PAGE ; } // 6.3.4.0 (2015/08/01) 207 if( !useTag() || sqlType == null ) { return EVAL_PAGE ; } // 7.2.9.1 (2020/10/23) sqlType == null の時は、何もしない。 208 209 final TableUpdateTag updateTag = (TableUpdateTag)findAncestorWithClass( this,TableUpdateTag.class ); 210 if( updateTag == null ) { 211 final String errMsg = "<b>" + getTagName() + "タグは、TableUpdateTagの内側(要素)に記述してください。</b>"; 212 throw new HybsSystemException( errMsg ); 213 } 214 215 final String upSqlType = updateTag.getSqlType() ; 216 217 // 7.2.9.1 (2020/10/23) TableUpdateParamTag のマージ(UPDATE,INSERT)対応 218 final boolean useInsert = "INSERT,COPY,MERGE".contains( sqlType ) ; 219 final boolean useUpdate = "UPDATE,MODIFY,MERGE".contains( sqlType ) || "DELETE".contains( sqlType ) && logicalDelete ; 220 final boolean useDelete = "DELETE".equals( sqlType ) && !logicalDelete ; 221 222 if( upSqlType == null || upSqlType.equals( sqlType ) ) { 223 // 通常の names カラム配列を設定します。 224 if( names == null ) { names = updateTag.getNames(); } 225 final NamesData namesData = makeNamesData( names ); 226 227 if( useInsert ) { 228 updateTag.setQuery( "INSERT",getInsertSQL( namesData ) ); 229 } 230 if( useUpdate ) { 231 updateTag.setQuery( "UPDATE",getUpdateSQL( namesData ) ); 232 } 233 if( useDelete ) { 234 updateTag.setQuery( "DELETE",getDeleteSQL() ); 235 } 236 } 237 238// // 7.2.9.1 (2020/10/23) TableUpdateParamTag のマージ(UPDATE,INSERT)対応 239// if( upSqlType == null || upSqlType.equals( sqlType ) || ) { 240// String query = null; 241// if( "INSERT".equalsIgnoreCase( sqlType ) || "COPY".equalsIgnoreCase( sqlType ) ) { 242// query = getInsertSQL( namesData ); 243// } 244// else if( "UPDATE".equalsIgnoreCase( sqlType ) || "MODIFY".equalsIgnoreCase( sqlType ) 245// || ( "DELETE".equalsIgnoreCase( sqlType ) && logicalDelete ) ) { // 4.3.7.0 (2009/06/01) 246// query = getUpdateSQL( namesData ); 247// } 248// else if( "DELETE".equalsIgnoreCase( sqlType ) ) { 249// query = getDeleteSQL(); 250// } 251// 252// jspPrint( query ); 253// } 254 255 return EVAL_PAGE ; 256 } 257 258 /** 259 * タグリブオブジェクトをリリースします。 260 * キャッシュされて再利用されるので、フィールドの初期設定を行います。 261 * 262 * @og.rev 4.3.7.0 (2009/06/01) logicalDelete属性追加 263 * @og.rev 5.5.1.9 (2012/04/19) asNames、orgNames、funcKeys、funcVals属性追加 264 * @og.rev 5.5.8.5 (2012/11/27) 処理対象を特定するCSV形式のカラム名 265 */ 266 @Override 267 protected void release2() { 268 super.release2(); // 3.5.6.0 (2004/06/18) 追加(抜けていた) 269 sqlType = null; // INSERT,COPY,UPDATE,MODIFY,DELETE,MERGE 270 table = null; // 処理対象のテーブル名 271 names = null; // 処理対象のカラム名 272 omitNames = ",ROWID,ROWNUM,WRITABLE,"; // 処理対象外のカラム名 273 where = null; // 処理対象を特定するキー 274 whereNames = null; // 5.5.8.5 (2012/11/27) 処理対象を特定するCSV形式のカラム名 275 constKeys = null; // 処理カラム名の中の固定情報カラム名 276 constVals = null; // 処理カラム名の中の固定情報設定値 277 quotCheck = HybsSystem.sysBool( "USE_SQL_INJECTION_CHECK" ); 278 constObjKey = SYSTEM_ID; // 固定情報カラムの処理オブジェクトを特定するキー 279 logicalDelete = false; // 4.3.7.0 (2009/06/01) 280 funcKeys = null; // 5.5.1.9 (2012/04/19) 関数等を設定するカラム名 281 funcVals = null; // 5.5.1.9 (2012/04/19) 関数等の設定値 282 asNames = null; // 5.5.1.9 (2012/04/19) 別名を付けたカラム名(select A as B from TBL の B を指定) 283 orgNames = null; // 5.5.1.9 (2012/04/19) tableの実際のカラム名(select A as B from TBL の A を指定) 284 } 285 286 /** 287 * 【TAG】BODY部に書かれている SQLタイプを指定します。 288 * 289 * @og.tag 290 * SQLタイプは、INSERT,COPY,UPDATE,MODIFY,DELETE,MERGE の中から指定する 291 * 必要があります。これらは、内部に書かれるSQLの形式を指定するのに使用します。 292 * 内部処理は、DBTableModelの改廃コード(A,C,D)に対して使用される 293 * SQL を選択する場合の情報に使用されます。 294 * なお、COPY と MODIFY は、command で指定できる簡易機能として用意しています。 295 * 上位の TableUpdateTag の sqlType 属性 と同じsqlType 属性の場合のみ、SQL文を 296 * 合成・出力します。(上位のsqlTypeがnullの場合は、無条件実行します。) 297 * 指定のタイプが、異なる場合は、なにも処理を行いません。 298 * 299 * @param type SQLタイプ [INSERT/COPY/UPDATE/MODIFY/DELETE/MERGE] 300 */ 301 public void setSqlType( final String type ) { 302 sqlType = nval( getRequestParameter( type ),sqlType ); 303 if( sqlType != null && SQL_TYPE.indexOf( "|" + sqlType + "|" ) < 0 ) { 304 sqlType = null; 305 } 306 } 307 308 /** 309 * 【TAG】処理対象のテーブル名を指定します。 310 * 311 * @og.tag 312 * テーブル名を指定することで、sqlTypeに応じた QUERYを生成することが出来ます。 313 * 生成する場合のカラムを特定する場合は、names 属性で指定できます。 314 * また、WHERE条件は、where属性で指定します。 315 * 316 * @param tbl テーブル名 317 * @see #setNames( String ) 318 * @see #setWhere( String ) 319 * @see #setSqlType( String ) 320 */ 321 public void setTable( final String tbl ) { 322 table = nval( getRequestParameter( tbl ),table ); 323 } 324 325 /** 326 * 【TAG】処理対象のカラム名をCSV形式で複数指定します。 327 * 328 * @og.tag 329 * 生成するQUERYのカラム名をCSV形式(CSV)で複数指定します。 330 * 指定がない場合は、DBTableModel の全カラム(※)を使用して、QUERYを構築します。 331 * 一般に、テーブル結合してDBTableModelを構築した場合は、登録すべきカラムを 332 * 指定する必要があります。 333 * (※)正確には、DBTableModel の全カラムのうち、ROWID,ROWNUM,WRITABLE カラムは 334 * 無視します。 335 * 分解方法は、通常のパラメータ取得後に、CSV分解します。 336 * 337 * @og.rev 3.8.8.5 (2007/03/09) 通常のパラメータ取得後に、CSV分解に戻します。 338 * 339 * @param nms カラム名 (CSV形式) 340 * @see #setTable( String ) 341 * @see #setOmitNames( String ) 342 */ 343 public void setNames( final String nms ) { 344 names = StringUtil.csv2Array( getRequestParameter( nms ) ); 345 if( names.length == 0 ) { names = null; } 346 } 347 348 /** 349 * 【TAG】処理対象外のカラム名をCSV形式で複数指定します。 350 * 351 * @og.tag 352 * 生成するQUERYのカラム名に指定しないカラム名をCSV形式(CSV)で複数指定します。 353 * 指定がない場合は、DBTableModel の全カラム(※)を使用して、QUERYを構築します。 354 * テーブル結合などで、処理したくないカラム数の方が少ない場合に、names ですべてを 355 * 指定するより少ない記述ですみます。 356 * (※)正確には、DBTableModel の全カラムのうち、ROWID,ROWNUM,WRITABLE カラムは 357 * 無視します。 358 * 359 * @param nms カラム名 (CSV形式) 360 * @see #setTable( String ) 361 * @see #setNames( String ) 362 */ 363 public void setOmitNames( final String nms ) { 364 omitNames = omitNames + nval( getRequestParameter( nms ),"" ) + ","; 365 } 366 367 /** 368 * 【TAG】処理対象を特定するキー条件(where句)を指定します。 369 * 370 * @og.tag 371 * 生成するQUERYのwhere 句を指定します。通常の WHERE 句の書き方と同じで、 372 * DBTableModelの値を割り当てたい箇所に[カラム名] を記述します。 373 * 文字列の場合、設定値をセットするときに、シングルコーテーションを 374 * 使用しますが、[カラム名]で指定する場合は、その前後に、(')シングル 375 * コーテーションは、不要です。 376 * {@XXXX}変数を使用する場合は、パース時に固定文字に置き換えられる為、 377 * 文字列指定時の(')シングルコーテーションが必要になります。 378 * ※ 5.5.8.5 (2012/11/27) whereNames 属性と併用した場合は、where が、and を付けて、文字列結合されます。 379 * 例:FGJ='1' and CLM=[CLM] and SYSTEM_ID in ([SYSID],'**') and KBSAKU='{@KBSAKU}' 380 * 381 * @param wr 検索条件 (where句) 382 */ 383 public void setWhere( final String wr ) { 384 where = nval( getRequestParameter( wr ),where ); 385 } 386 387 /** 388 * 【TAG】処理対象を特定するキー条件(where句)をCSV形式で複数指定します。 389 * 390 * @og.tag 391 * 生成するQUERYのwhere 句を指定する方法として、複数のカラム名をCSV指定し、内部で 392 * KEY=[KEY] 文字列を作成します。 393 * ここでは、カラム名は、データベースのカラム名と同じで、かつ、DBTableModel にも 394 * 同じカラムのデータが存在していること、という条件付きとします。 395 * また、where 条件との併用を行いますが、こちらの条件が先に使用され、where 条件は、 396 * and を付けて、文字列結合されます。 397 * 例: CLM,SYSTEM_ID,KBSAKU ⇒ CLM=[CLM] and SYSTEM_ID=[SYSTEM_ID] and KBSAKU=[KBSAKU] 398 * 399 * @og.rev 5.5.8.5 (2012/11/27) 新規追加 400 * 401 * @param wrnm 検索条件カラム (where句)作成のためのカラム名(CSV形式) 402 */ 403 public void setWhereNames( final String wrnm ) { 404 whereNames = nval( getRequestParameter( wrnm ),whereNames ); 405 } 406 407 /** 408 * 【TAG】設定値を固定値と置き換える対象となるカラム名をCSV形式で複数指定します。 409 * 410 * @og.tag 411 * names 属性のカラムや table 属性より、QUERYを作成して、DBTableModelの値を 412 * 割り当てる場合、DBTableModelの値ではなく、外部から指定した固定値を 413 * 割り当てたい場合に、そのカラム名をCSV形式(CSV)で複数指定します。 414 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。 415 * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。 416 * 417 * @param keys 固定値カラム (CSV形式) 418 * @see #setConstVals( String ) 419 */ 420 public void setConstKeys( final String keys ) { 421 constKeys = getCSVParameter( keys ); 422 } 423 424 /** 425 * 【TAG】設定値を固定値と置き換える対象となる設定値をCSV形式で複数指定します。 426 * 427 * @og.tag 428 * names 属性のカラムや table 属性より、QUERYを作成して、DBTableModelの 429 * 値を割り当てる場合、DBTableModelの値ではなく、外部から指定した固定値を 430 * 割り当てたい場合に、そのカラム名に対応する設定値をCSV形式(CSV)で 431 * 複数指定します。ここで指定する設定値は、constKeys 属性と対応させます。 432 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。 433 * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。 434 * 435 * @param vals 設定値(CSV形式) 436 * @see #setConstKeys( String ) 437 */ 438 public void setConstVals( final String vals ) { 439 constVals = getCSVParameter( vals ); 440 } 441 442 /** 443 * 【TAG】関数等を設定するカラム名をCSV形式で複数指定します。 444 * 445 * @og.tag 446 * constVals 属性で設定する値は、必ずシングルクオートが付与されます。 447 * その場合、関数などを設定したい場合でも、文字列として設定しようとします。 448 * ここで指定するカラム名(funcKeys)自身は、constKeys と同じ書式です。 449 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。 450 * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。 451 * 452 * @og.rev 5.5.1.9 (2012/04/19) 新規追加 453 * 454 * @param keys 関数カラム (CSV形式) 455 * @see #setFuncVals( String ) 456 */ 457 public void setFuncKeys( final String keys ) { 458 funcKeys = getCSVParameter( keys ); 459 } 460 461 /** 462 * 【TAG】関数等の設定値をCSV形式で複数指定します。 463 * 464 * @og.tag 465 * funcKeys 属性に対応する 関数などの設定値を割り当てます。 466 * constVals 属性との違いは、funcVals の設定値は、そのままの形で、SQL文の 467 * 構築に使われます。 468 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。 469 * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。 470 * 471 * @og.rev 5.5.1.9 (2012/04/19) 新規追加 472 * 473 * @param vals 関数設定値 (CSV形式) 474 * @see #setFuncKeys( String ) 475 */ 476 public void setFuncVals( final String vals ) { 477 funcVals = getCSVParameter( vals ); 478 } 479 480 /** 481 * 【TAG】別名を付けたカラム名(select A as B from TBL の B を指定)をCSV形式で複数指定します。 482 * 483 * @og.tag 484 * SELECT 文を記述したとき、別名を付けていたり、SELECTしたテーブルと別のテーブルに 485 * DBTableModelの値を書き込む場合、DBTableModel の持っているカラム名と、実際に 486 * 書き込むカラム名が異なります。そのようなケースに、元の別名カラムを指定します。 487 * orgNames属性の並び順と、asNames属性の並び順を合わせておく必要があります。 488 * このカラム名は、DBTableModel には持っているが、テーブル側には持っていない値 489 * なので、内部的に omitNames 属性に値を設定します。利用者は、omitNames に 490 * 書き込む必要はありません。 491 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。 492 * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。 493 * 494 * @og.rev 5.5.1.9 (2012/04/19) 新規追加 495 * 496 * @param keys 別名カラム (CSV形式) 497 * @see #setOrgNames( String ) 498 */ 499 public void setAsNames( final String keys ) { 500 asNames = getCSVParameter( keys ); 501 } 502 503 /** 504 * 【TAG】tableの実際のカラム名(select A as B from TBL の A を指定)をCSV形式で複数指定します。 505 * 506 * @og.tag 507 * SELECT 文を記述したとき、別名を付けていたり、SELECTしたテーブルと別のテーブルに 508 * DBTableModelの値を書き込む場合、DBTableModel の持っているカラム名と、実際に 509 * 書き込むカラム名が異なります。そのようなケースに、テーブルの実カラムを指定します。 510 * orgNames属性の並び順と、asNames属性の並び順を合わせておく必要があります。 511 * このカラム名は、DBTableModel には持っていませんが、テーブル側には持っている値 512 * なので、このカラム名で、SQL文を構築します。 UPDATE TBL SET A=[B] WHERE … となります。 513 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。 514 * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。 515 * 516 * @og.rev 5.5.1.9 (2012/04/19) 新規追加 517 * 518 * @param keys 実カラム (CSV形式) 519 * @see #setAsNames( String ) 520 */ 521 public void setOrgNames( final String keys ) { 522 orgNames = getCSVParameter( keys ); 523 } 524 525 /** 526 * 【TAG】リクエスト情報の シングルクォート(') 存在チェックを実施するかどうか[true/false]を設定します 527 * (初期値:USE_SQL_INJECTION_CHECK[={@og.value SystemData#USE_SQL_INJECTION_CHECK}])。 528 * 529 * @og.tag 530 * SQLインジェクション対策の一つとして、暫定的ではありますが、SQLのパラメータに 531 * 渡す文字列にシングルクォート(') を許さない設定にすれば、ある程度は防止できます。 532 * 数字タイプの引数には、 or 5=5 などのシングルクォートを使用しないコードを埋めても、 533 * 数字チェックで検出可能です。文字タイプの場合は、必ず (')をはずして、 534 * ' or 'A' like 'A のような形式になる為、(')チェックだけでも有効です。 535 * (') が含まれていたエラーにする(true)/かノーチェックか(false)を指定します。 536 * (初期値:システム定数のUSE_SQL_INJECTION_CHECK[={@og.value SystemData#USE_SQL_INJECTION_CHECK}])。 537 * 538 * @param flag クォートチェック [true:する/それ以外:しない] 539 * @see org.opengion.hayabusa.common.SystemData#USE_SQL_INJECTION_CHECK 540 */ 541 public void setQuotCheck( final String flag ) { 542 quotCheck = nval( getRequestParameter( flag ),quotCheck ); 543 } 544 545 /** 546 * 【TAG】固定情報カラムの処理オブジェクトを特定するキーを設定します(初期値:SYSTEM_ID)。 547 * 548 * @og.tag 549 * 固定情報カラム をシステム単位にJavaクラスで管理できます。 550 * そのクラスオブジェクトは、org.opengion.hayabusa.db.DBConstValue インターフェースを 551 * 継承した、plugin クラスになります。 552 * そのクラスを特定するキーワードを指定します。 553 * 初期値は、SYSTEM_ID でシステム単位にクラスを作成します。 554 * もし、他のシステムと共通の場合は、継承だけさせることも可能です。 555 * 対応したDBConstValueクラスがプラグインとして存在しない場合は、 556 * システムリソースのDEFAULT_CONST_CLASSで指定されたクラスが利用されます。 557 * 固定情報カラムを使用しない場合は、constObjKey="" をセットしてください。 558 * 559 * 初期値は、SYSTEM_ID です。 560 * 561 * @og.rev 6.9.8.0 (2018/05/28) 固定情報カラムを使用しない場合は、constObjKey="" をセット。 562 * 563 * @param key 固定カラムキー 564 */ 565 public void setConstObjKey( final String key ) { 566// constObjKey = nval( getRequestParameter( key ),constObjKey ); 567 final String objKey = getRequestParameter( key ); 568 if( objKey != null ) { constObjKey = objKey; } 569 } 570 571 /** 572 * 【TAG】sqlType="DELETE"の場合に論理削除(UPDATE)を行うかどうかを指定します(初期値:false)。 573 * 574 * @og.tag 575 * sqlType="DELETE"の場合に論理削除(UPDATE)を行うかどうかを指定します。 576 * trueが指定された場合は、DELETE文ではなく、UPDATE文が発行されます。 577 * falseが指定された場合は、DELETE文が発行されます。 578 * さらに論理削除を行う場合、org.opengion.hayabusa.db.DBConstValue インターフェースに 579 * 定義されている、getLogicalDeleteKeys()及びgetLogicalDeleteValsを実装することで、 580 * 論理削除する際のフラグの更新方法を統一的に管理することが可能になります。 581 * 初期値は、false(物理削除する)です 582 * 583 * @param flag 論理削除可否 [true:UPDATE文/false:DELETE文] 584 */ 585 public void setLogicalDelete( final String flag ) { 586 logicalDelete = nval( getRequestParameter( flag ),logicalDelete ); 587 } 588 589 /** 590 * データをインサートする場合に使用するSQL文を作成します。 591 * 592 * @og.rev 4.1.2.1 (2008/03/17) DBConstValue による固定値セットを採用 593 * @og.rev 4.3.6.4 (2009/05/01) デフォルト設定をシステムリソースで設定可能にする 594 * @og.rev 5.3.4.0 (2011/04/01) DEFAULT_CONST_OBJの初期値変更(null→ゼロ文字列) 595 * @og.rev 6.2.3.0 (2015/05/01) CSV形式の作成を、String#join( CharSequence , CharSequence... )を使用。 596 * 597 * @param namesData NamesDataオブジェクト 598 * 599 * @return インサートSQL 600 * @og.rtnNotNull 601 */ 602 private String getInsertSQL( final NamesData namesData ) { 603 String cls = HybsSystem.sys( "DBConstValue_" + constObjKey ) ; 604 605 // 4.3.6.4 (2009/05/01) 標準の追加 606 if( cls == null){ 607 cls = DEFAULT_CONST_OBJ; 608 } 609 610 if( cls != null && !cls.isEmpty() ) { 611 final DBConstValue constVal = HybsSystem.newInstance( cls ); 612 // 4.2.1.0 (2008/04/16) 初期化追加 613 constVal.init( table,getUser().getUserID(),getGUIInfoAttri( "KEY" ) ); 614 final String[] keys = constVal.getInsertKeys(); 615 final String[] vals = constVal.getInsertVals(); 616 namesData.add( keys,vals ); 617 } 618 619 final String[] nms = namesData.getNames(); 620 final String[] vls = namesData.getVals(); 621 622 // 6.2.3.0 (2015/05/01) CSV形式の作成を、String#join( CharSequence , CharSequence... )を使用。 623 final StringBuilder sql = new StringBuilder( BUFFER_MIDDLE ) 624 .append( "INSERT INTO " ).append( table ) 625 .append( " ( " ) 626 .append( String.join( "," , nms ) ) // 6.2.3.0 (2015/05/01) 627 .append( " ) VALUES ( " ) 628 .append( String.join( "," , vls ) ) // 6.2.3.0 (2015/05/01) 629 .append( " )" ); 630 631 return sql.toString(); 632 } 633 634 /** 635 * データをアップデートする場合に使用するSQL文を作成します。 636 * 637 * where と whereNames が同時に指定された場合は、whereNames が先に処理され 638 * where 条件は、and 結合されます。 639 * 640 * @og.rev 4.1.2.1 (2008/03/17) DBConstValue による固定値セットを採用 641 * @og.rev 4.3.6.4 (2009/05/01) デフォルト設定をシステムリソースで設定可能にする 642 * @og.rev 4.3.7.0 (2009/06/01) 論理削除対応 643 * @og.rev 5.3.7.0 (2011/07/01) DEFAULT_CONST_OBJの初期値変更(null→ゼロ文字列) 対応忘れ 644 * @og.rev 5.5.8.5 (2012/11/27) whereNames 対応 645 * 646 * @param namesData NamesDataオブジェクト 647 * 648 * @return アップデートSQL 649 * @og.rtnNotNull 650 */ 651 private String getUpdateSQL( final NamesData namesData ) { 652 String cls = HybsSystem.sys( "DBConstValue_" + constObjKey ) ; 653 654 // 4.3.6.4 (2009/05/01) 標準の追加 655 if( cls == null){ 656 cls = DEFAULT_CONST_OBJ; 657 } 658 659 if( cls != null && !cls.isEmpty() ) { // 5.3.7.0 (2011/07/01) 660 final DBConstValue constVal = HybsSystem.newInstance( cls ); 661 // 4.2.1.0 (2008/04/16) 初期化追加 662 constVal.init( table,getUser().getUserID(),getGUIInfoAttri( "KEY" ) ); 663 // 4.3.7.0 (2009/06/01) 論理削除対応 664 String[] keys = null; 665 String[] vals = null; 666 if( "DELETE".equalsIgnoreCase( sqlType ) ) { 667 keys = constVal.getLogicalDeleteKeys(); 668 vals = constVal.getLogicalDeleteVals(); 669 } 670 else { 671 keys = constVal.getUpdateKeys(); 672 vals = constVal.getUpdateVals(); 673 } 674 namesData.add( keys,vals ); 675 } 676 677 final String[] nms = namesData.getNames(); 678 final String[] vls = namesData.getVals(); 679 680 final StringBuilder sql = new StringBuilder( BUFFER_MIDDLE ) 681 .append( "UPDATE " ).append( table ).append( " SET " ) 682 .append( nms[0] ).append( '=' ).append( vls[0] ); // 6.0.2.5 (2014/10/31) char を append する。 683 684 for( int i=1; i<nms.length; i++ ) { 685 sql.append( ',' ).append( nms[i] ).append( '=' ).append( vls[i] ); // 6.0.2.5 (2014/10/31) char を append する。 686 } 687 688 // 5.5.8.5 (2012/11/27) whereNames 対応 689 String whereAnd = " WHERE " ; 690 if( whereNames != null && whereNames.length() > 0 ) { 691 final String[] wnms = whereNames.split(","); 692 sql.append( whereAnd ).append( wnms[0] ).append( "=[" ).append( wnms[0] ).append( ']' ); // 6.0.2.5 (2014/10/31) char を append する。 693 694 for( int i=1; i<wnms.length; i++ ) { 695 sql.append( " AND " ).append( wnms[i] ).append( "=[" ).append( wnms[i] ).append( ']' ); // 6.0.2.5 (2014/10/31) char を append する。 696 } 697 whereAnd = " AND " ; // whereNames 優先。ここを通らなければ、初期値のまま、" WHERE " が使われる 698 } 699 700 // 5.5.8.5 (2012/11/27) whereNames 対応。whereNames が登録されていれば、AND で繋げる。 701 if( where != null && where.length() > 0 ) { 702 sql.append( whereAnd ).append( where ); 703 } 704 705 return sql.toString(); 706 } 707 708 /** 709 * データをデリートする場合に使用するSQL文を作成します。 710 * 711 * where と whereNames が同時に指定された場合は、whereNames が先に処理され 712 * where 条件は、and 結合されます。 713 * 714 * @og.rev 5.5.8.5 (2012/11/27) whereNames 対応 715 * 716 * @return デリートSQL 717 * @og.rtnNotNull 718 */ 719 private String getDeleteSQL() { 720 final StringBuilder sql = new StringBuilder( BUFFER_MIDDLE ); 721 sql.append( "DELETE FROM " ).append( table ); 722 723 // 5.5.8.5 (2012/11/27) whereNames 対応 724 String whereAnd = " WHERE " ; 725 if( whereNames != null && whereNames.length() > 0 ) { 726 final String[] wnms = whereNames.split(","); 727 sql.append( whereAnd ).append( wnms[0] ).append( "=[" ).append( wnms[0] ).append( ']' ); // 6.0.2.5 (2014/10/31) char を append する。 728 729 for( int i=1; i<wnms.length; i++ ) { 730 sql.append( " AND " ).append( wnms[i] ).append( "=[" ).append( wnms[i] ).append( ']' ); // 6.0.2.5 (2014/10/31) char を append する。 731 } 732 whereAnd = " AND " ; // whereNames 優先。ここを通らなければ、初期値のまま、" WHERE " が使われる 733 } 734 735 // 5.5.8.5 (2012/11/27) whereNames 対応。whereNames が登録されていれば、AND で繋げる。 736 if( where != null && where.length() > 0 ) { 737 sql.append( whereAnd ).append( where ); 738 } 739 return sql.toString(); 740 } 741 742 /** 743 * names,constKeys,omitNames から、必要なキー情報と、属性情報を持った NamesData を作成します。 744 * 745 * @og.rev 4.1.2.1 (2008/03/17) 固定値の constVals の前後に、"'" を入れる。 746 * @og.rev 5.5.1.9 (2012/04/19) asNames、orgNames、funcKeys、funcVals属性追加 747 * 748 * @param nms カラム名配列(可変長引数) 749 * 750 * @return 属性情報を持ったNamesData 751 */ 752 private NamesData makeNamesData( final String... nms ) { 753 final NamesData namesData = new NamesData(); 754 755 // 5.5.1.9 (2012/04/19) omitNames に、asNames配列の値を設定しておきます。 756 if( asNames != null ) { 757 for( int i=0; i<asNames.length; i++ ) { 758 if( asNames[i] != null && asNames[i].length() > 0 ) { 759 omitNames = omitNames + asNames[i] + ","; 760 } 761 } 762 } 763 764 // names で指定されたカラム名 765 for( int i=0; i<nms.length; i++ ) { 766 final String nm = nms[i]; 767 if( nm != null && nm.length() > 0 && omitNames.indexOf( "," + nm + "," ) < 0 ) { 768 namesData.add( nm,"[" + nm + "]" ) ; 769 } 770 } 771 772 // 固定値の constKeys カラム配列を設定します。 773 if( constKeys != null && constKeys.length > 0 ) { 774 for( int j=0; j<constKeys.length; j++ ) { 775 final String nm = constKeys[j]; 776 if( nm != null && nm.length() > 0 ) { 777 namesData.add( nm,"'" + constVals[j] + "'" ) ; // constVals は、シングルクオートで囲います。 778 } 779 } 780 } 781 782 // 関数値の funcKeys カラム配列を設定します。 783 if( funcKeys != null && funcKeys.length > 0 ) { 784 for( int j=0; j<funcKeys.length; j++ ) { 785 final String nm = funcKeys[j]; 786 if( nm != null && nm.length() > 0 ) { 787 namesData.add( nm, funcVals[j] ) ; // funcVals は、シングルクオートで囲いません。 788 } 789 } 790 } 791 792 // 別名の asNames,orgNames カラム配列を設定します。 793 if( orgNames != null && orgNames.length > 0 ) { 794 for( int j=0; j<orgNames.length; j++ ) { 795 final String onm = orgNames[j]; 796 if( onm != null && onm.length() > 0 ) { 797 namesData.add( onm,"[" + asNames[j] + "]" ) ; 798 } 799 } 800 } 801 802 return namesData ; 803 } 804 805 /** 806 * 内部データを受け渡す為の、簡易クラスです。 807 * 更新するカラム名と値のセット配列を管理しています。 808 * 809 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更。 810 * ※ classのfinal化と変数のprivate化、メソッドの修飾子なし(パッケージプライベート)化を行います。 811 * @og.rev 6.4.1.2 (2016/01/22) nameの値を、大文字小文字の区別をなくすために、常に大文字で登録します。 812 */ 813 private static final class NamesData { 814 private final Map<String,String> nameMap = new LinkedHashMap<>() ; 815 816 /** 817 * キーと値のセットを追加します。 818 * 819 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更。 820 * @og.rev 6.4.1.2 (2016/01/22) nameの値を、大文字小文字の区別をなくすために、常に大文字で登録します。。 821 * 822 * @param nm キー(大文字のみ。内部で変換しておきます。) 823 * @param val 値 824 */ 825 /* default */ void add( final String nm,final String val ) { 826 nameMap.put( nm.toUpperCase(Locale.JAPAN),val ); 827 } 828 829 /** 830 * キー配列と対応する、値配列のセットを追加します。 831 * 832 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更。 833 * 834 * @param nms キー配列 835 * @param vals 値配列 836 */ 837 /* default */ void add( final String[] nms,final String[] vals ) { 838 if( nms != null ) { 839 for( int i=0; i<nms.length; i++ ) { 840 nameMap.put( nms[i].toUpperCase(Locale.JAPAN),vals[i] ); 841 } 842 } 843 } 844 845 /** 846 * キー配列を返します。 847 * 848 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更。 849 * 850 * @return キー配列 851 * @og.rtnNotNull 852 */ 853 /* default */ String[] getNames() { 854 return nameMap.keySet().toArray( new String[nameMap.size()] ); 855 } 856 857 /** 858 * 値配列を返します。 859 * 860 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更。 861 * 862 * @return 値配列 863 * @og.rtnNotNull 864 */ 865 /* default */ String[] getVals() { 866 return nameMap.values().toArray( new String[nameMap.size()] ); 867 } 868 } 869 870 /** 871 * このオブジェクトの文字列表現を返します。 872 * 基本的にデバッグ目的に使用します。 873 * 874 * @return このクラスの文字列表現 875 * @og.rtnNotNull 876 */ 877 @Override 878 public String toString() { 879 return ToString.title( this.getClass().getName() ) 880 .println( "VERSION" ,VERSION ) 881 .println( "sqlType" ,sqlType ) 882 .println( "table" ,table ) 883 .println( "names" ,names ) 884 .println( "omitNames" ,omitNames ) 885 .println( "where" ,where ) 886 .println( "whereNames" ,whereNames ) 887 .println( "constKeys" ,constKeys ) 888 .println( "constVals" ,constVals ) 889 .println( "logicalDelete" ,logicalDelete ) 890 .fixForm().toString() ; 891 } 892}