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.DBColumn; 021import org.opengion.hayabusa.db.DBTableModel; 022import org.opengion.hayabusa.db.DBTableModelUtil; 023import org.opengion.fukurou.system.OgBuilder ; // 6.4.4.1 (2016/03/18) 024import org.opengion.fukurou.util.StringUtil; 025import org.opengion.fukurou.util.ToString; // 6.1.1.0 (2015/01/17) 026import org.opengion.fukurou.util.ArraySet; // 6.4.3.4 (2016/03/11) 027 028import static org.opengion.fukurou.util.StringUtil.nval ; 029 030import java.util.Set; // 6.4.3.4 (2016/03/11) 031import java.util.Map; 032import java.util.LinkedHashMap; 033import java.util.Locale ; 034import java.util.List; 035import java.util.ArrayList; 036 037/** 038 * 2つの DBTableModel の 集合処理を行うタグです。 039 * 040 * マスタとスレーブのそれぞれの DBTableModel を取得し、マージ、差分、排他などの 041 * データ処理を行います。結果を、指定の tableId と scope に書き出します。 042 * マスタテーブルは、masterTableId と masterScope より取り出します。スレーブテーブルは、 043 * slaveTableId と slaveScope より取り出します。 044 * 取り出された DBTableModel は、マスタテーブルに対して、スレーブテーブル情報を参照する形で行われ、 045 * 結果はマスタテーブルに上書きされる形で行われます。 046 * 指定できるアクションは、全体集合(UNION_ALL)、和集合(UNION)、積集合(INTERSECT)、 047 * 差集合(MINUS)、差分集合(DIFFERENCE)、列合成(UNION_CLM)、 列追加(ADD_CLM)、 グループ(GROUP)です。 048 * 列合成と列追加、グループ以外の処理では、カラム順とカラム数は同数でなければなりません。 049 * primaryKeys や unionClms などの指定のキー名は、マスタテーブルに存在する必要があります。 050 * マスタテーブルと同じカラム番号でスレーブテーブルよりデータを読み出します。 051 * (カラム名や属性は、異なってもかまいませんが、マスタテーブルに準拠します。) 052 * また、単独(マスタテーブルのみ)で、和集合と同等の、グループ(GROUP)を使用すると、指定の 053 * カラムでのユニーク化を行うことが可能になります。グループ処理では、先行優先とし、 054 * 2回目に現れた情報を削除することになります。グループ が指定された場合は、 055 * スレーブテーブルは無視されます。いずれの処理においても、集合処理を行う主キーで 056 * 一旦グループ化されます。全体集合(UNION_ALL)で処理する場合でも、主キーがユニークで 057 * ない場合は、マスター、スレーブの各テーブルで一旦グループ化された後で、マージされます。 058 * (マージ後には、同一主キーを持つ行は存在します。) 059 * 全体集合(UNION_ALL)の場合のみ、mergeKeys を指定する必要はありません。その場合は、 060 * キーなしのため、マスターとスレーブのテーブルを単に合成するだけになります。 061 * 062 * 6.9.1.0 (2018/02/26) 063 * UNION_VAL は、スレーブテーブルの縦列を横列に割り当てるUNION_CLMの特殊版です。 064 * 処理としては、そのまま割り当てるのではなく、スレーブテーブルの縦列の値と 065 * ヘッダーとして指定する、unionClms の値を比較して、下限値の最大値を割り当てます。 066 * つまり、ヘッダーの値と同じか、それ以下で、最も近い値を割り当てることになります。 067 * これは、時間的な推移を表現することになります。 068 * masterKeys:マスタとスレーブを関連付ける共通のキーとなるカラム 069 * slaveKeys :スレーブの縦列を横列にするための、キーと値のカラム 070 * unionClms :横列にするカラム名。この値とスレーブのキーと比較してセットするかどうかを決めます。 071 * 072 * 処理前後でのDBTableModelの件数は、以下の変数で取得することが可能です。 073 * 処理前のマスターテーブル : {@DB.MASTER_COUNT} 074 * 処理前のスレーブテーブル : {@DB.SLAVE_COUNT} 075 * 処理後 : {@DB.COUNT} 076 * 077 * @og.formSample 078 * ●形式: 079 * ・<og:tableMerge 080 * action = "UNION_ALL|UNION|INTERSECT|MINUS|DIFFERENCE|UNION_CLM|UNION_VAL|ADD_CLM|GROUP|UNION_SELROW|CDKH|TABLE_REMOVE" 081 * tableId = "DEFAULT" 出力テーブルの tableId 082 * scope = "session" 出力テーブルの scope 083 * masterTableId = "DEFAULT" マスタテーブルの tableId 084 * masterScope = "session" マスタテーブルの scope 085 * slaveTableId = "DEFAULT" スレーブテーブルの tableId 086 * slaveScope = "request" スレーブテーブルの scope 087 * masterKeys = "AA,BB,CC" マスタテーブルの集合処理を行う主キー 088 * slaveKeys = "AA,BB,CC" スレーブテーブルの集合処理を行う主キー(null時=masterKeys) 089 * diffKeys = "DD,EE" マスタテーブルのDIFFERENCE時の差分カラム名 090 * unionClms = "DD,EE" UNION_CLM,ADD_CLM時にスレーブからマスタへ追加するカラム名 091 * unionLbls = "dd,ee" UNION_CLM,ADD_CLM時にスレーブからマスタへ追加するカラムのラベル名 6.9.1.0 (2018/02/26) 092 * modifyClms = "FF,GG" DIFFERENCE,CDKH時にスレーブからマスタへ値を更新するカラム名 093 * noSideEffect = "false" テーブルモデルに対する副作用(true:ない/false:ある) 094 * useDiffData = "true" DIFFERENCE時に差分のスレーブデータを追加するかどうか 095 * useCheckOnly = "false" マスタテーブルの選択行のデータのみを対象に処理を行うかどうか 096 * groupAddClms = "FF,GG" masterKeysで集合処理するときに、相違データをCSV連結して残すカラム名 097 * display = "true" 処理概要を表示するかどうか 098 * /> 099 * ●body:なし 100 * 101 * ●Tag定義: 102 * <og:tableMerge 103 * command 【TAG】コマンド (NEW,RENEW)をセットします(初期値:NEW) 104 * action 【TAG】アクションを指定します(UNION_ALL|UNION|INTERSECT|MINUS|DIFFERENCE|UNION_CLM|UNION_VAL|ADD_CLM|GROUP|UNION_SELROW|CDKH|TABLE_REMOVE) 105 * tableId 【TAG】出力先のtableIdを指定します (初期値:HybsSystem#TBL_MDL_KEY[=h_tblmdl]) 106 * scope 【TAG】出力先のscopeを指定します(初期値:session) 107 * masterTableId 【TAG】マスタテーブルのtableIdを指定します (初期値:HybsSystem#TBL_MDL_KEY[=h_tblmdl]) 108 * masterScope 【TAG】マスタテーブルのscopeを指定します(初期値:session) 109 * slaveTableId 【TAG】スレーブテーブルのtableIdを指定します (初期値:HybsSystem#TBL_MDL_KEY[=h_tblmdl]) 110 * slaveScope 【TAG】スレーブテーブルのscopeを指定します(初期値:session) 111 * masterKeys 【TAG】マスタテーブルの集合処理を行う主キーを指定します 112 * slaveKeys 【TAG】スレーブテーブルの集合処理を行う主キーを指定します 113 * diffKeys 【TAG】マスタテーブルのDIFFERENCE時の差分カラム名を(CSV形式)指定します 114 * unionClms 【TAG】スレーブからマスタへ追加するカラム名をCSV形式で指定します 115 * unionLbls 【TAG】スレーブからマスタへ追加するラベルをCSV形式で指定します 6.9.1.0 (2018/02/26) 116 * nullDelClms 【TAG】指定の値がすべてnull(空文字列)の場合、レコードを削除するカラムをCSV形式で指定します 117 * useSlaveLoop 【TAG】scope="request" のスレーブテーブルを、ループ処理して使用します(action="TABLE_REMOVE" と、"UNION_CLM" で、利用します) 118 * modifyClms 【TAG】DIFFERENCE,CDKH時にスレーブからマスタへ値を更新するカラム名をCSV形式で指定します 119 * groupAddClms 【TAG】集合処理するときに、相違データをCSV連結して残すカラム名をCSV形式で指定します 120 * noSideEffect 【TAG】テーブルモデルに対する副作用の有無[true:ない/false:ある]を指定します(初期値:false:ある) 121 * useDiffData 【TAG】差分のスレーブデータを結果テーブルに追加するかどうかを指定します(初期値:true) 122 * useCheckOnly 【TAG】マスタテーブルの選択行のデータのみを対象に処理を行うかどうかを指定します(初期値:false) 123 * stopZero 【TAG】検索結果が0件のとき処理を続行するかどうか[true/false]を指定します(初期値:false[続行する]) 124 * display 【TAG】マージの結果を表示するかどうかを指定します(初期値:true) 125 * mainTrans 【TAG】(通常は使いません)タグで処理される処理がメインとなるトランザクション処理かどうかを指定します(初期値:false) 126 * caseKey 【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null) 127 * caseVal 【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null) 128 * caseNN 【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない) 129 * caseNull 【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない) 130 * caseIf 【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない) 131 * separator 【TAG】groupAddClmsで文字列を連結する項目区切り文字をセットします(初期値:",") 132 * debug 【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false) 133 * /> 134 * 135 * ●使用例 136 * 例1)デフォルト以外に必要な属性のみ指定するサンプル 137 * <og:tableMerge action="UNION" 138 * slaveScope = "request" masterKeys = "AA,BB,CC" 139 * /> 140 * 141 * ・出力先、マスターともに初期値は、tableId="DEFAULT" scope="session" です。 142 * スレーブは、tableId か scope をける必要がある為、scope="request" を指定しています。 143 * 比較するカラム名は、マスタ、スレーブ同じであれば、マスタのみ指定でかまいません。 144 * 145 * 例2)マスタ、スレーブともメモリに残らないように request で作成します。 146 * <og:tableMerge action="INTERSECT" 147 * masterScope = "request" 148 * slaveScope = "request" 149 * slaveTableId = "SLAVE" 150 * masterKeys = "AA,BB,CC" 151 * /> 152 * 153 * ・マスタ、スレーブともメモリに残らないように request で作成した場合は、 154 * どちらかの TableId を変える必要があります。 155 * 出力先は初期値の、tableId="DEFAULT" scope="session" です。 156 * 157 * @og.rev 3.8.0.9 (2005/10/17) 新規追加 158 * @og.group DB登録 159 * 160 * @version 0.9.0 2000/10/17 161 * @author Kazuhiko Hasegawa 162 * @since JDK5.0, 163 */ 164public class TableMergeTag extends CommonTagSupport { 165 /** このプログラムのVERSION文字列を設定します。 {@value} */ 166 private static final String VERSION = "7.4.2.0 (2021/04/30)" ; 167 private static final long serialVersionUID = 742020210430L ; 168 169 /** action 引数に渡す事の出来る アクションコマンド 全体集合 {@value} */ 170 public static final String ACT_UNION_ALL = "UNION_ALL" ; 171 /** action 引数に渡す事の出来る アクションコマンド 和集合 {@value} */ 172 public static final String ACT_UNION = "UNION" ; 173 /** action 引数に渡す事の出来る アクションコマンド 積集合 {@value} */ 174 public static final String ACT_INTERSECT = "INTERSECT" ; 175 /** action 引数に渡す事の出来る アクションコマンド 差集合{@value} */ 176 public static final String ACT_MINUS = "MINUS" ; 177 /** action 引数に渡す事の出来る アクションコマンド 差分集合{@value} */ 178 public static final String ACT_DIFFERENCE = "DIFFERENCE" ; 179 /** action 引数に渡す事の出来る アクションコマンド 列合成{@value} */ 180 public static final String ACT_UNION_CLM = "UNION_CLM" ; 181 /** action 引数に渡す事の出来る アクションコマンド 列合成{@value} */ 182 public static final String ACT_UNION_VAL = "UNION_VAL" ; // 6.9.1.0 (2018/02/26) 183 /** action 引数に渡す事の出来る アクションコマンド 列合成{@value} */ 184 public static final String ACT_ADD_CLM = "ADD_CLM" ; 185 /** action 引数に渡す事の出来る アクションコマンド グループ {@value} */ 186 public static final String ACT_GROUP = "GROUP" ; 187 /** action 引数に渡す事の出来る アクションコマンド グループ {@value} */ 188 public static final String ACT_UNION_SELROW = "UNION_SELROW" ; // 4.3.2.0 (2008/09/11) 189 /** action 引数に渡す事の出来る アクションコマンド 改廃コード {@value} */ 190 public static final String ACT_CDKH = "CDKH" ; // 6.3.1.1 (2015/07/10) 191 /** action 引数に渡す事の出来る アクションコマンド DBTableModelの削除 {@value} */ 192 public static final String ACT_TABLE_REMOVE = "TABLE_REMOVE" ; // 6.7.3.0 (2017/01/27) 193 194 // 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。 195 private static final Set<String> ACTION_SET = new ArraySet<>( ACT_UNION_ALL , ACT_UNION , ACT_INTERSECT , ACT_MINUS , ACT_DIFFERENCE , ACT_UNION_CLM , ACT_UNION_VAL , 196 ACT_ADD_CLM , ACT_GROUP , ACT_UNION_SELROW , ACT_CDKH , ACT_TABLE_REMOVE ); 197 198 /** command 引数に渡す事の出来る コマンド 新規 {@value} */ 199 public static final String CMD_NEW = "NEW" ; 200 /** command 引数に渡す事の出来る コマンド 再検索 {@value} */ 201 public static final String CMD_RENEW = "RENEW" ; 202 // 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。 203 private static final Set<String> COMMAND_SET = new ArraySet<>( CMD_NEW , CMD_RENEW ); 204 205 private String command = CMD_NEW; 206 207 private String action ; 208 private String tableId = HybsSystem.TBL_MDL_KEY; // 出力先の tableId 209 private String scope = "session"; // 出力先の scope 210 211 private String masterTableId = HybsSystem.TBL_MDL_KEY; // マスタテーブルの tableId 212 private String masterScope = "session"; // マスタテーブルの scope 213 private String slaveTableId = HybsSystem.TBL_MDL_KEY; // スレーブテーブルの tableId 214 private String slaveScope = "request"; // スレーブテーブルの scope 215 private String masterKeys ; // マスタテーブルの集合処理を行う主キー 216 private String slaveKeys ; // スレーブテーブルの集合処理を行う主キー(null時=masterKeys) 217 private String diffKeys ; // マスタテーブルのDIFFERENCE時の差分カラム名 218 private String unionClms ; // 6.7.3.0 (2017/01/27) 値がnull時にレコードを削除 219 private String unionLbls ; // 6.9.1.0 (2018/02/26) 追加 220 private String nullDelClms ; // UNION_CLM時にスレーブからマスタへ追加するカラム名 221 private String modifyClms ; // DIFFERENCE,CDKH時にスレーブからマスタへ値を更新するカラム名 222 private String groupAddClms ; // 5.1.4.0 (2010/03/01) masterKeysで集合処理するときに、相違データをCSV連結して残すカラム名 223 private boolean noSideEffect ; // テーブルモデルに対する副作用(true:ない/false:ある) 224 private boolean useDiffData = true; // DIFFERENCE時に差分のスレーブデータを追加するかどうか 225 private boolean useCheckOnly ; // マスタテーブルの選択行のデータのみを対象に処理を行うかどうか 226 private boolean display = true; 227 private boolean isMainTrans = true; // 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し 228 private String separator = ","; // 5.3.1.0 (2011/01/01) groupAddClmnsで結合する際の区切り文字 229 private boolean stopZero ; // 5.7.6.2 (2014/05/16) stopZero属性追加 230 private boolean useSlaveLoop ; // 6.7.3.0 (2017/01/27) useSlaveLoop属性追加 231 232 /** 233 * デフォルトコンストラクター 234 * 235 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor. 236 */ 237 public TableMergeTag() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 238 239 /** 240 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。 241 * 242 * @og.rev 4.3.2.0 (2008/09/11) UNION_SELROWアクション対応 243 * @og.rev 4.3.3.0 (2008/10/01) 処理前後のテーブル件数を取得できるようにする 244 * @og.rev 4.3.3.1 (2008/10/08) スレーブのnullチェック追加 245 * @og.rev 5.1.6.0 (2010/05/01) DBLastSqlの処理は、DBTableModelが新規作成された処理でのみ行う。 246 * @og.rev 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応 247 * @og.rev 5.7.6.2 (2014/05/16) stopZero属性、DB.COUNT キーで検索件数をリクエストにセットする。 248 * @og.rev 6.3.1.1 (2015/07/10) Map作成時に、DBTableModel のグループ化を行うかどうか指定できるようにする。 249 * @og.rev 6.3.1.1 (2015/07/10) 改廃コードを設定する CDKH アクションを追加 250 * @og.rev 6.3.4.0 (2015/08/01) Arrays.toString から String.join に置き換え。 251 * @og.rev 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。 252 * @og.rev 6.4.4.1 (2016/03/18) StringBuilderの代わりに、OgBuilderを使用する。 253 * @og.rev 6.7.3.0 (2017/01/27) useSlaveLoop,nullDelClms属性追加と、action="TABLE_REMOVE" の追加 254 * @og.rev 6.9.1.0 (2018/02/26) ACT_UNION_VAL追加 255 * 256 * @return 後続処理の指示 257 */ 258 @Override 259 public int doEndTag() { 260 debugPrint(); // 4.0.0 (2005/02/28) 261 // 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応 262 if( !useTag() || ! check( command, COMMAND_SET ) ) { return EVAL_PAGE ; } 263 264 // 整合性チェック:アクションリストとの存在チェック 265 if( !check( action, ACTION_SET ) ) { 266 final String errMsg = "指定のアクションは実行できません。アクションエラー" + CR 267 + "action=[" + action + "] " + CR 268 + "指定可能なアクション:" 269 + String.join( ", " , ACTION_SET ) ; 270 throw new HybsSystemException( errMsg ); 271 } 272 273 // 6.7.3.0 (2017/01/27) action="TABLE_REMOVE" で、DBTableModel を無条件で、削除します。 274 if( ACT_TABLE_REMOVE.equalsIgnoreCase( action ) ) { 275 super.setScope( masterScope ); 276 removeObject( masterTableId ); 277 return EVAL_PAGE ; 278 } 279 280 // スレーブテーブルの集合処理を行う主キー(null時=masterKeys) 281 if( slaveKeys == null ) { slaveKeys = masterKeys; } 282 283 super.setScope( masterScope ); 284 DBTableModel masterTable = (DBTableModel)getObject( masterTableId ); 285 286 // 6.7.3.0 (2017/01/27) useSlaveLoop属性追加と、action="TABLE_REMOVE" の追加 287 // 6.9.7.0 (2018/05/14) PMD These nested if statements could be combined 288// if( useSlaveLoop ) { 289// // 初っ端はDBTableModel が存在しないはずなので、slave のテーブルを、マスターに登録しなおす。(処理は、これだけで終了) 290// if( masterTable == null ) { 291 if( useSlaveLoop && masterTable == null ) { 292 // 初っ端はDBTableModel が存在しないはずなので、slave のテーブルを、マスターに登録しなおす。(処理は、これだけで終了) 293 // slave の scope と、tableId から取り出した DBTableModel を、master に登録する。 294 super.setScope( slaveScope ); 295 masterTable = (DBTableModel)getObject( slaveTableId ); 296 if( masterTable != null && masterTable.getRowCount() > 0 ) { 297 super.setScope( masterScope ); 298 setObject( masterTableId , masterTable ); 299 } 300 return EVAL_PAGE ; 301// } 302 } 303 304 // 整合性チェック:マスタテーブルは必須 305 if( masterTable == null ) { 306 final String errMsg = "マスタテーブルは必須です。" 307 + CR 308 + "action=[" + action + "] " 309 + "masterTableId=[" + masterTableId + "] " 310 + "masterScope=[" + masterScope + "] " ; 311 throw new HybsSystemException( errMsg ); 312 } 313 if( noSideEffect ) { masterTable = cloneTable( masterTable ); } 314 // 6.3.1.1 (2015/07/10) Map作成時に、DBTableModel のグループ化を行うかどうか指定できるようにする。 315 // useGroup=true(行う) なので、masterTable は副作用が発生する。 316 final Map<String,Integer> masterMap = makeKeyMap( masterTable,masterKeys,useCheckOnly,true ); 317 318 DBTableModel slaveTable = null; 319 Map<String,Integer> slaveMap = null; 320 321 // 整合性チェック:action="GROUP" と "ADD_CLM" 以外では、スレーブテーブルは必須 322 // 6.9.1.0 (2018/02/26) ACT_UNION_VAL追加 323 if( ! ACT_GROUP.equalsIgnoreCase( action ) && !ACT_ADD_CLM.equalsIgnoreCase( action ) ) { 324 super.setScope( slaveScope ); 325 slaveTable = (DBTableModel)getObject( slaveTableId ); 326 327 if( slaveTable == null ) { 328 final String errMsg = "action=[" + action + "] 時には、スレーブテーブルが必要です。" 329 + CR 330 + "slaveTableId=[" + slaveTableId + "] " 331 + "slaveScope=[" + slaveScope + "] " ; 332 throw new HybsSystemException( errMsg ); 333 } 334 if( noSideEffect ) { slaveTable = cloneTable( slaveTable ); } 335 // 6.3.1.1 (2015/07/10) Map作成時に、DBTableModel のグループ化を行うかどうか指定できるようにする。 336 slaveMap = makeKeyMap( slaveTable,slaveKeys,false,false ); // スレーブはuseCheckOnlyしない、グループ化しない。 337 338 // その場合、マスタとスレーブが同一メモリ上のテーブル(物理的に同じ)でない事。 339 if( slaveTable == masterTable ) { 340 final String errMsg = "マスタとスレーブが同一メモリになっています。" 341 + "通常、検索結果は tableId と scope を別々に指定するか、noSideEffect=true(副作用なし)を、指定してください。" 342 + CR 343 + "action=[" + action + "] " 344 + "masterTableId=[" + masterTableId + "] " 345 + "slaveTableId=[" + slaveTableId + "] " 346 + "masterScope=[" + masterScope + "] " 347 + "slaveScope=[" + slaveScope + "] " ; 348 throw new HybsSystemException( errMsg ); 349 } 350 } 351 352 super.setScope( scope ); 353 useMainTrans( isMainTrans ); // 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し 354 startQueryTransaction( tableId ); // 3.6.0.8 (2004/11/19) 355 356 // 設定値の整合性チェック 357 // 整合性チェック:action="UNION_ALL" と "UNION_VAL" と "GROUP"と "ADD_CLM"と "UNION_CLM" 以外では、masterKeys が必須 358 // 6.9.1.0 (2018/02/26) ACT_UNION_VAL追加 359 if( ! ACT_UNION_ALL.equalsIgnoreCase( action ) 360 && !ACT_UNION_VAL.equalsIgnoreCase( action ) // 6.9.1.0 (2018/02/26) 361 && !ACT_UNION_SELROW.equalsIgnoreCase( action ) // 4.3.2.0 (2008/09/11) 362 && !ACT_GROUP.equalsIgnoreCase( action ) 363 && !ACT_ADD_CLM.equalsIgnoreCase( action ) 364 && !ACT_UNION_CLM.equalsIgnoreCase( action ) 365 && masterKeys == null ) { 366 final String errMsg = "action=\"" + action + "\" 時には、masterKeys が必須です。" 367 + CR 368 + "masterKeys=[" + masterKeys + "] "; 369 throw new HybsSystemException( errMsg ); 370 } 371 372 // 設定値の整合性チェック 373 // 整合性チェック:action="DIFFERENCE" では、diffKeys が必須 374 if( ACT_DIFFERENCE.equalsIgnoreCase( action ) && diffKeys == null ) { 375 final String errMsg = "action=\"" + action + "\" 時には、diffKeys が必須です。" 376 + CR 377 + "diffKeys=[" + diffKeys + "] "; 378 throw new HybsSystemException( errMsg ); 379 } 380 381 // 設定値の整合性チェック 382 // 整合性チェック:列合成(UNION_CLM) と 値列合成(UNION_VAL) と グループ(GROUP) と 列追加(ADD_CLM) と 積集合(INTERSECT) と 差集合(MINUS) 以外では、 383 // テーブルカラム数が同じである必要がある。 384 // 6.9.1.0 (2018/02/26) ACT_UNION_VAL追加 385 if( ! ACT_UNION_CLM.equalsIgnoreCase( action ) 386 && !ACT_UNION_VAL.equalsIgnoreCase( action ) // 6.9.1.0 (2018/02/26) 387 && !ACT_GROUP.equalsIgnoreCase( action ) 388 && !ACT_ADD_CLM.equalsIgnoreCase( action ) 389 && !ACT_INTERSECT.equalsIgnoreCase( action ) // 6.3.1.1 (2015/07/10) INTERSECT と MINUS を条件に追加 390 && !ACT_MINUS.equalsIgnoreCase( action ) ) { // 6.3.1.1 (2015/07/10) INTERSECT と MINUS を条件に追加 391 final int mClmSize = masterTable.getColumnCount(); 392 final int sClmSize = slaveTable.getColumnCount(); 393 394 if( mClmSize != sClmSize ) { 395 final String errMsg = "action=\"" + action + "\" 時には、テーブルカラム数が異なってはいけません。" 396 + CR 397 + "Master=" + mClmSize + " ,[" 398 + StringUtil.array2csv( masterTable.getNames() ) + "]" 399 + CR 400 + "Slave =" + sClmSize + " ,[" 401 + StringUtil.array2csv( slaveTable.getNames() ) + "]"; 402 throw new HybsSystemException( errMsg ); 403 } 404 } 405 406 // 6.4.4.1 (2016/03/18) display == true 時の出力を後ろにします。 407 final String MST_CNT = String.valueOf( masterTable.getRowCount() ); 408 final String SLV_CNT = slaveTable == null ? null : String.valueOf( slaveTable.getRowCount() ); 409 410 setRequestAttribute( "DB.MASTER_COUNT" , MST_CNT ); 411 if( SLV_CNT != null ) { 412 setRequestAttribute( "DB.SLAVE_COUNT", SLV_CNT ); 413 } 414 415 DBTableModel table = null; 416 if( ACT_UNION_ALL.equalsIgnoreCase( action ) ) { // 全体集合 417 table = makeUnionAll( masterTable,slaveTable ); 418 } 419 else if( ACT_UNION_SELROW.equalsIgnoreCase( action ) ) { // 4.3.2.0 (2008/09/11) 全体集合(slave表をmaster表のチェック行から追加) 420 table = makeUnionSelrow( masterTable,slaveTable ); 421 } 422 else if( ACT_UNION.equalsIgnoreCase( action ) ) { // 和集合 423 table = makeUnion( masterTable,masterMap,slaveTable,slaveMap ); 424 } 425 else if( ACT_INTERSECT.equalsIgnoreCase( action ) ) { // 積集合 426 table = makeIntersect( masterTable,masterMap,slaveMap ); 427 } 428 else if( ACT_MINUS.equalsIgnoreCase( action ) ) { // 差集合 429 table = makeMinus( masterTable,masterMap,slaveMap ); 430 } 431 else if( ACT_DIFFERENCE.equalsIgnoreCase( action ) ) { // 差分集合 432 table = makeDifference( masterTable,masterMap,slaveTable,slaveMap ); 433 } 434 else if( ACT_UNION_CLM.equalsIgnoreCase( action ) ) { // 列合成 435 if( unionClms == null ) { 436 final String errMsg = "action=\"UNION_CLM\" 時には、unionClms が必須です。" ; 437 throw new HybsSystemException( errMsg ); 438 } 439 440 table = makeUnionClm( masterTable,slaveKeys,slaveTable,slaveMap ); 441 } 442 // 6.9.1.0 (2018/02/26) ACT_UNION_VAL追加 443 else if( ACT_UNION_VAL.equalsIgnoreCase( action ) ) { // 値列合成 444 if( unionClms == null ) { 445 final String errMsg = "action=\"UNION_VAL\" 時には、unionClms が必須です。" ; 446 throw new HybsSystemException( errMsg ); 447 } 448 449 table = makeUnionVal( masterTable,masterKeys,slaveTable,slaveKeys ); 450 } 451 else if( ACT_ADD_CLM.equalsIgnoreCase( action ) ) { // 列追加 452 if( unionClms == null ) { 453 final String errMsg = "action=\"ADD_CLM\" 時には、unionClms が必須です。" ; 454 throw new HybsSystemException( errMsg ); 455 } 456 457 table = makeAddClm( masterTable ); 458 } 459 else if( ACT_GROUP.equalsIgnoreCase( action ) ) { // グループ化は、先のレコードが残ります。 460 table = makeGroup( masterTable ); 461 462 // 6.7.3.0 (2017/01/27) nullDelClmsで、値がnull時にレコードを削除 463 if( nullDelClms != null && !nullDelClms.isEmpty() ) { 464 table = deleteRows( table,nullDelClms ); 465 } 466 } 467 else if( ACT_CDKH.equalsIgnoreCase( action ) ) { // 6.3.1.1 (2015/07/10) CDKH 468 table = makeCdkh( masterTable,masterMap,slaveTable,slaveMap ); 469 } 470 471 // 6.0.2.4 (2014/10/17) NP:null 値を利用している可能性があります。 472 if( table == null ) { 473 final String errMsg = "テーブルデータを作成できませんでした。action=[" + action + "]" ; 474 throw new HybsSystemException( errMsg ); 475 } 476 477 // 3.6.0.8 (2004/11/19) トランザクションチェックを行います。 478 super.setScope( scope ); 479 if( ! commitTableObject( tableId, table ) ) { 480 return SKIP_PAGE ; 481 } 482 483 // 5.7.6.2 (2014/05/16) マージした結果の DBTableModel のデータ件数 484 final int rowCnt = table.getRowCount(); 485 486 // 6.4.4.1 (2016/03/18) display == true 時の出力を後ろにします。 487 // 6.4.4.1 (2016/03/18) StringBuilderの代わりに、OgBuilderを使用する。 488 if( display ) { 489 final OgBuilder buf = new OgBuilder() 490 .append( action , "( M[" , MST_CNT , "]" ) 491 .appendIf( SLV_CNT != null 492 , ",S[" , SLV_CNT , "]" ) 493 .append( " ) = [" , String.valueOf( rowCnt ) , "]" ); 494 495 jspPrint( buf.toString() ); 496 } 497 498 // 5.7.6.2 (2014/05/16) 検索結果の件数を、"DB.COUNT" キーでリクエストにセットする。 499 setRequestAttribute( "DB.COUNT" , String.valueOf( rowCnt ) ); 500 501 // 5.7.6.2 (2014/05/16) 件数0件かつ stopZero = true 502 // 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method 503 return rowCnt == 0 && stopZero 504 ? SKIP_PAGE 505 : EVAL_PAGE ; // ページの残りを評価する。 506 } 507 508 /** 509 * タグリブオブジェクトをリリースします。 510 * キャッシュされて再利用されるので、フィールドの初期設定を行います。 511 * 512 * @og.rev 5.1.4.0 (2010/03/01) groupAddClms 追加 513 * @og.rev 5.1.6.0 (2010/05/01) DBLastSqlの処理は、DBTableModelが新規作成された処理でのみ行う。 514 * @og.rev 5.3.1.0 (2011/01/01) separator追加 515 * @og.rev 5.7.6.2 (2014/05/16) stopZero属性追加 516 * @og.rev 6.7.3.0 (2017/01/27) useSlaveLoop,nullDelClms属性追加 517 */ 518 @Override 519 protected void release2() { 520 super.release2(); 521 command = CMD_NEW; 522 action = null; 523 tableId = HybsSystem.TBL_MDL_KEY; // 出力先の tableId 524 scope = "session"; // 出力先の scope 525 masterTableId = HybsSystem.TBL_MDL_KEY; // マスタテーブルの tableId 526 masterScope = "session"; // マスタテーブルの scope 527 slaveTableId = HybsSystem.TBL_MDL_KEY; // スレーブテーブルの tableId 528 slaveScope = "request"; // スレーブテーブルの scope 529 masterKeys = null; // マスタテーブルの集合処理を行う主キー 530 slaveKeys = null; // スレーブテーブルの集合処理を行う主キー(null時=masterKeys) 531 diffKeys = null; // マスタテーブルのDIFFERENCE時の差分カラム名 532 unionClms = null; // スレーブからマスタへ追加するカラム名 533 unionLbls = null; // 6.9.1.0 (2018/02/26) スレーブからマスタへ追加するラベル名 534 nullDelClms = null; // 6.7.3.0 (2017/01/27) 値がnull時にレコードを削除 535 modifyClms = null; // DIFFERENCE,CDKH時にスレーブからマスタへ値を更新するカラム名 536 groupAddClms = null; // 5.1.4.0 (2010/03/01) masterKeysで集合処理するときに、相違データをCSV連結して残すカラム名 537 noSideEffect = false; // テーブルモデルに対する副作用(true:ない/false:ある) 538 useDiffData = true; // DIFFERENCE時に差分のスレーブデータを追加するかどうか 539 useCheckOnly = false; // マスタテーブルの選択行のデータのみを対象に処理を行うかどうか 540 display = true; 541 stopZero = false; // 5.7.6.2 (2014/05/16) stopZero属性追加 542 useSlaveLoop = false; // 6.7.3.0 (2017/01/27) useSlaveLoop属性追加 543 isMainTrans = true; // 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し 544 separator = ","; 545 } 546 547 /** 548 * 指定のテーブルの指定のカラム(CSV)より、行番号をマップ化します。 549 * なお、引数のテーブルについては、useGroup属性に応じて、主キーによるグループ処理を 550 * 行うかどうか決定します。(グループ化処理を行うと、副作用が発生します。) 551 * 552 * @og.rev 4.3.2.0 (2008/09/11) マスタキー未指定時は何もしない 553 * @og.rev 5.1.4.0 (2010/03/01) groupAddClms 追加 554 * @og.rev 5.3.1.0 (2011/01/01) groupAddClms の separator指定対応 555 * @og.rev 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。 556 * @og.rev 6.3.1.1 (2015/07/10) Map作成時に、DBTableModel のグループ化を行うかどうか指定できるようにする。 557 * 558 * @param table マップ作成元のデータテーブル(副作用あり) 559 * @param keys マップ作成の主キー(CSV形式) 560 * @param useCheckOnly チェック行のみ対象 [true:する/false:しない] 561 * @param useGroup 主キーによるグループ処理 [true:する/false:しない] 562 * 563 * @return マップ化された主キーと行番号 564 */ 565 private Map<String,Integer> makeKeyMap( final DBTableModel table, final String keys, final boolean useCheckOnly, final boolean useGroup ) { 566 567 // カラム名をカラム番号に変換します。 568 final int[] clmNo = makeColumnNo( table,keys ); 569 final int clmSize = clmNo.length; 570 if( clmSize == 0 ) { // マスタキー未指定時には全カラムを使用します。 571 return null; // 4.3.2.0 (2008/09/11) 572 } 573 574 // groupAddClms をカラム番号に変換します。 575 final int[] grpClmNo = makeColumnNo( table,groupAddClms ); 576 final int grpClmSize = grpClmNo.length; 577 578 final Map<String,Integer> tempMap = new LinkedHashMap<>(); 579 580 // 注意:GROUP化の過程で テーブル件数が動的に変化します。 581 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); // 6.1.0.0 (2014/12/26) refactoring 582 for( int row=0; row<table.getRowCount(); row++ ) { 583 if( useCheckOnly && table.getModifyType( row ).isEmpty() ) { 584 continue; 585 } 586 587 buf.setLength(0); // 6.1.0.0 (2014/12/26) refactoring 588 for( int i=0; i<clmSize; i++ ) { 589 buf.append( table.getValue( row,clmNo[i] ) ).append( '|' ); // 6.0.2.5 (2014/10/31) char を append する。 590 } 591 // 主キー連結文字列 592 final String key = buf.toString().toUpperCase(Locale.JAPAN); // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。 593 if( tempMap.containsKey( key ) ) { 594 // 6.3.1.1 (2015/07/10) Map作成時に、DBTableModel のグループ化を行うかどうか指定できるようにする。 595 if( useGroup ) { 596 // 5.1.4.0 (2010/03/01) groupAddClms 追加 597 final int orgRow = tempMap.get( key ).intValue() ; // 追加済みの行番号を取得 598 for( int i=0; i<grpClmSize; i++ ) { 599 final int clm = grpClmNo[i]; 600 final String val1 = table.getValue( orgRow,clm ) ; // 既存の行・カラムのデータ 601 final String val2 = table.getValue( row,clm ) ; // 削除する行・カラムのデータ 602 // 5.3.1.0 (2011/01/01) 603 table.setValueAt( val1 + separator + val2,orgRow,clm ); // CSV 連結、データを戻す。 604 } 605 table.removeValue( row ); 606 row-- ; 607 } 608 } 609 else { // まだ、未登録の場合 610 tempMap.put( key,Integer.valueOf( row ) ); 611 } 612 } 613 614 return tempMap; 615 } 616 617 /** 618 * 指定のテーブルの指定のカラム(CSV)より、行番号をマップ化します。 619 * なお、引数のテーブルについては、useGroup属性に応じて、主キーによるグループ処理を 620 * 行うかどうか決定します。(グループ化処理を行うと、副作用が発生します。) 621 * 622 * @og.rev 6.9.1.0 (2018/02/26) 新規追加 623 * 624 * @param table マップ作成元のデータテーブル(副作用なし) 通常は、スレーブテーブル 625 * @param keys マップ作成の主キー(CSV形式) 通常は、マスターキー(スレーブとマッチングさせるためのキー) 626 * @param valClm 値の配列リストに設定するカラム列(CSV形式) キーカラムと値カラム 627 * 628 * @return マップ化された主キーと値の配列リスト 629 */ 630 private Map<String,List<String[]>> makeValMap( final DBTableModel table, final String keys, final String valClm ) { 631 // カラム名をカラム番号に変換します。 632 final int[] clmNo = makeColumnNo( table,keys ); // キーとなるカラムのCSV形式 633 final int clmSize = clmNo.length; 634 if( clmSize == 0 ) { // マスタキー未指定時には全カラムを使用します。 635 return null; 636 } 637 638 final int[] valNo = makeColumnNo( table,valClm ); // 値のカラムのCSV形式 (PMD:6.9.9.4 (2018/10/01)) 639 final Map<String,List<String[]>> tempMap = new LinkedHashMap<>(); 640 641 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 642 for( int row=0; row<table.getRowCount(); row++ ) { 643 buf.setLength(0); 644 for( int i=0; i<clmSize; i++ ) { 645 buf.append( table.getValue( row,clmNo[i] ) ).append( '|' ); 646 } 647 // 値の取得 648 final String[] vals = new String[valNo.length]; 649 for( int i=0; i<valNo.length; i++ ) { 650 vals[i] = table.getValue( row,valNo[i] ); 651 } 652 653 // 主キー連結文字列 654 final String key = buf.toString().toUpperCase(Locale.JAPAN); // 大文字・小文字を区別しない。 655 tempMap.computeIfAbsent( key , k -> new ArrayList<String[]>() ).add( vals ); 656 } 657 658 return tempMap; 659 } 660 661 /** 662 * 指定のマスタ,スレーブテーブルを使用して 全体集合 処理を実行します。 663 * 664 * @param masterTbl マスターテーブルモデル 665 * @param slaveTbl スレーブテーブルモデル 666 * 667 * @return 処理結果のテーブルモデル 668 */ 669 private DBTableModel makeUnionAll( final DBTableModel masterTbl,final DBTableModel slaveTbl ) { 670 final DBTableModel table = masterTbl; 671 672 for( int row=0; row<slaveTbl.getRowCount(); row++ ) { 673 final String[] vals = slaveTbl.getValues( row ); 674 table.addColumnValues( vals ); 675 } 676 677 return table; 678 } 679 680 /** 681 * 指定のマスタ,スレーブテーブルを使用して 全体集合 処理を実行します。 682 * スレーブ表は、マスタ表のチェックされた行を起点として登録されます。 683 * チェックされていない場合は、スレーブ表は先頭から追加されます。 684 * 685 * @og.rev 4.3.2.0 (2008/09/11) 新規作成 686 * 687 * @param masterTbl マスターテーブルモデル 688 * @param slaveTbl スレーブテーブルモデル 689 * 690 * @return 処理結果のテーブルモデル 691 */ 692 private DBTableModel makeUnionSelrow( final DBTableModel masterTbl,final DBTableModel slaveTbl ) { 693 final DBTableModel table = masterTbl; 694 695 final int insRowNo = getParameterRows().length > 0 ? getParameterRows()[0] + 1 : 0 ; 696 for( int row=0; row<slaveTbl.getRowCount(); row++ ) { 697 final String[] vals = slaveTbl.getValues( row ); 698 table.addValues( vals, insRowNo + row, false ); 699 } 700 701 return table; 702 } 703 704 /** 705 * 指定のマスタ,スレーブテーブルを使用して 和集合 処理を実行します。 706 * 707 * @og.rev 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。 708 * 709 * @param masterTbl マスターテーブルモデル 710 * @param masterMap マスターテーブルの主キーマップ 711 * @param slaveTbl スレーブテーブルモデル 712 * @param slaveMap スレーブテーブルの主キーマップ 713 * 714 * @return 処理結果のテーブルモデル 715 */ 716 private DBTableModel makeUnion( final DBTableModel masterTbl,final Map<String,Integer> masterMap, 717 final DBTableModel slaveTbl,final Map<String,Integer> slaveMap ) { 718 final DBTableModel table = masterTbl; 719 720 @SuppressWarnings("rawtypes") 721 final Map.Entry[] entry = slaveMap.entrySet().toArray( new Map.Entry[slaveMap.size()] ); 722 723 final int size = entry.length; 724 for( int i=0; i<size; i++ ) { 725 String key = (String)entry[i].getKey(); 726 if( key != null ) { key = key.toUpperCase(Locale.JAPAN); } // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。 727 if( ! masterMap.containsKey( key ) ) { // マスタにスレーブデータが存在しない 728 final int row = ((Integer)entry[i].getValue()).intValue(); 729 final String[] vals = slaveTbl.getValues( row ); 730 table.addColumnValues( vals ); 731 } 732 } 733 return table; 734 } 735 736 /** 737 * 指定のマスタ,スレーブテーブルを使用して 積集合 処理を実行します。 738 * 739 * @og.rev 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。 740 * @og.rev 6.2.5.1 (2015/06/12) Modify情報が書かれていると、DELETE状況が再セットされないため、削除されなかった。 741 * 742 * @param masterTbl マスターテーブルモデル 743 * @param masterMap マスターテーブルの主キーマップ 744 * @param slaveMap スレーブテーブルの主キーマップ 745 * 746 * @return 処理結果のテーブルモデル 747 */ 748 private DBTableModel makeIntersect( final DBTableModel masterTbl,final Map<String,Integer> masterMap, final Map<String,Integer> slaveMap ) { 749 final DBTableModel table = masterTbl; 750 751 @SuppressWarnings("rawtypes") 752 final Map.Entry[] entry = masterMap.entrySet().toArray( new Map.Entry[masterMap.size()] ); 753 754 // 6.2.5.1 (2015/06/12) 削除する行番号を記憶しておく。 755 final List<Integer> rowList = new ArrayList<>(); 756 757 // table.resetModify(); // 6.2.5.1 (2015/06/12) Modify情報をリセットしておく必要があった。 758 759 final int size = entry.length; 760 for( int i=0; i<size; i++ ) { 761 String key = (String)entry[i].getKey(); 762 if( key != null ) { key = key.toUpperCase(Locale.JAPAN); } // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。 763 if( ! slaveMap.containsKey( key ) ) { // スレーブにマスタが存在しない 764 rowList.add( (Integer)entry[i].getValue() ); // 6.2.5.1 (2015/06/12) rowをセット 765 } 766 } 767 768 // 行番号が変わらないように逆順削除します。 769 // 6.2.5.1 (2015/06/12) rowを削除 770 for( int i=rowList.size()-1 ; i>=0; i-- ) { 771 table.removeValue( rowList.get(i) ); 772 } 773 774 return table; 775 } 776 777 /** 778 * 指定のマスタ,スレーブテーブルを使用して 差集合 処理を実行します。 779 * 780 * @og.rev 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。 781 * @og.rev 6.2.5.1 (2015/06/12) Modify情報が書かれていると、DELETE状況が再セットされないため、削除されなかった。 782 * 783 * @param masterTbl マスターテーブルモデル 784 * @param masterMap マスターテーブルの主キーマップ 785 * @param slaveMap スレーブテーブルの主キーマップ 786 * 787 * @return 処理結果のテーブルモデル 788 */ 789 private DBTableModel makeMinus( final DBTableModel masterTbl,final Map<String,Integer> masterMap, 790 final Map<String,Integer> slaveMap ) { 791 final DBTableModel table = masterTbl; 792 793 @SuppressWarnings("rawtypes") 794 final Map.Entry[] entry = masterMap.entrySet().toArray( new Map.Entry[masterMap.size()] ); 795 796 // 6.2.5.1 (2015/06/12) 削除する行番号を記憶しておく。 797 final List<Integer> rowList = new ArrayList<>(); 798 799 // table.resetModify(); // 6.2.5.1 (2015/06/12) Modify情報をリセットしておく必要があった。 800 801 final int size = entry.length; 802 for( int i=0; i<size; i++ ) { 803 String key = (String)entry[i].getKey(); 804 if( key != null ) { key = key.toUpperCase(Locale.JAPAN); } // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。 805 if( slaveMap.containsKey( key ) ) { // スレーブにマスタが存在する 806 rowList.add( (Integer)entry[i].getValue() ); // 6.2.5.1 (2015/06/12) rowをセット 807 } 808 } 809 810 // 行番号が変わらないように逆順削除します。 811 // 6.2.5.1 (2015/06/12) rowを削除 812 for( int i=rowList.size()-1 ; i>=0; i-- ) { 813 table.removeValue( rowList.get(i) ); 814 } 815 816 return table; 817 } 818 819 /** 820 * 指定のマスタ,スレーブテーブルを使用して 差分集合 処理を実行します。 821 * 822 * @og.rev 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。 823 * @og.rev 7.4.2.0 (2021/04/30) spanタグでunmatched 表示の中身のタグを削除します。 824 * 825 * @param masterTbl マスターテーブルモデル 826 * @param masterMap マスターテーブルの主キーマップ 827 * @param slaveTbl スレーブテーブルモデル 828 * @param slaveMap スレーブテーブルの主キーマップ 829 * 830 * @return 処理結果のテーブルモデル 831 */ 832 private DBTableModel makeDifference( final DBTableModel masterTbl,final Map<String,Integer> masterMap, 833 final DBTableModel slaveTbl ,final Map<String,Integer> slaveMap ) { 834 final DBTableModel table = masterTbl; 835 836 // テーブルの差分属性のカラム番号を求めます。(マスタ、スレーブ共通) 837 final int[] diffClmNo = makeColumnNo( table,diffKeys ); 838 final int diffClmSize = diffClmNo.length; 839 840 // スレーブからマスタへ値の再セットを行うカラム番号を求めます。(マスタ、スレーブ共通) 841 final int[] modClmNo = makeColumnNo( table,modifyClms ); 842 final int modClmSize = modClmNo.length; 843 844 final int clmSize = table.getColumnCount(); 845 final DBTableModel newTable = DBTableModelUtil.newDBTable(); 846 newTable.init( clmSize ); 847 848 // テーブルの全カラムを新たに作成するテーブルにコピーします。 849 for( int i=0; i<clmSize; i++ ) { 850 newTable.setDBColumn( i,table.getDBColumn( i ) ); 851 } 852 853 // スレーブの先頭カラムが、WRITABLE かどうか 854 final boolean writeFlag = "WRITABLE".equalsIgnoreCase( slaveTbl.getColumnName(0) ); 855 856 @SuppressWarnings("rawtypes") 857 final Map.Entry[] entry = masterMap.entrySet().toArray( new Map.Entry[masterMap.size()] ); 858 859 final int size = entry.length; 860 for( int i=0; i<size; i++ ) { 861 String key = (String)entry[i].getKey(); 862 if( key != null ) { key = key.toUpperCase(Locale.JAPAN); } // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。 863 if( slaveMap.containsKey( key ) ) { // スレーブにマスタが存在する 864 final int mrow = ((Integer)entry[i].getValue()).intValue(); 865 final int srow = slaveMap.get( key ).intValue(); 866 boolean unmatched = false; 867 for( int j=0; j<diffClmSize; j++ ) { 868 final String mval = table.getValue( mrow,diffClmNo[j] ); 869 final String sval = slaveTbl.getValue( srow,diffClmNo[j] ); 870 if( mval != null && !mval.equalsIgnoreCase( sval ) ) { unmatched = true; break; } // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。 871 } 872 873 if( unmatched ) { // 属性情報が異なる場合のみ処理 874 // データのコピー 875 String[] mvals = new String[clmSize]; 876 System.arraycopy( table.getValues( mrow ),0,mvals,0 ,clmSize ); 877 878 // スレーブの値をマスタの値にセットする。 879 for( int j=0; j<modClmSize; j++ ) { 880 final String val = slaveTbl.getValue( srow,modClmNo[j] ); 881 mvals[modClmNo[j]] = val; 882 } 883 newTable.addColumnValues( mvals ); 884 885 if( useDiffData ) { 886 // データのコピー 887 String[] svals = new String[clmSize]; 888 System.arraycopy( slaveTbl.getValues( srow ),0,svals,0 ,clmSize ); 889 if( writeFlag ) { svals[0] = "0"; } // 書き込み不許可 890 for( int j=0; j<diffClmSize; j++ ) { 891 final int dclmNo = diffClmNo[j]; 892 final String mval = mvals[dclmNo]; 893 final String sval = svals[dclmNo]; 894 if( mval != null && !mval.equalsIgnoreCase( sval ) ) { // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。 895 // 7.4.2.0 (2021/04/30) spanタグでunmatched 表示の中身のタグを削除します。 896// svals[dclmNo] = "<span class=\"unmatched\">" + sval + "</span>" ; 897 svals[dclmNo] = "<span class=\"unmatched\">" + StringUtil.htmlFilter(sval) + "</span>" ; 898 } 899 } 900 newTable.addColumnValues( svals ); 901 } 902 } 903 } 904 } 905 return newTable; 906 } 907 908 /** 909 * 指定のマスタ,スレーブテーブルを使用して 値列合成 処理を実行します。 910 * 911 * UNION_VAL は、スレーブテーブルの縦列を横列に割り当てるUNION_CLMの特殊版です。 912 * 処理としては、そのまま割り当てるのではなく、スレーブテーブルの縦列の値と 913 * ヘッダーとして指定する、unionClms の値を比較して、下限値の最大値を割り当てます。 914 * つまり、ヘッダーの値と同じか、それ以下で、最も近い値を割り当てることになります。 915 * これは、時間的な推移を表現することになります。 916 * masterKeys:マスタとスレーブを関連付ける共通のキーとなるカラム 917 * slaveKeys :スレーブの縦列を横列にするための、キーと値のカラム 918 * unionClms :横列にするカラム名。この値とスレーブのキーと比較してセットするかどうかを決めます。 919 * 920 * @og.rev 6.9.1.0 (2018/02/26) 新規追加 921 * 922 * @param masterTbl マスターテーブルモデル 923 * @param mstrKeys マスターキー 924 * @param slaveTbl スレーブテーブルモデル 925 * @param slaveKeys スレーブキー 926 * 927 * @return 処理結果のテーブルモデル 928 */ 929 private DBTableModel makeUnionVal( final DBTableModel masterTbl , final String mstrKeys , final DBTableModel slaveTbl , final String slaveKeys ) { 930 931 final Map<String,List<String[]>> slvMap = makeValMap( slaveTbl,mstrKeys,slaveKeys ); 932 933 final DBTableModel table = makeAddClm( masterTbl ); 934 935 // カラム名をカラム番号に変換します。 936 final int[] keyClmNo = makeColumnNo( table,mstrKeys ); 937 938 final int[] mClmNo = makeColumnNo( table ,unionClms ); 939// final int addSize = mClmNo.length; // unionClms の個数なので、マスタ、スレーブは同一 940 941 final String[] valClms = StringUtil.csv2Array( unionClms ); // 942 943 // 突合せを行い、マッチするカラムについて、値をセットし直します。 944 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 945 for( int row=0; row<table.getRowCount(); row++ ) { 946 final String[] mvals = table.getValues( row ); 947 buf.setLength(0); 948 for( int i=0; i<keyClmNo.length; i++ ) { 949 buf.append( mvals[keyClmNo[i]] ).append( '|' ); 950 } 951 // 主キー連結文字列 952 final String key = buf.toString().toUpperCase(Locale.JAPAN); // 大文字・小文字を区別しない。 953 final List<String[]> slvVals = slvMap.get( key ); 954 if( slvVals != null ) { 955 for( int j=0; j<valClms.length; j++ ) { // テーブルのヘッダー列 956 for( final String[] svals : slvVals ) { // 値の配列 [0]=比較キー [1]=設定値 957 if( svals[0].compareTo( valClms[j] ) <= 0 ) { 958 table.setValueAt( svals[1] , row , mClmNo[j] ); 959 } 960 else { 961 break; // 値が超えれば、終了 962 } 963 } 964 } 965 } 966 } 967 table.resetModify(); 968 969 return table; 970 } 971 972 /** 973 * 指定のマスタ,スレーブテーブルを使用して 列合成(UNION_CLM) 処理を実行します。 974 * 975 * すでに、マスタ にカラムが存在している場合は、値の書き換えを、 976 * カラムが存在しなければ、カラムを追加します。 977 * マスタとスレーブのデータ突合せ時のキーは、slaveKeys になります。 978 * これは、masterMap を使用しないことを意味します。(masterKeys 指定によるGROUP化は 979 * 行います。masterKeys を指定しない場合は、サマライズなしで処理します。) 980 * 具体的には、マスタテーブルの複数の行に対して、スレーブデータの値が設定 981 * される可能性があることを意味します。 982 * 983 * @og.rev 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。 984 * 985 * @param masterTbl マスターテーブルモデル 986 * @param slaveKeys スレーブキー 987 * @param slaveTbl スレーブテーブルモデル 988 * @param slaveMap スレーブテーブルの主キーマップ 989 * 990 * @return 処理結果のテーブルモデル 991 */ 992 private DBTableModel makeUnionClm( final DBTableModel masterTbl ,final String slaveKeys , 993 final DBTableModel slaveTbl ,final Map<String,Integer> slaveMap ) { 994 995 final DBTableModel table = makeAddClm( masterTbl ); 996 997 // カラム名をカラム番号に変換します。 998 final int[] keyClmNo = makeColumnNo( table,slaveKeys ); 999 1000 final int[] mClmNo = makeColumnNo( table ,unionClms ); 1001 final int[] sClmNo = makeColumnNo( slaveTbl,unionClms ); 1002 final int addSize = mClmNo.length; // unionClms の個数なので、マスタ、スレーブは同一 1003 1004 // 突合せを行い、マッチするカラムについて、値をセットし直します。 1005 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); // 6.1.0.0 (2014/12/26) refactoring 1006 for( int row=0; row<table.getRowCount(); row++ ) { 1007 final String[] mvals = table.getValues( row ); 1008 buf.setLength(0); // 6.1.0.0 (2014/12/26) refactoring 1009 for( int i=0; i<keyClmNo.length; i++ ) { 1010 buf.append( mvals[keyClmNo[i]] ).append( '|' ); // 6.0.2.5 (2014/10/31) char を append する。 1011 } 1012 // 主キー連結文字列 1013 final String key = buf.toString().toUpperCase(Locale.JAPAN); // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。 1014 if( slaveMap.containsKey( key ) ) { // スレーブにマスタに対応するデータが存在する 1015 final int slvRow = slaveMap.get( key ).intValue(); 1016 final String[] svals = slaveTbl.getValues( slvRow ); // スレーブの行番号 1017 for( int j=0; j<addSize; j++ ) { 1018 mvals[mClmNo[j]] = svals[sClmNo[j]]; 1019 } 1020 table.setValues( mvals,row ); 1021 } 1022 } 1023 table.resetModify(); 1024 1025 return table; 1026 } 1027 1028 /** 1029 * 指定のマスタテーブルに 列追加 処理を実行します。 1030 * 1031 * すでに、マスタ にカラムが存在している場合は、値の書き換えを、 1032 * カラムが存在しなければ、カラムを追加します。 1033 * 1034 * @og.rev 6.9.1.0 (2018/02/26) unionLbls追加 1035 * 1036 * @param masterTbl マスターテーブルモデル 1037 * 1038 * @return 処理結果のテーブルモデル 1039 */ 1040 private DBTableModel makeAddClm( final DBTableModel masterTbl ) { 1041 final String[] addClms = StringUtil.csv2Array( unionClms ); 1042 final int addClmSize = addClms.length; 1043 boolean[] useAdd = new boolean[addClmSize]; 1044 int addSize = 0; 1045 for( int i=0; i<addClmSize; i++ ) { 1046 if( masterTbl.getColumnNo( addClms[i],false ) < 0 ) { // 存在しなければ -1 1047 useAdd[i] = true; 1048 addSize++ ; 1049 } 1050 else { 1051 useAdd[i] = false; // マスタに存在すれば、追加不要 1052 } 1053 } 1054 1055 final int mstrClmSize = masterTbl.getColumnCount(); 1056 final int clmSize = mstrClmSize + addSize; 1057 1058 final DBTableModel table = DBTableModelUtil.newDBTable(); 1059 table.init( clmSize ); 1060 int clmNo = 0; 1061 // マスタテーブルの全カラムを新たに作成するテーブルにコピーします。 1062 for( int i=0; i<mstrClmSize; i++ ) { 1063 table.setDBColumn( clmNo++,masterTbl.getDBColumn( i ) ); 1064 } 1065 1066 // 6.9.1.0 (2018/02/26) unionLbls追加 1067 final String[] addLbls = StringUtil.csv2Array( unionLbls , ',' , addClmSize ); // 無くてもaddClmSize 個の配列を作る。 1068 1069 // 追加するカラムを新たに作成するテーブルに追加します。 1070 String[] addClmVal = new String[addSize]; // 追加分のみ 1071 int addCnt = 0; 1072 for( int i=0; i<addClmSize; i++ ) { 1073 if( useAdd[i] ) { 1074// final DBColumn column = getDBColumn( addClms[i] ); 1075 final DBColumn column = getResource().makeDBColumn( addClms[i] , addLbls[i] ); // 6.9.1.0 (2018/02/26) 1076 addClmVal[addCnt++] = nval( column.getDefault(),"" ); 1077 table.setDBColumn( clmNo++,column ); 1078 } 1079 } 1080 // 一旦、すべてのマスタデータを新テーブルにコピーします。 1081 for( int row=0; row<masterTbl.getRowCount(); row++ ) { 1082 final String[] vals = new String[clmSize]; 1083 System.arraycopy( masterTbl.getValues( row ),0,vals,0 ,mstrClmSize ); // マスタデータのコピー 1084 System.arraycopy( addClmVal,0,vals,mstrClmSize ,addSize ); // 追加列情報の初期値 1085 table.addColumnValues( vals ); 1086 } 1087 1088 return table; 1089 } 1090 1091 /** 1092 * 指定のマスタテーブルを使用して グループ 処理を実行します。 1093 * 1094 * @param masterTbl マスターテーブルモデル 1095 * 1096 * @return 処理結果のテーブルモデル 1097 */ 1098 private DBTableModel makeGroup( final DBTableModel masterTbl ) { 1099 return masterTbl ; 1100 } 1101 1102 /** 1103 * 指定のマスタ,スレーブテーブルを使用して、改廃コード(A,C)を設定します。 1104 * スレーブにデータがあれば、"C" を、なければ、"A" をセットします。 1105 * "D" は、設定できません。 1106 * 1107 * @og.rev 6.3.1.1 (2015/07/10) 改廃コードを設定する CDKH アクションを追加 1108 * 1109 * @param masterTbl マスターテーブルモデル 1110 * @param masterMap マスターテーブルの主キーマップ 1111 * @param slaveTbl スレーブテーブルモデル 1112 * @param slaveMap スレーブテーブルの主キーマップ 1113 * 1114 * @return 処理結果のテーブルモデル 1115 */ 1116 private DBTableModel makeCdkh( final DBTableModel masterTbl,final Map<String,Integer> masterMap, 1117 final DBTableModel slaveTbl ,final Map<String,Integer> slaveMap ) { 1118 final DBTableModel table = masterTbl; 1119 1120 // スレーブからマスタへ値の再セットを行うカラム番号を求めます。(マスタ、スレーブ共通) 1121 final int[] modClmNo = makeColumnNo( table,modifyClms ); 1122 final int modClmSize = modClmNo.length; 1123 1124 @SuppressWarnings("rawtypes") 1125 final Map.Entry[] entry = masterMap.entrySet().toArray( new Map.Entry[masterMap.size()] ); 1126 1127 table.resetModify(); // リセットしておきます。 1128 1129 final int size = entry.length; 1130 for( int i=0; i<size; i++ ) { 1131 String key = (String)entry[i].getKey(); 1132 if( key != null ) { key = key.toUpperCase(Locale.JAPAN); } // 5.5.8.5 (2012/11/27) 大文字・小文字を区別しない。 1133 if( slaveMap.containsKey( key ) ) { // スレーブにマスタが存在する 1134 final int mrow = ((Integer)entry[i].getValue()).intValue(); 1135 table.setModifyType( mrow , "C" ); // 一致すれば、C:変更タイプ 1136 1137 // スレーブの値をマスタの値にセットする。 1138 final int srow = slaveMap.get( key ).intValue(); 1139 for( int j=0; j<modClmSize; j++ ) { 1140 final String val = slaveTbl.getValue( srow,modClmNo[j] ); // スレーブから取り出して 1141 table.setValueAt( val,mrow,modClmNo[j] ); // マスタにセットする。 1142 } 1143 } 1144 } 1145 1146 for( int row=0; row<table.getRowCount(); row++ ) { 1147 if( table.getModifyType( row ).isEmpty() ) { 1148 table.setModifyType( row , "A" ); // 残りを、A:追加タイプ 1149 } 1150 } 1151 1152 return table; 1153 } 1154 1155 /** 1156 * 指定の値がすべてnull(空文字列)の場合、レコードを削除します。 1157 * 1158 * @og.rev 6.7.3.0 (2017/01/27) 指定の値がすべてnull(空文字列)の場合、レコードを削除します。 1159 * 1160 * @param table マップ作成元のデータテーブル(副作用あり) 1161 * @param delClms null(空文字列)判定を行うカラム列(CSV形式) 1162 * 1163 * @return レコードが削除されたテーブル 1164 */ 1165 private DBTableModel deleteRows( final DBTableModel table, final String delClms ) { 1166 1167 // カラム名をカラム番号に変換します。 1168 final int[] clmNo = makeColumnNo( table,delClms ); 1169 final int clmSize = clmNo.length; 1170 if( clmSize == 0 ) { // マスタキー未指定時には全カラムを使用します。 1171 return table; 1172 } 1173 1174 // レコードの削除なので逆順に処理します。 1175 for( int row=table.getRowCount()-1; row>=0; row-- ) { 1176 boolean isNull = true; 1177 for( int i=0; i<clmSize; i++ ) { 1178 if( !StringUtil.isNull( table.getValue( row,clmNo[i] ) ) ) { 1179 isNull = false; 1180 break; 1181 } 1182 } 1183 1184 if( isNull ) { table.removeValue( row ); } 1185 } 1186 1187 return table; 1188 } 1189 1190 /** 1191 * 指定のテーブルと同じテーブルを別オブジェクトとして作成します。 1192 * 1193 * @param tbl コピー元テーブルモデル 1194 * 1195 * @return コピーされた新テーブルモデル 1196 */ 1197 private DBTableModel cloneTable( final DBTableModel tbl ) { 1198 final int clmSize = tbl.getColumnCount(); 1199 1200 final DBTableModel table = DBTableModelUtil.newDBTable(); 1201 table.init( clmSize ); 1202 1203 // テーブルの全カラムを新たに作成するテーブルにコピーします。 1204 for( int i=0; i<clmSize; i++ ) { 1205 table.setDBColumn( i,tbl.getDBColumn( i ) ); 1206 } 1207 // すべてのデータを新テーブルにコピーします。 1208 for( int row=0; row<tbl.getRowCount(); row++ ) { 1209 final String[] vals = new String[clmSize]; 1210 System.arraycopy( tbl.getValues( row ),0,vals,0 ,clmSize ); // データのコピー 1211 table.addColumnValues( vals ); 1212 } 1213 return table; 1214 } 1215 1216 /** 1217 * 指定のテーブルより、カラム列名に対応するカラム番号配列を作成します。 1218 * 1219 * カラム名のCSVデータが 指定されない場合(clmcsv == null)は、長さ0の配列を返します。 1220 * 1221 * @param table テーブルモデル 1222 * @param clmcsv カラム名のCSVデータ 1223 * 1224 * @return カラム名に対応する、列番号配列(なければ、長さ0配列) 1225 */ 1226 private int[] makeColumnNo( final DBTableModel table,final String clmcsv ) { 1227 1228 // マスタテーブルの差分属性のカラム番号を求めます。 1229 final String[] clms = StringUtil.csv2Array( clmcsv ); 1230 int[] clmNo = new int[clms.length]; 1231 1232 // カラム名をカラム番号に変換します。 1233 for( int i=0; i<clms.length; i++ ) { 1234 clmNo[i] = table.getColumnNo( clms[i] ); 1235 } 1236 1237 return clmNo; 1238 } 1239 1240 /** 1241 * 【TAG】コマンド (NEW,RENEW)をセットします(初期値:NEW)。 1242 * 1243 * @og.tag 1244 * コマンドは,HTMLから(get/post)指定されますので,CMD_xxx で設定される 1245 * フィールド定数値のいづれかを、指定できます。 1246 * 初期値は NEW です。 1247 * 1248 * @param cmd コマンド (public static final 宣言されている文字列) 1249 * @see <a href="../../../../constant-values.html#org.opengion.hayabusa.taglib.TableMergeTag.CMD_NEW">コマンド定数</a> 1250 */ 1251 public void setCommand( final String cmd ) { 1252 final String cmd2 = getRequestParameter( cmd ); 1253 if( cmd2 != null && cmd2.length() > 0 ) { command = cmd2.toUpperCase(Locale.JAPAN); } 1254 } 1255 1256 /** 1257 * 【TAG】アクションを指定します[UNION_ALL/UNION/INTERSECT/MINUS/DIFFERENCE/UNION_CLM/UNION_VAL/ADD_CLM/GROUP/UNION_SELROW/CDKH/TABLE_REMOVE]。 1258 * 1259 * @og.tag 1260 * 指定できるアクションは、全体集合(UNION_ALL)、全体集合(挿入位置指定)(UNION_SELROW)、和集合(UNION) 1261 * 、積集合(INTERSECT)、差集合(MINUS)、差分集合(DIFFERENCE)、列合成(UNION_CLM)、値列合成(UNION_VAL)、列追加(ADD_CLM)、 グループ(GROUP)です。 1262 * 列合成とグループ以外の処理では、カラム順とカラム数は同数でなければなりません。 1263 * primaryKeys や unionClms などの指定のキー名は、マスタテーブルに存在する必要があります。 1264 * マスタテーブルと同じカラム番号でスレーブテーブルよりデータを読み出します。 1265 * (カラム名や属性は、異なってもかまいませんが、マスタテーブルに準拠します。) 1266 * また、単独(マスタテーブルのみ)で、和集合と同等の、グループ(GROUP)を使用すると、指定の 1267 * カラムでのユニーク化を行うことが可能になります。グループ処理では、先行優先とし、 1268 * 2回目に現れた情報を削除することになります。グループ が指定された場合は、 1269 * スレーブテーブルは無視されます。いずれの処理においても、集合処理を行う主キーで 1270 * 一旦グループ化されます。全体集合(UNION_ALL)で処理する場合でも、主キーがユニークで 1271 * ない場合は、マスター、スレーブの各テーブルで一旦グループ化された後で、マージされます。 1272 * (マージ後には、同一主キーを持つ行は存在します。) 1273 * 全体集合(UNION_ALL)の場合のみ、mergeKeys を指定する必要はありません。その場合は、 1274 * キーなしのため、マスターとスレーブのテーブルを単に合成するだけになります。 1275 * 1276 * <table class="plain"> 1277 * <caption>アクションの説明</caption> 1278 * <tr><th>action </th><th>名称 </th><th>処理概要 </th><th>1</th><th>2</th><th>3</th><th>4</th><tr> 1279 * <tr><td>UNION_ALL </td><td>全体集合</td><td>マスタとスレーブを合成 </td><td>○</td><td>○</td><td> </td><td> </td><tr> 1280 * <tr><td>UNION </td><td>和集合 </td><td>マスタとスレーブのユニーク部のみ合成 </td><td>○</td><td>○</td><td> </td><td> </td><tr> 1281 * <tr><td>INTERSECT </td><td>積集合 </td><td>マスタとスレーブのユニーク部が一致するマスタのみ選択 </td><td>○</td><td>○</td><td> </td><td> </td><tr> 1282 * <tr><td>MINUS </td><td>差集合 </td><td>マスタからスレーブに存在するユニーク部を削除した残り </td><td>○</td><td>○</td><td> </td><td> </td><tr> 1283 * <tr><td>DIFFERENCE </td><td>差分集合</td><td>ユニーク部が一致し、差分カラムが異なるマスタのみ選択 </td><td>○</td><td>○</td><td> </td><td>○</td><tr> 1284 * <tr><td>UNION_CLM </td><td>列合成 </td><td>マスタとキー一致するスレーブのカラム情報を追加 </td><td>○</td><td>○</td><td>○</td><td> </td><tr> 1285 * <tr><td>UNION_VAL </td><td>値列合成</td><td>マスタとキー一致するスレーブの縦列をカラムに追加 </td><td>○</td><td>○</td><td>○</td><td> </td><tr> 1286 * <tr><td>ADD_CLM </td><td>列追加 </td><td>UNION_CLMとの違いは、カラムのみ追加することです。 </td><td> </td><td> </td><td>○</td><td> </td><tr> 1287 * <tr><td>GROUP </td><td>グループ</td><td>マスタのユニーク部化 </td><td> </td><td> </td><td> </td><td> </td><tr> 1288 * <tr><td>UNION_SELROW</td><td>全体集合</td><td>マスタとスレーブを合成(マスタ表のチェック行を起点に追加 </td><td>○</td><td> </td><td> </td><td> </td><tr> 1289 * <tr><td>CDKH </td><td>改廃CD </td><td>改廃コードを設定 </td><td>○</td><td>○</td><td> </td><td>○</td><tr> 1290 * <tr><td>TABLE_REMOVE</td><td>TBL削除 </td><td>マスタテーブルオブジェクトを無条件で、削除します </td><td> </td><td> </td><td> </td><td> </td><tr> 1291 * </table> 1292 * 1293 * ※:マスタテーブルオブジェクトは、常に必須 1294 * 1:スレーブテーブルオブジェクト必須 1295 * 2:masterKeys 属性必須 1296 * 3:unionClms 属性必須(スレーブテーブルのカラム名または追加するカラム名) 1297 * 4:diffKeys 属性必須(DIFFERENCE時の差分カラム名)、modifyClms 属性使用可能 1298 * 1299 * @param action アクション [UNION_ALL/UNION/INTERSECT/MINUS/DIFFERENCE/UNION_CLM/ADD_CLM/GROUP/UNION_SELROW/CDKH] 1300 */ 1301 public void setAction( final String action ) { 1302 this.action = nval( getRequestParameter( action ),this.action ); 1303 } 1304 1305 /** 1306 * 【TAG】出力先のtableIdを指定します 1307 * (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。 1308 * 1309 * @og.tag 1310 * 集合処理結果の DBTableModel をメモリにセットする場合のキー(tableId)を指定します。 1311 * (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。 1312 * 1313 * @param id テーブルID (sessionに登録する時のID) 1314 * @see org.opengion.hayabusa.common.HybsSystem#TBL_MDL_KEY 1315 */ 1316 public void setTableId( final String id ) { 1317 tableId = nval( getRequestParameter( id ),tableId ); 1318 } 1319 1320 /** 1321 * 【TAG】出力先のscopeを指定します(初期値:session)。 1322 * 1323 * @og.tag 1324 * 集合処理結果の DBTableModel をメモリにセットする場合のスコープを指定します。 1325 * ここでは、マスタやスレーブのスコープ設定が必要な為、superクラスのメソッドを 1326 * オーバーライドしてこのオブジェクト内でキープしています。 1327 * 初期値は、session です。 1328 * 1329 * @param scope 出力先のscope 1330 */ 1331 @Override 1332 public void setScope( final String scope ) { 1333 this.scope = nval( getRequestParameter( scope ),this.scope ); 1334 } 1335 1336 /** 1337 * 【TAG】マスタテーブルのtableIdを指定します 1338 * (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。 1339 * 1340 * @og.tag 1341 * 集合処理のマスタとなる DBTableModel をメモリから取り出す場合のキー(tableId)を指定します。 1342 * (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。 1343 * 1344 * @param masterTableId マスタテーブルID 1345 * @see org.opengion.hayabusa.common.HybsSystem#TBL_MDL_KEY 1346 */ 1347 public void setMasterTableId( final String masterTableId ) { 1348 this.masterTableId = nval( getRequestParameter( masterTableId ),this.masterTableId ); 1349 } 1350 1351 /** 1352 * 【TAG】マスタテーブルのscopeを指定します(初期値:session)。 1353 * 1354 * @og.tag 1355 * 集合処理のマスタとなる DBTableModel をメモリから取り出す場合のスコープを指定します。 1356 * 初期値は、session です。 1357 * 1358 * @param masterScope マスタスコープ 1359 */ 1360 public void setMasterScope( final String masterScope ) { 1361 this.masterScope = nval( getRequestParameter( masterScope ),this.masterScope ); 1362 } 1363 1364 /** 1365 * 【TAG】マスタテーブルの集合処理を行う主キーを指定します。 1366 * 1367 * @og.tag 1368 * 集合処理を行う場合の、カラム名を、CSV形式(CSV形式)で指定します。 1369 * このキーの組み合わせを元に、集合処理の突合せを行います。 1370 * なお、アクションがグループ(GROUP)以外の処理では、マスタとスレーブのカラム数と 1371 * 並び順は、同じでなければなりません。カラム名は、各々別々でもかまいません。 1372 * アクションが全体集合(UNION_ALL)以外の場合は、必須属性になります。 1373 * 1374 * @param masterKeys マスタテーブル主キー (CSV形式) 1375 */ 1376 public void setMasterKeys( final String masterKeys ) { 1377 this.masterKeys = nval( getRequestParameter( masterKeys ),this.masterKeys ); 1378 } 1379 1380 /** 1381 * 【TAG】スレーブテーブルの集合処理を行う主キーを指定します。 1382 * 1383 * @og.tag 1384 * 集合処理を行う場合の、カラム名を、CSV形式(CSV形式)で指定します。 1385 * このキーの組み合わせを元に、集合処理の突合せを行います。 1386 * なお、アクションがグループ(GROUP)以外の処理では、マスタとスレーブのカラム数と 1387 * 並び順は、同じでなければなりません。カラム名は、各々別々でもかまいません。 1388 * null の場合は、masterKeys と同じとします。 1389 * 1390 * @param slaveKeys スレーブ主キー (CSV形式) 1391 */ 1392 public void setSlaveKeys( final String slaveKeys ) { 1393 this.slaveKeys = nval( getRequestParameter( slaveKeys ),this.slaveKeys ); 1394 } 1395 1396 /** 1397 * 【TAG】マスタテーブルのDIFFERENCE時の差分カラム名を(CSV形式)指定します。 1398 * 1399 * @og.tag 1400 * アクションが差分処理(DIFFERENCE)の場合に、差分チェックを行うカラム名を、 1401 * CSV形式(CSV形式)で指定します。 1402 * 差分処理とは、masterKeys で指定されたキーでは、マスタ、スレーブともに存在し 1403 * かつ、このキー(diffKeys)で指定されたキーの値が異なるマスタレコードを 1404 * 抜き出します。 1405 * つまり、主キーは存在し、属性が異なる情報のピックアップになりますので、 1406 * データ更新(UPDATE)対象を見つける場合に使用できます。 1407 * アクションが差分処理(DIFFERENCE)の場合は、必須属性になります。 1408 * 1409 * @param diffKeys 差分カラム名 (CSV形式) 1410 * @see #setMasterKeys( String ) 1411 */ 1412 public void setDiffKeys( final String diffKeys ) { 1413 this.diffKeys = nval( getRequestParameter( diffKeys ),this.diffKeys ); 1414 } 1415 1416 /** 1417 * 【TAG】スレーブテーブルのtableIdを指定します 1418 * (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。 1419 * 1420 * @og.tag 1421 * 集合処理のスレーブとなる DBTableModel をメモリから取り出す場合のキー(tableId)を指定します。 1422 * なお、アクションがグループ(GROUP)の場合は、スレーブテーブルは使用されません。 1423 * (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。 1424 * 1425 * @param slaveTableId スレーブテーブルID 1426 * @see org.opengion.hayabusa.common.HybsSystem#TBL_MDL_KEY 1427 */ 1428 public void setSlaveTableId( final String slaveTableId ) { 1429 this.slaveTableId = nval( getRequestParameter( slaveTableId ),this.slaveTableId ); 1430 } 1431 1432 /** 1433 * 【TAG】スレーブテーブルのscopeを指定します(初期値:session)。 1434 * 1435 * @og.tag 1436 * 集合処理のスレーブとなる DBTableModel をメモリから取り出す場合のスコープを指定します。 1437 * なお、アクションがグループ(GROUP)の場合は、スレーブテーブルは使用されません。 1438 * 初期値は、session です。 1439 * 1440 * @param slaveScope スレーブスコープ 1441 */ 1442 public void setSlaveScope( final String slaveScope ) { 1443 this.slaveScope = nval( getRequestParameter( slaveScope ),this.slaveScope ); 1444 } 1445 1446 /** 1447 * 【TAG】スレーブからマスタへ追加するカラム名をCSV形式で指定します。 1448 * 1449 * @og.tag 1450 * アクションが列合成(UNION_CLM)、値列合成(UNION_VAL)または列追加(ADD_CLM)の場合に使用されます。 1451 * 列合成(UNION_CLM)は、マスタとスレーブの主キーに対して、ここで指定のスレーブの 1452 * カラム列名を、マスタの列に追加します。主キーがマッチしない行に関しては、 1453 * カラムの初期値が適用されたデータを作成します。 1454 * 列追加(ADD_CLM)は、マスタテーブルに指定のカラムを追加するだけです、スレーブテーブルは 1455 * 参照しません。よって、主キーも指定不要です。 1456 * 1457 * @param unionClms 列合成カラム名 (CSV形式) 1458 */ 1459 public void setUnionClms( final String unionClms ) { 1460 this.unionClms = nval( getRequestParameter( unionClms ),this.unionClms ); 1461 } 1462 1463 /** 1464 * 【TAG】スレーブからマスタへ追加するラベルをCSV形式で指定します。 1465 * 1466 * @og.tag 1467 * アクションが列合成(UNION_CLM)、値列合成(UNION_VAL)または列追加(ADD_CLM)の場合に、 1468 * unionClmsと共に使用されます。 1469 * 1470 * 特に、値列合成(UNION_VAL)時のカラムは、縦列の値からカラムを作成する関係で、 1471 * 通常カラムリソースに存在しません。 1472 * そこで、キーをそのままヘッダーに表示する場合に、不都合が発生する場合があるため、 1473 * ラベルを置き換える機能を用意します。 1474 * 1475 * @og.rev 6.9.1.0 (2018/02/26) unionLbls属性追加 1476 * 1477 * @param unionLbls 列合成カラム名 (CSV形式) 1478 */ 1479 public void setUnionLbls( final String unionLbls ) { 1480 this.unionLbls = nval( getRequestParameter( unionLbls ),this.unionLbls ); 1481 } 1482 1483 /** 1484 * 【TAG】指定の値がすべてnull(空文字列)の場合、レコードを削除するカラムをCSV形式で指定します。 1485 * 1486 * @og.tag 1487 * アクションが(GROUP)で、指定のカラム列の値が、すべてnull(空文字列)の場合は、 1488 * そのレコード自身を削除します。 1489 * 1490 * @og.rev 6.7.3.0 (2017/01/27) nullDelClms属性追加 1491 * 1492 * @param nullDelClms null(空文字列)の場合、レコードを削除するカラムをCSV形式で指定 1493 */ 1494 public void setNullDelClms( final String nullDelClms ) { 1495 this.nullDelClms = nval( getRequestParameter( nullDelClms ),this.nullDelClms ); 1496 } 1497 1498 /** 1499 * 【TAG】スレーブからマスタへ値を更新するカラム名をCSV形式で指定します。 1500 * 1501 * @og.tag 1502 * アクションが差分処理(DIFFERENCE)、改廃CD(CDKH)の場合に、結果にマスタテーブルが抜き出されますが、 1503 * 更新する場合に、スレーブ特有のユニークキー(例:UNIQ)を用いて更新する場合、 1504 * 指定のカラム値は、スレーブの値にセットしておきたい場合があります。 1505 * ここでは、指定のカラムについて、値だけスレーブからマスタへセットします。 1506 * なお、値の更新については、マスタとスレーブが同一キーという制約があります。 1507 * 1508 * @param modifyClms 値更新カラム名 (CSV形式) 1509 */ 1510 public void setModifyClms( final String modifyClms ) { 1511 this.modifyClms = nval( getRequestParameter( modifyClms ),this.modifyClms ); 1512 } 1513 1514 /** 1515 * 【TAG】集合処理するときに、相違データをCSV連結して残すカラム名をCSV形式で指定します。 1516 * 1517 * @og.tag 1518 * masterKeysで集合処理するときに、通常、最初に見つかった行データのみ残りますが、 1519 * ここに指定したカラムについては、発生都度、自分自身の情報に、CSV形式で連結して 1520 * いきます。 1521 * この操作により、本来削除された情報が、1行のCSV形式で取得できる効果が得られます。 1522 * これは、value タグの action="APPEND" を、DBTableModel に対して実施するような感じです。 1523 * 1524 * @og.rev 5.1.4.0 (2010/03/01) 新規追加 1525 * 1526 * @param groupAddClms 相違データCSV連結 (CSV形式) 1527 */ 1528 public void setGroupAddClms( final String groupAddClms ) { 1529 this.groupAddClms = nval( getRequestParameter( groupAddClms ),this.groupAddClms ); 1530 } 1531 1532 /** 1533 * 【TAG】テーブルモデルに対する副作用の有無[true:ない/false:ある]を指定します(初期値:false:ある)。 1534 * 1535 * @og.tag 1536 * すべての処理で、DBTableModel に対して、ユニーク化やグループ化などの集合処理を 1537 * 行う過程で、マスタテーブルに対して直接処理を行うと、副作用が発生します。 1538 * 同様に、スレーブテーブルにおいても、一旦キー列でグループ化されるため、副作用が 1539 * 発生します。これは、無駄なメモリ領域の確保と、テーブル(マスタ、スレーブとも)の 1540 * コピー処理時間の節約になります。初期値の設定も副作用がある状態になっています。 1541 * ところが、都合によっては、色々な action を連続して行いたい場合など、毎回、 1542 * データベースを検索するよりもメモリ上でコピーしたほうが都合がよいケースでは、 1543 * 副作用が出ないように、noSideEffect="true" に設定します。 1544 * ただし、マスタ、スレーブともテーブルをコピーを行い、結果のテーブルも派生する為、 1545 * 通常、2つの領域(マスタと結果は同じテーブルに書かれる)で良い所を、5つの領域が 1546 * 作成されます。 1547 * 初期値は、副作用がある(noSideEffect="false")です。 1548 * 1549 * @param noSideEffect 処理結果副作用 [true:ない/false:ある] 1550 */ 1551 public void setNoSideEffect( final String noSideEffect ) { 1552 this.noSideEffect = nval( getRequestParameter( noSideEffect ),this.noSideEffect ); 1553 } 1554 1555 /** 1556 * 【TAG】差分のスレーブデータを結果テーブルに追加するかどうかを指定します(初期値:true)。 1557 * 1558 * @og.tag 1559 * アクションが差分処理(DIFFERENCE)の場合に、結果にマスタテーブルが抜き出されますが、 1560 * 差分対象のスレーブデータと比較したい場合があります。 1561 * このフラグを true にセットすると、書込み禁止属性が付いた状態で、スレーブデータが 1562 * 結果テーブルに追加されます。 1563 * なお、この処理では、通常と異なり、マスタテーブルにはグループ化の副作用は発生しますが、 1564 * 結果テーブルは新規に作成され、先頭行に必ず WRITABLE カラムが付加されます。 1565 * 初期値は、true:追加する です。 1566 * 1567 * @param flag 差分追加 [true:追加する/false:追加しない] 1568 */ 1569 public void setUseDiffData( final String flag ) { 1570 useDiffData = nval( getRequestParameter( flag ),useDiffData ); 1571 } 1572 1573 /** 1574 * 【TAG】マスタテーブルの選択行のデータのみを対象に処理を行うかどうか[true:する/false:しない]を指定します(初期値:false)。 1575 * 1576 * @og.tag 1577 * 処理対象のマスタテーブルについて、選択行が指定された場合に、選択行のみを処理対象に 1578 * するか、全件を対象にするかを指定します。 1579 * 積集合や差分集合など通常は、全件を対象にすることになりますが、列合成や列追加など、 1580 * マスタテーブルに対してのみ作用を及ぼす処理では、選択行のみを対象に処理を行う事が 1581 * 考えられます。その場合、初期グループ化と同じで、対象とする行が選択行のみになります。 1582 * 初期値は、false:全件対象 です。 1583 * 1584 * @param flag 選択行のみ対象 [true:選択行のみ/false:全件] 1585 */ 1586 public void setUseCheckOnly( final String flag ) { 1587 useCheckOnly = nval( getRequestParameter( flag ),useCheckOnly ); 1588 } 1589 1590 /** 1591 * 【TAG】マージの結果を表示するかどうか[true:する/false:しない]を指定します(初期値:true)。 1592 * 1593 * @og.tag 1594 * true で、マージ結果を表示します。 false では、何も表示しません(初期値:true) 1595 * マスタテーブルの件数は、通常、キーでグループ化されるため、入力件数と異なります。 1596 * 同様に、スレーブ件数も異なります。結果の件数は、処理結果が現実的かどうかの 1597 * 判断に使用されます。 1598 * 初期値は、true:表示する です。 1599 * 1600 * @param flag 結果表示 [true:する/false:しない] 1601 */ 1602 public void setDisplay( final String flag ) { 1603 display = nval( getRequestParameter( flag ),display ); 1604 } 1605 1606 /** 1607 * 【TAG】検索結果が0件のとき処理を停止するかどうか[true/false]を指定します(初期値:false[続行する])。 1608 * 1609 * @og.tag 1610 * 初期値は、false(続行する)です。 1611 * 1612 * @og.rev 5.7.6.2 (2014/05/16) 新規追加 1613 * 1614 * @param flag 0件時停止 [true:処理を中止する/false:続行する] 1615 */ 1616 public void setStopZero( final String flag ) { 1617 stopZero = nval( getRequestParameter( flag ),stopZero ); 1618 } 1619 1620 /** 1621 * 【TAG】scope="request" のスレーブテーブルをループして使用するかどうか[true/false]を指定します(初期値:false[使用しない])。 1622 * 1623 * action="UNION_CLM" では、カラムを動的に追加していきます。これを、繰り返すことで、特定の処理結果を、 1624 * カラム列にコピーしていく処理が可能になります。(クロステーブル的な処理です。) 1625 * 内容的には、scope="request" の同一の DBTableModel を、初回に、scope="session" にコピーすることで、 1626 * 以後、マスターテーブルとして処理を続けるというものです。 1627 * 初回かどうかを判別するのは、scope="session" の DBTableModel が存在しない場合は、scope="request" のDBTableModel を 1628 * scope="session" に移すだけの処理を行います。 1629 * 以降は、scope="session" に、DBTableModel が存在するはずなので、これを、マスターテーブルとして使用します。 1630 * scope="session" の DBTableModel を消すのは、action="TABLE_REMOVE" です。 1631 * 1632 * @og.tag 1633 * 初期値は、false(使用しない)です。 1634 * 1635 * @og.rev 6.7.3.0 (2017/01/27) useSlaveLoop属性追加 1636 * 1637 * @param flag スレーブテーブルをループして使用するかどうか [true:使用する/false:使用しない] 1638 */ 1639 public void setUseSlaveLoop( final String flag ) { 1640 useSlaveLoop = nval( getRequestParameter( flag ),useSlaveLoop ); 1641 } 1642 1643 /** 1644 * 【TAG】(通常は使いません)タグで処理される処理がメインとなるトランザクション処理かどうかを指定します(初期値:true)。 1645 * 1646 * @og.tag 1647 * この値は、ファイルダウンロード処理に影響します。この値がtrueに指定された時にcommitされたDBTableModelが 1648 * ファイルダウンロードの対象の表になります。 1649 * 1650 * このパラメーターは、通常、各タグにより実装され、ユーザーが指定する必要はありません。 1651 * 但し、1つのJSP内でDBTableModelが複数生成される場合に、前に処理したDBTableModelについてファイルダウンロードをさせたい 1652 * 場合は、後ろでDBTableModelを生成するタグで、明示的にこの値をfalseに指定することで、ファイルダウンロード処理の対象から 1653 * 除外することができます。 1654 * 1655 * @og.rev 5.1.6.0 (2010/05/01) 新規作成 1656 * 1657 * @param flag メイントランザクションかどうか [true:メイン/false:その他] 1658 */ 1659 public void setMainTrans( final String flag ) { 1660 isMainTrans = nval( getRequestParameter( flag ),isMainTrans ); 1661 } 1662 1663 /** 1664 * 【TAG】groupAddClmsで文字列を連結する項目区切り文字をセットします(初期値:",")。 1665 * 1666 * @og.tag 1667 * groupAddClmsで文字列を連結する項目区切り文字をセットします(初期値:",")。 1668 * 初期値は、"," に設定されています。 1669 * 1670 * @og.rev 5.3.1.0 (2011/01/01) 1671 * 1672 * @param sepa 項目区切り文字 (初期値:",") 1673 */ 1674 public void setSeparator( final String sepa ) { 1675 separator = nval( getRequestParameter( sepa ),separator ); 1676 } 1677 1678 /** 1679 * このオブジェクトの文字列表現を返します。 1680 * 基本的にデバッグ目的に使用します。 1681 * 1682 * @return このクラスの文字列表現 1683 * @og.rtnNotNull 1684 */ 1685 @Override 1686 public String toString() { 1687 return ToString.title( this.getClass().getName() ) 1688 .println( "VERSION" ,VERSION ) 1689 .println( "action" ,action ) 1690 .println( "tableId" ,tableId ) 1691 .println( "scope" ,scope ) 1692 .println( "masterTableId" ,masterTableId ) 1693 .println( "masterScope" ,masterScope ) 1694 .println( "slaveTableId" ,slaveTableId ) 1695 .println( "slaveScope" ,slaveScope ) 1696 .println( "masterKeys" ,masterKeys ) 1697 .println( "diffKeys" ,diffKeys ) 1698 .println( "unionClms" ,unionClms ) 1699 .println( "nullDelClms" ,nullDelClms ) 1700 .println( "modifyClms" ,modifyClms ) 1701 .println( "noSideEffect" ,noSideEffect ) 1702 .println( "useDiffData" ,useDiffData ) 1703 .println( "useCheckOnly" ,useCheckOnly ) 1704 .println( "display" ,display ) 1705 .println( "ACTION_LIST" ,ACTION_SET ) 1706 .println( "Other..." ,getAttributes().getAttribute() ) // 6.4.3.4 (2016/03/11) 1707 .fixForm().toString() ; 1708 } 1709}