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.fukurou.system.OgRuntimeException ; // 6.4.2.0 (2016/01/29) 019import org.opengion.hayabusa.common.HybsSystem; 020import org.opengion.hayabusa.common.HybsSystemException; 021import org.opengion.hayabusa.common.HybsOverflowException; // 6.2.5.0 (2015/06/05) 022import org.opengion.hayabusa.resource.GUIInfo; 023import org.opengion.hayabusa.db.DBColumn; 024import org.opengion.hayabusa.db.ColumnActionListener; // 6.2.2.0 (2015/03/27) 025import org.opengion.hayabusa.io.TableReader; 026import org.opengion.fukurou.db.Transaction; 027import org.opengion.fukurou.util.ErrorMessage; // 6.2.5.0 (2015/06/05) 028import org.opengion.fukurou.util.StringUtil; 029import org.opengion.fukurou.system.Closer ; 030import org.opengion.fukurou.util.ToString; // 6.1.1.0 (2015/01/17) 031import org.opengion.fukurou.util.FileInfo; // 6.2.3.0 (2015/05/01) 032import org.opengion.fukurou.model.Formatter; 033import org.opengion.fukurou.model.ArrayDataModel; 034import org.opengion.fukurou.model.DataModel; // 8.2.1.0 (2022/07/15) 035 036import static org.opengion.fukurou.util.StringUtil.nval ; 037import static org.opengion.fukurou.system.HybsConst.BR; // 6.1.0.0 (2014/12/26) refactoring 038 039import java.sql.Connection; 040import java.sql.PreparedStatement; 041import java.sql.SQLException; 042 043import java.io.File; 044import java.util.List ; // 7.3.0.0 (2021/01/06) 045 046/** 047 * 指定のファイルを直接データベースに登録するデータ入力タグです。 048 * 049 * 通常の readTable などは、DBTableModelオブジェクトを介して全件メモリに 050 * ロードしてから表示させる為、大量データ処理ができません。 051 * このタグでは、直接ファイルを読み取りながらデータベース登録するので 052 * 大量データをバッチ的に登録する場合に使用します。 053 * 054 * 読み取るファイルは、先頭(または実データが現れるまでに) #NAME 行が必要です。 055 * これは、ファイルデータのカラム名を指定しています。また、columns 属性を使用すれば、 056 * ファイルの#NAME 行より優先して(つまり存在していなくても良い)データのカラム名を 057 * 指定することが出来ます。 058 * この#NAME 行は、ファイルのセパレータと無関係に必ずタブ区切りで用意されています。 059 * タグのBODY部に、実行するSQL文を記述します。 060 * このSQL文は、 061 * INSERT INTO GE41 (CLM,NAME_JA,SYSTEM_ID,FGJ,DYSET) 062 * VALUES ([CLM],[NAME_JA],[SYSTEM_ID],'1','{@USER.YMDH}') 063 * と、いう感じで、ファイルから読み込んだ値は、[カラム名]に割り当てられます。 064 * もちろん、通常の固定値(FGJに'1'をセット)や、リクエスト変数(DYSETの{@USER.YMDH}) 065 * なども使用できます。 066 * 067 * ※ 6.2.3.0 (2015/05/01) 068 * BODY部にSQL文を記述しない場合は、table 属性に、INSERTするテーブルIDを指定します。 069 * 070 * ※ 6.2.4.0 (2015/05/15) 071 * omitNames に、WRITABLE と ROWID を、強制的に含めます(無条件)。 072 * 073 * ※ 7.3.1.3 (2021/03/09) DB.NAMES , DB.ORGNAMES 074 * #NAME や columns で指定したカラムは、{@DB.NAMES} で取り出すことが可能です。 075 * ファイルにかかれた、オリジナルの #NAME は、{@DB.ORGNAMES} で取り出すことが可能です。 076 * 077 * ※ このタグは、Transaction タグの対象です。 078 * 079 * @og.formSample 080 * ●形式:<og:directTableInsert filename="[・・・]" ・・・ >INSERT INTO ・・・ </og:directTableInsert > 081 * ●body:あり(EVAL_BODY_BUFFERED:BODYを評価し、{@XXXX} を解析します) 082 * 083 * ●Tag定義: 084 * <og:directTableInsert 085 * readerClass 【TAG】実際に読み出すクラス名の略称(TableReader_**** の ****)をセットします 086 * (初期値:TABLE_READER_DEFAULT_CLASS[={@og.value SystemData#TABLE_READER_DEFAULT_CLASS}]) 087 * 専 commitBatch 【TAG】指定数毎にコミットを発行します(初期値:0 終了までコミットしません) 088 * 専 dbid 【TAG】(通常は使いません)検索時のDB接続IDを指定します(初期値:DEFAULT) 089 * 専 table 【TAG】BODYのSQL文を指定しない場合に使用するテーブルIDを指定します 090 * command 【TAG】コマンド (NEW,RENEW)をセットします(初期値:NEW) 091 * fileURL 【TAG】読取元ディレクトリ名を指定します(初期値:FILE_URL) 092 * filename 【TAG】ファイルを作成するときのファイル名をセットします (初期値:FILE_FILENAME[=file.xls]) 093 * encode 【TAG】ファイルを作成するときのファイルエンコーディング名をセットします(初期値:FILE_ENCODE) 094 * skipRowCount 【TAG】(通常は使いません)データの読み飛ばし件数を設定します 095 * maxRowCount 【TAG】読取時の最大取り込み件数をセットします (初期値:0:[無制限]) 096 * errRowCount 【TAG】読取時の最大エラー件数をセットします (初期値:{@og.value ReadTableTag#ERROR_ROW_COUNT})(0:[無制限]) 097 * separator 【TAG】可変長ファイルを作成するときの項目区切り文字をセットします 098 * columns 【TAG】読取元ファイルのカラム列を、外部(タグ)よりCSV形式で指定します 099 * omitNames 【TAG】読取対象外のカラム列を、外部(タグ)よりCSV形式で指定します 100 * modifyType 【TAG】ファイル取り込み時の モディファイタイプ(A(追加),C(更新),D(削除))を指定します 101 * displayMsg 【TAG】query の結果を画面上に表示するメッセージIDを指定します(初期値:VIEW_DISPLAY_MSG[={@og.value SystemData#VIEW_DISPLAY_MSG}])) 102 * overflowMsg 【TAG】読取データが最大検索数をオーバーした場合に表示するメッセージリソースIDを指定します (初期値:MSG0007[検索結果が、制限行数を超えましたので、残りはカットされました]) 103 * notfoundMsg 【TAG】検索結果がゼロ件の場合に表示するメッセージリソースIDを指定します(初期値:MSG0077[対象データはありませんでした]) 104 * ※ sheetName 【TAG】EXCELファイルを読み込むときのシート名を設定します(初期値:指定なし) 105 * ※ sheetNos 【TAG】EXCELファイルを読み込むときのシート番号を複数設定できます(初期値:0) 106 * ※ sheetConstKeys 【TAG】EXCELファイルを読み込むときの固定値となるカラム名(CSV形式) 107 * ※ sheetConstAdrs 【TAG】EXCELファイルを読み込むときの固定値となるアドレス(行-列,行-列,・・・) 108 * nullBreakClm 【TAG】カラム列に NULL が現れた時点で読取を中止します(複数Sheetの場合は、次のSheetを読みます)。 109 * nullSkipClm 【TAG】カラム列に NULL が現れたレコードは読み飛ばします。 110 * useNumber 【TAG】行番号情報を、使用している/していない[true/false]を指定します(初期値:true) 111 * useRepeatClms 【TAG】読取処理で横持ちデータの繰り返しが存在する場合に、trueを指定します(初期値:false) 7.3.0.0 (2021/01/06) 112 * useRenderer 【TAG】読取処理でKEY:VAL形式のコードリソースから、KEYを取り出す処理を行うかどうかを指定します(初期値:USE_TABLE_READER_RENDERER[=false]) 113 * adjustColumns 【TAG】読取元ファイルのデータ変換を行うカラム列をカンマ指定します 114 * checkColumns 【TAG】読取元ファイルの整合性チェックを行うカラム列をカンマ指定します 115 * useStrict 【TAG】整合性チェック時に、厳密にチェックするかどうか[true/false]を指定します(初期値:true) 7.3.2.0 (2021/03/19) 116 * nullCheck 【TAG】NULL チェックすべきカラム列をCSV形式(CSV形式)で指定します 117 * matchKeys 【TAG】レコードの読取条件指定時のカラム列をCSV形式で指定します 6.4.6.0 (2016/05/27) 118 * matchVals 【TAG】レコードの読取条件指定時のカラム列に対応する正規表現データをCSV形式で指定します 6.4.6.0 (2016/05/27) 119 * language 【TAG】タグ内部で使用する言語コード[ja/en/zh/…]を指定します 120 * stopZero 【TAG】読込件数が0件のとき処理を続行するかどうか[true/false]を指定します(初期値:false[続行する]) 121 * mainTrans 【TAG】(通常は使いません)タグで処理される処理がメインとなるトランザクション処理かどうかを指定します(初期値:false) 122 * tableId 【TAG】(通常は使いません)sessionから所得する DBTableModelオブジェクトの ID 123 * scope 【TAG】キャッシュする場合のスコープ[request/page/session/application]を指定します(初期値:session) 124 * useTimeView 【TAG】処理時間を表示する TimeView を表示するかどうかを指定します 125 * (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。 126 * useSLabel 【TAG】7.0.7.0 (2019/12/13) エラーメッセージにSLABELを利用するかどうか[true/false]を指定します(初期値:false) 127 * useLocal 【TAG】システム定数でクラウド設定されていても、クラウド環境を使用しない場合、trueを指定します(初期値:false) 8.0.1.0 (2021/10/29) 128 * caseKey 【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null) 5.7.7.2 (2014/06/20) 129 * caseVal 【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null) 5.7.7.2 (2014/06/20) 130 * caseNN 【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない) 5.7.7.2 (2014/06/20) 131 * caseNull 【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない) 5.7.7.2 (2014/06/20) 132 * caseIf 【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない) 133 * debug 【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false) 134 * > ... Body ... 135 * </og:directTableInsert> 136 * 137 * ●使用例 138 * <og:directTableInsert 139 * dbid = "ORCL" 接続データベースID(初期値:DEFAULT) 140 * separator = "," ファイルの区切り文字(初期値:タブ) 141 * fileURL = "{@USER.ID}" 読み取り元ディレクトリ名 142 * filename = "{@filename}" 読み取り元ファイル名 143 * encode = "Shift_JIS" 読み取り元ファイルエンコード名 144 * displayMsg = "MSG0040" 登録完了後のメッセージ 145 * columns = "CLM,NAME_JA,LABEL_NAME,KBSAKU,SYSTEM_ID,LANG" 146 * #NAME の代わりに使用するカラム列名 147 * commitBatch = "100" この件数ずつコミットを発行(初期値:無制限) 148 * useColumnCheck = "true" カラムチェックを行うかどうか(初期値:false) 149 * useColumnAdjust = "true" カラム変換を行うかどうか(初期値:false) 150 * nullCheck = "CLM,SYSTEM_ID" NULLチェックを実行します。 151 * > 152 * INSERT INTO GE41 153 * (CLM,NAME_JA,LABEL_NAME,KBSAKU,SYSTEM_ID,LANG, 154 * FGJ,DYSET,DYUPD,USRSET,USRUPD,PGUPD) 155 * VALUES 156 * ([CLM],[NAME_JA],[LABEL_NAME],[KBSAKU],[SYSTEM_ID],[LANG], 157 * '1','{@USER.YMDH}','{@USER.YMDH}','{@USER.ID}','{@USER.ID}','{@GUI.KEY}') 158 * </og:directTableInsert > 159 * 160 * @og.group ファイル入力 161 * 162 * @version 4.0 163 * @author Kazuhiko Hasegawa 164 * @since JDK5.0, 165 */ 166public class DirectTableInsertTag extends ReadTableTag { 167 /** このプログラムのVERSION文字列を設定します。 {@value} */ 168 private static final String VERSION = "7.0.7.0 (2019/12/13)" ; 169 private static final long serialVersionUID = 707020191213L ; 170 171 // 6.2.4.0 (2015/05/15) 無条件でOMITする名称を指定します(WRITABLE,ROWID) 172 private static final String DEFAULT_OMIT = "WRITABLE,ROWID" ; 173 174 // 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更 175 private String dbid ; 176 private int commitBatch ; // コミットするまとめ件数 177 private String sql ; 178 private String table ; // 6.2.3.0 (2015/05/01) 179 private long dyStart ; // 実行時間測定用のDIV要素を出力します。 180 private boolean useTimeView = HybsSystem.sysBool( "VIEW_USE_TIMEBAR" ); // 6.3.6.0 (2015/08/16) 181 private boolean sqlError ; // 8.1.1.0 (2022/02/04) sqlError フラグでの判定は、DirectTableInsertTag のみで行います。 182 183 /** 184 * デフォルトコンストラクター 185 * 186 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor. 187 */ 188 public DirectTableInsertTag() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 189 190 /** 191 * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。 192 * 193 * @og.rev 6.2.2.0 (2015/03/27) ColumnActionListener 対応。 194 * @og.rev 6.2.3.0 (2015/05/01) table属性追加。BODYのSQL文が無くても、table属性で自動生成します。 195 * @og.rev 6.2.4.0 (2015/05/15) 無条件でOMITする名称を指定します。 196 * 197 * @return 後続処理の指示( EVAL_BODY_BUFFERED ) 198 */ 199 @Override 200 public int doStartTag() { 201 dyStart = System.currentTimeMillis(); 202 203 // 6.2.4.0 (2015/05/15) 無条件でOMITする名称を指定します。 204 addOmitNames( DEFAULT_OMIT ); 205 206 // 6.2.3.0 (2015/05/01) table属性追加 207 // 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 208 return table == null || table.isEmpty() 209 ? EVAL_BODY_BUFFERED // Body を評価する。( extends BodyTagSupport 時) 210 : SKIP_BODY ; // Body を評価しない 211 } 212 213 /** 214 * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。 215 * 216 * @og.rev 3.6.0.2 (2004/10/04) SQL文の [カラム] 対応とパーサー機能追加 217 * @og.rev 3.8.6.3 (2006/11/30) SQL 文の前後のスペースを取り除きます。 218 * @og.rev 6.2.3.0 (2015/05/01) table属性追加。BODYのSQL文が無くても、table属性で自動生成します。 219 * 220 * @return 後続処理の指示(SKIP_BODY) 221 */ 222 @Override 223 public int doAfterBody() { 224 sql = getBodyString(); 225 if( sql == null || sql.isEmpty() || sql.trim().isEmpty() ) { 226 final String errMsg = "table属性を指定しない場合は、BODY 部の登録用 Insert/Update文は、必須です。"; 227 throw new HybsSystemException( errMsg ); 228 } 229 230 return SKIP_BODY ; // Body を評価しない 231 } 232 233 /** 234 * #doEndTag() の後続処理を記述します。 235 * 236 * これは、サブクラスで、DBTableModel以外の処理を行う場合に、 237 * 処理内容を分けるために用意します。 238 * 239 * @og.rev 6.2.2.0 (2015/03/27) #afterEnd() メソッド 新規作成。 240 * @og.rev 6.2.5.0 (2015/06/05) AutoReaderの仕様変更。checkColumns エラー処理が抜けていたので、追加します。 241 * @og.rev 7.0.7.0 (2019/12/13) useSLabel 属性を追加。 242 * 243 * @return 後続処理の指示 244 */ 245 @Override 246 protected int afterEnd() { 247 // 6.2.5.0 (2015/06/05) エラー処理の追加 248 final ErrorMessage errMsg = clmAct.getErrorMessage(); 249 if( !errMsg.isOK() ) { 250// jspPrint( TaglibUtil.makeHTMLErrorTable( errMsg,getResource() ) ); 251 jspPrint( TaglibUtil.makeHTMLErrorTable( errMsg,getResource(),useSLabel ) ); // 7.0.7.0 (2019/12/13) 252 return SKIP_PAGE ; 253 } 254 255 // 実行件数の表示 256 // 4.0.0 (2005/11/30) 出力順の変更。一番最初に出力します。 257 if( displayMsg != null && displayMsg.length() > 0 ) { 258 final String status = executeCount + getResource().getLabel( displayMsg ) ; 259 jspPrint( status + BR ); 260 } 261 262 // 5.7.6.2 (2014/05/16) 検索結果の件数を、"DB.COUNT" キーでリクエストにセットする。 263 setRequestAttribute( "DB.COUNT" , String.valueOf( executeCount ) ); 264 265 // 5.7.6.2 (2014/05/16) 件数0件かつ stopZero = true 266 if( executeCount == 0 && stopZero ) { return SKIP_PAGE; } 267 268 final long dyTime = System.currentTimeMillis()-dyStart; 269 270 // 4.0.0 (2005/01/31) セキュリティチェック(データアクセス件数登録) 271 final GUIInfo guiInfo = (GUIInfo)getSessionAttribute( HybsSystem.GUIINFO_KEY ); 272 if( guiInfo != null ) { guiInfo.addWriteCount( executeCount,dyTime,sql ); } 273 274 if( useTimeView ) { // 6.3.6.0 (2015/08/16) 275 // 時間測定用の DIV 要素を出力 276 jspPrint( "<div id=\"queryTime\" value=\"" + (dyTime) + "\"></div>" ); // 3.5.6.3 (2004/07/12) 277 } 278 return EVAL_PAGE ; 279 } 280 281 /** 282 * タグリブオブジェクトをリリースします。 283 * キャッシュされて再利用されるので、フィールドの初期設定を行います。 284 * 285 * @og.rev 3.6.0.2 (2004/10/04) useColumnCheck,useColumnAdjust 属性追加 286 * @og.rev 3.8.0.2 (2005/06/30) nullCheck 属性追加 287 * @og.rev 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更 288 * @og.rev 5.5.7.1 (2012/10/05) skipRowCount追加 289 * @og.rev 5.7.6.2 (2014/05/16) stopZero属性追加 290 * @og.rev 6.2.2.0 (2015/03/27) ColumnActionListener 対応。 291 * @og.rev 6.2.3.0 (2015/05/01) table属性追加 292 */ 293 @Override 294 protected void release2() { 295 super.release2(); 296 dbid = null; 297 commitBatch = 0; // コミットするまとめ件数 298 table = null; // 6.2.3.0 (2015/05/01) 299 useTimeView = HybsSystem.sysBool( "VIEW_USE_TIMEBAR" ); // 6.3.6.0 (2015/08/16) 300 } 301 302 /** 303 * ファイルオブジェクト より読み込み、データベースに書き込みます。 304 * 305 * @og.rev 3.6.0.2 (2004/10/04) カラムオブジェクトのDBType属性の整合性チェック 306 * @og.rev 3.8.0.2 (2005/06/30) nullチェック確認 307 * @og.rev 3.8.5.1 (2006/05/08) 取込データが name 列より少ない場合の対応を追加 308 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為、ApplicationInfoオブジェクトを設定 309 * @og.rev 4.0.0.0 (2005/01/31) CheckColumnDataクラス static 化、引数にResourceManager追加 310 * @og.rev 4.0.0.1 (2007/12/03) try ~ catch ~ finally をきちんと行う。 311 * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応 312 * @og.rev 5.2.2.0 (2010/11/01)) ""で囲われているデータに改行が入っていた場合の対応 313 * @og.rev 5.3.7.0 (2011/07/01) TransactionReal の引数変更 314 * @og.rev 5.3.8.0 (2011/08/01) pstmt.setObject で、useParamMetaData の判定を避けるため、pstmt.setString で代用(PostgreSQL対応) 315 * @og.rev 5.5.7.1 (2012/10/05) omitFirstLine対応 316 * @og.rev 5.7.0.3 (2013/11/22) BufferedReaderのclose処理をこのメソッド内のfinallyで行う 317 * @og.rev 6.2.2.0 (2015/03/27) ColumnActionListener 対応。 318 * @og.rev 6.2.3.0 (2015/05/01) 行読み飛ばし nullSkipClm追加 319 * @og.rev 6.2.4.2 (2015/05/29) executeCount の設定がおかしい。DirectTableInsertTagでは、初期値が -1 のため、件数が1件少なくなっていた。 320 * @og.rev 6.2.5.0 (2015/06/05) AutoReaderの仕様変更。checkColumns エラー処理が抜けていたので、追加します。 321 * @og.rev 6.3.6.1 (2015/08/28) Transaction でAutoCloseableを使用したtry-with-resources構築に対応。 322 * @og.rev 6.4.3.3 (2016/03/04) HybsSystem.newInstance(String,String) への置き換え。 323 * @og.rev 6.6.0.1 (2016/12/07) エンコードが複数ある場合、SQLのパースで、上書きするとカラム名が取れないバグ修正。 324 * @og.rev 7.0.4.3 (2019/07/15) AutoReaderでencode指定の場合、columnNamesを複数回通ることがあるため、executeCountは都度クリアする 325 * 326 * @param file ファイルオブジェクト 327 */ 328 @Override 329 protected void create( final File file ) { 330 331 // 6.3.6.1 (2015/08/28) Transaction で処理 332 try( Transaction tran = getTransaction() ) { 333 /** 334 * ColumnActionListenerインターフェースの内部無名クラス 335 * 336 * @og.rev 6.2.2.0 (2015/03/27) ColumnActionListener 対応。 337 * @og.rev 6.3.6.1 (2015/08/28) Transaction で処理 338 * 339 * @param names カラム名配列 340 */ 341 final ColumnActionListener listener = new ColumnActionListener() { 342 private DBColumn[] dbClms ; 343 private String[] clmKeys ; // SQL文の[カラム名]配列 344 private int commitCount ; 345 346 private int[] clmNos ; 347 private int clmNosLen ; 348 349 private PreparedStatement pstmt ; 350 private final Connection conn = tran.getConnection( dbid ); // 6.3.6.1 (2015/08/28) 351 352 /** 353 * 一連の作業終了時に呼ばれます。 354 * 355 * @og.rev 6.2.2.0 (2015/03/27) ColumnActionListener 対応。 356 * @og.rev 6.3.6.1 (2015/08/28) Transaction で処理 357 * 358 */ 359 @Override 360 public void end() { 361 Closer.stmtClose( pstmt ); 362 tran.commit(); // 6.3.6.1 (2015/08/28) ここでは常にcommit()させる。 363 } 364 365 /** 366 * カラム名の配列が設定された場合に、呼び出されます。 367 * 368 * @og.rev 6.2.2.0 (2015/03/27) ColumnActionListener 対応。 369 * @og.rev 6.2.4.2 (2015/05/29) executeCount の設定がおかしい。DirectTableInsertTagでは、初期値が -1 のため、件数が1件少なくなっていた。 370 * @og.rev 6.3.6.1 (2015/08/28) Transaction で処理 371 * @og.rev 6.4.1.1 (2016/01/16) HybsOverflowException をthrow するとき、最大件数を引数に渡す。 372 * @og.rev 6.4.3.4 (2016/03/11) Formatterに新しいコンストラクターを追加する。 373 * @og.rev 6.6.0.1 (2016/12/07) エンコードが複数ある場合、SQLのパースで、上書きするとカラム名が取れないバグ修正。 374 * @og.rev 6.9.5.0 (2018/04/23) カラム名が存在しない場合に、Exception を throw するかどうかを指定可能にする。 375 * @og.rev 7.0.4.3 (2019/07/15) AutoReaderでencode指定の場合、columnNamesを複数回通ることがあるため、executeCountは都度クリアする 376 * @og.rev 7.3.1.1 (2021/02/25) カラム列を、"DB.NAMES" キーでリクエストにセットする。 377 * 378 * @param names カラム名配列 379 */ 380 @Override 381 public void columnNames( final String[] names ) { 382 String pstSql = null; // 6.6.0.1 (2016/12/07) 383 384 final String[] nms = clmAct.makeNames( names ); 385 386 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..; 387 if( table == null || table.isEmpty() ) { 388 if( sql != null && !sql.isEmpty() ) { 389// final ArrayDataModel nmdata = new ArrayDataModel( nms ); 390// final ArrayDataModel nmdata = new ArrayDataModel( nms,true ); // 6.9.5.0 (2018/04/23) カラム名が存在しない場合に、Exception を throw する 391 final DataModel<String> nmdata = new ArrayDataModel( nms,true ); // 8.2.1.0 (2022/07/15) 392 final Formatter format = new Formatter( nmdata,sql.trim() ); // 6.4.3.4 (2016/03/11) 393 clmNos = format.getClmNos(); // 指定されたセルのカラム番号。存在しなければ、Exception を throw する 394 clmNosLen = clmNos.length ; 395 clmKeys = format.getClmKeys(); 396 pstSql = format.getQueryFormatString(); // 6.6.0.1 (2016/12/07) 397 } 398 } 399 else { 400 if( "FILE.NAME".equals( table ) ) { 401 table = FileInfo.getNAME( file ); 402 } 403 404 clmNosLen = nms.length; 405 clmNos = new int[clmNosLen]; 406 clmKeys = nms; 407 408 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 409 buf.append( "INSERT INTO " ).append( table ).append( " ( " ) 410 .append( String.join( ",",nms ) ) 411 .append( " ) VALUES ( ?" ); 412 clmNos[0] = 0; 413 for( int i=1; i<clmNosLen; i++ ) { 414 clmNos[i] = i; 415 buf.append( ",?" ); 416 } 417 buf.append( ')' ); 418 pstSql = buf.toString(); // 6.6.0.1 (2016/12/07) 419 } 420 421 try { 422 pstmt = conn.prepareStatement( pstSql ); // 6.6.0.1 (2016/12/07) 423 } 424 catch( final SQLException ex ) { 425 final String errMsg = CR + ex.getMessage() + CR 426 + " sql =[" + sql + "]" + CR 427 + " names =[" + StringUtil.array2csv( names ) + "]" + CR // 6.6.0.1 (2016/12/07) 428 + " encode=[" + encode + "]" + CR // 6.6.0.1 (2016/12/07) 429 + " ErrorCode=[" + ex.getErrorCode() + "]" 430 + " State=[" + ex.getSQLState() + "]" + CR ; 431 jspPrint( errMsg ); 432 tran.rollback(); // 6.3.6.1 (2015/08/28) Transaction で処理 433 // tran.close(); // 6.3.6.1 (2015/08/28) Transaction で処理 434 sqlError = true; // 6.3.6.1 (2015/08/28) DirectTableInsertTag でSQLException発生時 435 throw new HybsSystemException( errMsg,ex ); // 6.2.4.2 (2015/05/29) refactoring 436 } 437 438 final StringBuilder nmsBuf = new StringBuilder( BUFFER_MIDDLE ); // 7.3.1.1 (2021/02/25) 439 dbClms = new DBColumn[nms.length]; 440 for( int no=0; no<nms.length; no++ ) { 441 nmsBuf.append( ',' ).append( nms[no] ); // 7.3.1.1 (2021/02/25) 442 dbClms[no] = getDBColumn( nms[no] ); 443 } 444 445 if( nmsBuf.length() > 1 ) { // 7.3.1.1 (2021/02/25) 最初のカンマを削除して、DB.NAMES で登録する。 446 setRequestAttribute( "DB.NAMES" , nmsBuf.substring(1) ); 447 } 448 449 // 6.2.4.2 (2015/05/29) executeCount の設定がおかしい。DirectTableInsertTagでは、初期値が -1 のため、件数が1件少なくなっていた。 450 // 7.0.4.3 (2019/07/15) AutoReaderでencode指定の場合、columnNamesを複数回通ることがあるため、executeCountは都度クリアする 451// executeCount++ ; 452 executeCount = 0; // 7.0.4.3 (2019/07/15) 453 } 454 455 /** 456 * #NAME のオリジナルカラム名配列がそろった段階で、イベントが発生します。 457 * 458 * @og.rev 7.3.1.3 (2021/03/09) #NAMEのオリジナルを取得できるようにします。 459 * 460 * @param names カラム名配列 461 */ 462 @Override 463 public void originalNames( final String[] names ) { 464 if( names != null && names.length > 0 ) { 465 final StringBuilder nmsBuf = new StringBuilder( BUFFER_MIDDLE ); 466 for( int no=0; no<names.length; no++ ) { 467 nmsBuf.append( ',' ).append( names[no] ); 468 } 469 470 if( nmsBuf.length() > 1 ) { // 7.3.1.1 (2021/02/25) 最初のカンマを削除して、DB.NAMES で登録する。 471 setRequestAttribute( "DB.ORGNAMES" , nmsBuf.substring(1) ); 472 } 473 } 474 } 475 476 /** 477 * 1行分のデータが設定された場合に、呼び出されます。 478 * 479 * @og.rev 6.2.2.0 (2015/03/27) ColumnActionListener 対応 480 * @og.rev 6.2.5.0 (2015/06/05) 読み取り件数オーバーフロー時、HybsOverflowException を throw します 481 * @og.rev 6.3.6.1 (2015/08/28) Transaction で処理 482 * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs) 483 * @og.rev 6.4.6.0 (2016/05/27) レコードの読取条件指定を追加。条件に一致しなければ、null が返されます 484 * @og.rev 7.3.0.0 (2021/01/06) 横持ちデータの繰り返し対応 485 * @og.rev 7.3.1.3 (2021/03/09) [J] で、登録件数(1~) を表現する。 486 * 487 * @param vals 文字列値の1行分の配列 488 * @param rowNo 行番号(0~) 489 */ 490 @Override 491 public void values( final String[] vals, final int rowNo ) { 492 if( maxRowCount <= 0 || executeCount <= maxRowCount ) { // 読み取り件数無制限か、最大件数以下の場合 493 // 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs) 494 // 6.4.1.1 (2016/01/16) PMD refactoring. 処理の順番を入れ替えます。 495 if( pstmt == null || clmNos == null ) { 496 final String errMsg = "#columnNames(String[])を先に実行しておいてください。" ; 497 throw new OgRuntimeException( errMsg ); 498 } 499 500 try { 501 // 7.3.0.0 (2021/01/06) 横持ちデータの繰り返し対応。 502 final List<String[]> valsList = clmAct.clmAction( vals , dbClms , rowNo ); // この処理結果が、getErrorMessage() に反映するので、移動不可 503// final String[] newVals = clmAct.clmAction( vals , dbClms , rowNo ); // この処理結果が、getErrorMessage() に反映するので、移動不可 504 505 // 6.2.5.0 (2015/06/05) エラー発生時は、以下の処理は行いません。 506 // 6.4.6.0 (2016/05/27) レコードの読取条件指定を追加。条件に一致しなければ、null が返されます。 507// if( newVals == null || !clmAct.getErrorMessage().isOK() || sqlError ) { return; } // 6.3.6.1 (2015/08/28) 508 if( valsList.isEmpty() || !clmAct.getErrorMessage().isOK() || sqlError ) { return; } // 7.3.0.0 (2021/01/06) 509 510 // 7.3.0.0 (2021/01/06) 横持ちデータの繰り返し対応。 511 for( final String[] newVals : valsList ) { 512 // ※ skipTailCount:末尾から指定行数分は取り込まない処理を入れる場合、newVals[]をListにaddしておき、指定件数以上で先頭を処理する。 513 for( int i=0; i<clmNosLen; i++ ) { 514 // 7.3.1.3 (2021/03/09) [I] で行番号を指定できるようにする。 515// pstmt.setString( i+1,newVals[clmNos[i]] ); 516 if( clmNos[i] >= 0 ) { 517 pstmt.setString( i+1,newVals[clmNos[i]] ); 518 } 519 // 7.3.1.3 (2021/03/09) ここでは行番号をセットします。 520 else if( clmNos[i] == Formatter.SYS_ROWNUM ) { // [KEY.カラム名] , [I] , [ROW.ID] は、行番号指定 521 pstmt.setString( i+1,String.valueOf( rowNo ) ); // ここでは行番号をセットします。 522 } 523 // 7.3.1.3 (2021/03/09) [J] 登録時の件数(1~) 524 else if( clmNos[i] == Formatter.SYS_CNT ) { // [J] 登録時の件数(1~) 525 pstmt.setString( i+1,String.valueOf( executeCount+1 ) ); 526 } 527 else { 528 pstmt.setString( i+1,"" ); // 7.3.1.3 (2021/03/09) エラーでもよいが、空文字列をセットしておきます。 529 } 530 } 531 pstmt.execute(); 532 if( commitBatch > 0 && ( executeCount%commitBatch == 0 ) ) { 533 conn.commit(); // 5.1.9.0 (2010/08/01) Transaction 対応 534 commitCount = executeCount; 535 } 536 executeCount++ ; 537 } 538 } 539 catch( final SQLException ex ) { 540 final String errMsg = CR + ex.getMessage() + CR 541 + " sql =[" + sql + "]" + CR 542 + " encode=[" + encode + "]" + CR // 6.6.0.1 (2016/12/07) 543 + " keys =[" + StringUtil.array2csv( clmKeys ) + "]" + CR 544 + " vals =[" + StringUtil.array2csv( vals ) + "]" + CR 545 + " 行番号=[" + (rowNo+1) + "]" 546 + " 登録件数=[" + commitCount + "]" + CR 547 + " ErrorCode=[" + ex.getErrorCode() + "]" 548 + " State=[" + ex.getSQLState() + "]" + CR ; 549 jspPrint( errMsg ); 550 tran.rollback(); // 6.3.6.1 (2015/08/28) Transaction で処理 551 // tran.close(); // 6.3.6.1 (2015/08/28) Transaction で処理 552 sqlError = true; // 6.3.6.1 (2015/08/28) DirectTableInsertTag でSQLException発生時 553 throw new HybsSystemException( errMsg,ex ); 554 } 555 } 556 else { 557 throw new HybsOverflowException( maxRowCount ); // 6.4.1.1 (2016/01/16) 558 } 559 } 560 561 /** 562 * 新しくEXCELのシートを処理する際に、シート名をセットするときに呼び出されます。 563 * 564 * @og.rev 7.3.1.1 (2021/02/25) 現在実行中のシート名を、"DB.SHEET_NAME" キーでリクエストにセットする。 565 * 566 * @param sheetName 現在実行中のシート名 567 */ 568 @Override 569 public void shtName( final String sheetName ) { 570 setRequestAttribute( "DB.SHEET_NAME" , sheetName ); 571 } 572 }; 573 574 final TableReader reader = HybsSystem.newInstance( "TableReader_" , readerClass ); // 3.5.5.3 (2004/04/09) 575 576 reader.setSeparator( separator ); 577 reader.setColumns( columns ); // 3.5.4.5 (2004/01/23) 、6.2.0.0 (2015/02/27) 削除 578 reader.setUseNumber( useNumber ); // 3.7.0.5 (2005/04/11) 579 reader.setSkipRowCount( skipRowCount ); // 5.1.6.0 (2010/05/01) 580 reader.setDebug( isDebug() ); // 5.5.7.2 (2012/10/09) デバッグ情報を出力するかどうかを指定 581 // 6.2.0.0 (2015/02/27) EXCELでない場合でも、メソッドは呼び出す。(空振りします) 582 reader.setSheetName( sheetName ); // 3.5.4.2 (2003/12/15) 583 reader.setSheetNos( sheetNos ); // 5.5.7.2 (2012/10/09) 複数シートを指定できるようにシート番号を指定できるようにする。 584 reader.setSheetConstData( sheetConstKeys,sheetConstAdrs ) ; // 5.5.8.2 (2012/11/09) 固定値となるカラム名、アドレスの指定 585 reader.setNullBreakClm( nullBreakClm ) ; // 5.5.8.2 (2012/11/09) 取込み条件/Sheet BREAK条件 586 reader.setNullSkipClm( nullSkipClm ) ; // 6.2.3.0 (2015/05/01) 行読み飛ばし 587 // 6.6.0.1 (2016/12/07) setColumnActionListener は、内部処理が走るため、他の設定が終わってから呼び出す。 588 reader.setColumnActionListener( listener ); // 6.2.2.0 (2015/03/27) 589 590 reader.readDBTable( file,encode ); // 6.2.0.0 (2015/02/27) 追加 591 592 listener.end(); // 6.3.6.1 (2015/08/28) tran.commit() は、この、end メソッドで行われる。 593 } 594 } 595 596 // 6.3.6.1 (2015/08/28) Transaction で処理 597 598 /** 599 * 【TAG】(通常は使いません)検索時のDB接続IDを指定します(初期値:DEFAULT)。 600 * 601 * @og.tag 602 * 検索時のDB接続IDを指定します。初期値は、DEFAULT です。 603 * 604 * @param id データベース接続ID 605 */ 606 public void setDbid( final String id ) { 607 dbid = nval( getRequestParameter( id ),dbid ); 608 } 609 610 /** 611 * 【TAG】指定数毎にコミットを発行します(初期値:0 終了までコミットしません)。 612 * 613 * @og.tag 614 * 通常は、全ての処理が正常に終了するか、なにもしないか(トランザクション) 615 * を判断すべきで、途中でのコミットはしません。 616 * しかし、場合によって、件数が異常に多い場合や、再実行可能な場合は、 617 * 途中でコミットして、都度、処理できるものだけを処理してしまうという方法があります。 618 * また、ロールバックエリアの関係などで、データ量が多い場合に、処理時間が異常に 619 * 長くなる事があり、指定件数ごとのコミット機能を用意しています。 620 * 0 に設定すると、終了までコミットしません。初期値は、0 です。 621 * 622 * @param cmtBat コミットを発行する行数 (初期値:0) 623 */ 624 public void setCommitBatch( final String cmtBat ) { 625 commitBatch = nval( getRequestParameter( cmtBat ),commitBatch ); 626 } 627 628 /** 629 * 【TAG】BODYのSQL文を指定しない場合に使用するテーブルIDを指定します。 630 * 631 * @og.tag 632 * 通常は、BODYに記述したSQL文を実行しますが、テーブルIDを指定すると、 633 * INSERT用のSQL文を自動作成します。 634 * その場合は、BODYのSQL文は設定不要です。 635 * また、FILE.NAME という文字列を指定した場合は、file1 に指定した 636 * ファイル名から、拡張子を取り除いた名称をテーブル名として使用します。 637 * 638 * @og.rev 6.2.3.0 (2015/05/01) table属性追加 639 * 640 * @param tbl テーブルID 641 */ 642 public void setTable( final String tbl ) { 643 table = nval( getRequestParameter( tbl ),table ); 644 } 645 646 /** 647 * 【TAG】処理時間を表示する TimeView を表示するかどうか[true:する/false:しない]を指定します 648 * (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。 649 * 650 * @og.tag 651 * true に設定すると、処理時間を表示するバーイメージが表示されます。 652 * これは、DB検索、APサーバー処理、画面表示の各処理時間をバーイメージで 653 * 表示させる機能です。処理時間の目安になります。 654 * (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。 655 * 656 * @og.rev 6.3.6.0 (2015/08/16) useTimeView の初期値を、VIEW_USE_TIMEBAR にする。 657 * 658 * @param flag 処理時間を表示 [true:する/false:しない] 659 */ 660 public void setUseTimeView( final String flag ) { 661 useTimeView = nval( getRequestParameter( flag ),useTimeView ); 662 } 663 664 /** 665 * このオブジェクトの文字列表現を返します。 666 * 基本的にデバッグ目的に使用します。 667 * 668 * @return このクラスの文字列表現 669 * @og.rtnNotNull 670 */ 671 @Override 672 public String toString() { 673 return ToString.title( this.getClass().getName() ) 674 .println( "VERSION" ,VERSION ) 675 .println( "dbid" ,dbid ) 676 // .println( "clmKeys" ,clmKeys ) 677 .println( "sql" ,sql ) 678 .println( "commitBatch" ,commitBatch ) 679 .println( "Other..." ,getAttributes().getAttribute() ) 680 .fixForm().toString() ; 681 } 682}