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 */ 016 package org.opengion.fukurou.process; 017 018 import org.opengion.fukurou.util.Argument; 019 import org.opengion.fukurou.util.SystemParameter; 020 import org.opengion.fukurou.util.StringUtil; 021 import org.opengion.fukurou.util.LogWriter; 022 import org.opengion.fukurou.util.HybsEntry ; 023 import org.opengion.fukurou.util.Closer; 024 import org.opengion.fukurou.model.Formatter; 025 import org.opengion.fukurou.db.ConnectionFactory; 026 027 import java.util.Map ; 028 import java.util.LinkedHashMap ; 029 import java.util.Set ; 030 import java.util.HashSet ; 031 032 import java.sql.Connection; 033 import java.sql.Statement; 034 import java.sql.PreparedStatement; 035 import java.sql.ParameterMetaData; 036 import java.sql.SQLException; 037 038 /** 039 * Process_DBWriter は、上流から受け取ったデータをデータベ?スに書き込? 040 * CainProcess インターフェースの実?ラスです? 041 * 042 * 上?プロセスチェインの??タは上流から下流へと渡されます?)から受け取っ? 043 * LineModel を?に、データベ?スへの書き込みを行います? 044 * 045 * ??タベ?ス接続?等?、ParamProcess のサブクラス(Process_DBParam)に 046 * 設定された接?Connection)を使用します? 047 * 048 * 引数??中にスペ?スを含??合?、ダブルコー??ション("") で括って下さ?? 049 * 引数??の ?』?前後には、スペ?スは挟めません。??key=value の様に 050 * 繋げてください? 051 * 052 * SQL?は、{@DATE.YMDH}等?シス?変数が使用できます? 053 * 054 * @og.formSample 055 * Process_DBWriter -dbid=DBGE -table=GE41 056 * 057 * [ -dbid=DB接続ID ] ??-dbid=DBGE (? Process_DBParam の -configFile で?す?DBConfig.xml ファイルで規? 058 * [ -table=登録??ブルID ] ???????する?合?不要?INSERT する場合???ブルID 059 * [ -sql=検索SQL? ] ??-sql="UPDATE GE41 SET NAME_JA = [NAME_JA],LABEL_NAME = [LABEL_NAME] 060 * WHERE SYSTEM_ID = [SYSTEM_ID] AND CLM = [CLM]" 061 * [ -sqlFile=登録SQL?ァ??? ] ??-sqlFile=update.sql 062 * ?? -sql ?-sqlFile が指定されな??合??table で????ブルに全カラ?insert です? 063 * [ -sql_XXXX=固定? ] ??-sql_SYSTEM_ID=GE 064 * SQL?の{@XXXX}??を指定?固定?で置き換えます? 065 * WHERE SYSTEM_ID='{@SYSTEM_ID}' ?WHERE SYSTEM_ID='GE' 066 * [ -const_XXXX=固定? ] ??-const_FGJ=1 067 * LineModel のキー(const_ に続く??)の値に、固定?を設定します? 068 * キーが異なれ?、?のカラ?を指定できます? 069 * [ -omitClms=AAA,BBB,… ] ??-omitClms=UNIQ,FGJ,DYSET 070 * -table 属?でINSERT?自動作?する場合?取り除くカラ?? 071 * カンマ区?で??できます? 072 * [ -initSql=開始時SQL? ] ??-initSql="DELETE FROM GE41 WHERE FGJ = '9'" 073 * [ -initSqlFile=開始時SQL?ァ??] ??-initSqlFile=update.sql 074 * [ -endSql=終?SQL? ] ??-endSql="UPDATE GE41 SET FGJ = '1'" 075 * [ -endSqlFile=終?SQL?ァ???] ??-endSqlFile=update.sql 076 * [ -commitCnt=commit処??] ???数毎にコミットを発行します?0 の場合?、終?でコミットしません? 077 * [ -display=false|true ] ??結果を標準?力に表示する(true)かしな?false)?初期値:false[表示しない]) 078 * 079 * @version 4.0 080 * @author Kazuhiko Hasegawa 081 * @since JDK5.0, 082 */ 083 public class Process_DBWriter extends AbstractProcess implements ChainProcess { 084 private static final String CNST_KEY = "const_" ; 085 private static final String SQL_KEY = "sql_" ; 086 087 private Connection connection = null; 088 private PreparedStatement pstmt = null; 089 private ParameterMetaData pMeta = null; // 5.1.1.0 (2009/11/11) setObject に、Type を渡す?(PostgreSQL対? 090 private boolean useParamMetaData = false; // 5.1.1.0 (2009/11/11) setObject に、Type を渡す?(PostgreSQL対? 091 092 private String dbid = null; 093 private String sql = null; 094 private String initSql = null; // 5.7.2.2 (2014/01/24) 追? 095 private String endSql = null; // 5.7.2.2 (2014/01/24) 追? 096 private String table = null; 097 private int[] clmNos = null; // ファイルのヘッ??のカラ?号 098 private int commitCnt = 0; // コミットするまとめ件数 099 private boolean display = false; // 表示しな? 100 101 private String[] cnstClm = null; // 固定?を設定するカラ? 102 private int[] cnstClmNos = null; // 固定?を設定するカラ?号 103 private String[] constVal = null; // カラ?号に対応した固定? 104 105 private boolean firstRow = true; // ??の?目 106 private int count = 0; 107 private String[] omitClms = null; // 4.0.0.0 (2007/09/21) table ?時に取り除くカラ? 108 109 private static final Map<String,String> mustProparty ; // ?プロパティ???チェ?用 Map 110 private static final Map<String,String> usableProparty ; // ?プロパティ?整合?チェ? Map 111 112 static { 113 mustProparty = new LinkedHashMap<String,String>(); 114 115 usableProparty = new LinkedHashMap<String,String>(); 116 usableProparty.put( "dbid", "Process_DBParam の -configFile で?す?DBConfig.xml ファイルで規? ); 117 usableProparty.put( "table", "INSERT する場合???ブルID SQL??する?合?不要?" ); 118 usableProparty.put( "sql", "更新SQL?sql or sqlFile ??)" + 119 CR + "? \"UPDATE GE41 " + 120 CR + "SET NAME_JA = [NAME_JA],LABEL_NAME = [LABEL_NAME] " + 121 CR + "WHERE SYSTEM_ID = [SYSTEM_ID] AND CLM = [CLM]\"" ); 122 usableProparty.put( "sqlFile", "登録SQLファイル(sql or sqlFile ??)? update.sql" ); 123 usableProparty.put( "sql_", "SQL?の{@XXXX}??を指定?固定?で置き換えます?" + 124 CR + "WHERE SYSTEM_ID='{@SYSTEM_ID}' ?WHERE SYSTEM_ID='GE'" ); 125 usableProparty.put( "const_", "LineModel のキー(const_ に続く??)の値に、固定?? + 126 CR + "設定します?キーが異なれ?、?のカラ?を指定できます?" + 127 CR + "? -sql_SYSTEM_ID=GE" ); 128 // 4.0.0.0 (2007/09/21) 属?を追? 129 usableProparty.put( "omitClms", "-table 属?でINSERT?自動作?する場合?取り除くカラ?? + 130 CR + "カンマ区?で??できます?" + 131 CR + "? -omitClms=UNIQ,FGJ,DYSET" ); 132 usableProparty.put( "initSql" , "開始時に??実行されるSQL??します?" ); // 5.7.2.2 (2014/01/24) 追? 133 usableProparty.put( "initSqlFile", "開始時に??実行されるSQLファイルを指定します?" ); // 5.7.2.2 (2014/01/24) 追? 134 usableProparty.put( "endSql" , "終?に??実行されるSQL??します?" ); // 5.7.2.2 (2014/01/24) 追? 135 usableProparty.put( "endSqlFile" , "終?に??実行されるSQLファイルを指定します?" ); // 5.7.2.2 (2014/01/24) 追? 136 usableProparty.put( "commitCnt", "?数毎にコミットを発行します?" + 137 CR + "0 の場合?、終?でコミットしません(初期値:0)" ); 138 usableProparty.put( "display", "結果を標準?力に表示する(true)かしな?false)? + 139 CR + "(初期値:false:表示しな?" ); 140 } 141 142 /** 143 * ?ォルトコンストラクター? 144 * こ?クラスは、動??されます??ォルトコンストラクターで? 145 * super クラスに対して、?な初期化を行っておきます? 146 * 147 */ 148 public Process_DBWriter() { 149 super( "org.opengion.fukurou.process.Process_DBWriter",mustProparty,usableProparty ); 150 } 151 152 /** 153 * プロセスの初期化を行います?初めに??、呼び出されます? 154 * 初期処?ファイルオープン??オープン?に使用します? 155 * 156 * @og.rev 4.0.0.0 (2007/09/21) omitClms 属?を追? 157 * @og.rev 5.1.1.0 (2009/11/11) setObject に ParameterMetaData の getParameterType を渡す?(PostgreSQL対? 158 * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData ?ConnectionFactory経由で取得?(PostgreSQL対? 159 * @og.rev 5.7.2.2 (2014/01/24) initSql,initSqlFile,endSql,endSqlFile 追? 160 * 161 * @param paramProcess ??タベ?スの接続???などを持って?オブジェク? 162 */ 163 public void init( final ParamProcess paramProcess ) { 164 Argument arg = getArgument(); 165 166 table = arg.getProparty("table"); 167 sql = arg.getFileProparty("sql","sqlFile",false); 168 initSql = arg.getFileProparty("initSql","initSqlFile",false); // 5.7.2.2 (2014/01/24) 追? 169 endSql = arg.getFileProparty("endSql","endSqlFile",false); // 5.7.2.2 (2014/01/24) 追? 170 commitCnt = arg.getProparty("commitCnt",commitCnt); 171 display = arg.getProparty("display",display); 172 173 dbid = arg.getProparty("dbid"); 174 connection = paramProcess.getConnection( dbid ); 175 // 5.1.1.0 (2009/11/11) setObject に ParameterMetaData の getParameterType を渡す?(PostgreSQL対? 176 // useParamMetaData = ApplicationInfo.useParameterMetaData( connection ); 177 useParamMetaData = ConnectionFactory.useParameterMetaData( dbid ); // 5.3.8.0 (2011/08/01) 178 179 // 取り除くカラ?リストを配?に変換します? 180 String tempClms = arg.getProparty("omitClms",null); 181 if( tempClms != null ) { 182 omitClms = StringUtil.csv2Array( tempClms ); 183 } 184 185 if( sql == null && table == null ) { 186 String errMsg = "sql を指定しな??合?、table を??してください?; 187 throw new RuntimeException( errMsg ); 188 } 189 190 // 3.8.0.1 (2005/06/17) {@DATE.XXXX} 変換処??追? 191 // {@DATE.YMDH} などの??を?yyyyMMddHHmmss 型?日付に置き換えます? 192 // SQL?? {@XXXX} ??の固定?への置き換? 193 HybsEntry[] entry =arg.getEntrys(SQL_KEY); // 配? 194 SystemParameter sysParam = new SystemParameter( sql ); 195 sql = sysParam.replace( entry ); 196 197 // 5.7.2.2 (2014/01/24) initSql,endSql に?@XXXX} ??の置き換えを行います? 198 if( initSql != null ) { 199 SystemParameter sysParam2 = new SystemParameter( initSql ); 200 initSql = sysParam2.replace( entry ); 201 execSql( initSql ); 202 } 203 if( endSql != null ) { 204 SystemParameter sysParam3 = new SystemParameter( endSql ); 205 endSql = sysParam3.replace( entry ); 206 } 207 208 HybsEntry[] cnstKey = arg.getEntrys( CNST_KEY ); // 配? 209 int csize = cnstKey.length; 210 cnstClm = new String[csize]; 211 constVal = new String[csize]; 212 for( int i=0; i<csize; i++ ) { 213 cnstClm[i] = cnstKey[i].getKey(); 214 constVal[i] = cnstKey[i].getValue(); 215 } 216 } 217 218 /** 219 * プロセスの終?行います??に??、呼び出されます? 220 * 終???ファイルクローズ??クローズ?に使用します? 221 * 222 * @og.rev 4.0.0.0 (2007/11/27) commit,rollback,remove 処?追? 223 * @og.rev 5.1.1.0 (2009/11/11) pMeta のクリア 224 * @og.rev 5.7.2.2 (2014/01/24) endSql 処??追? 225 * 226 * @param isOK ト?タルで、OK?たかど?[true:成功/false:失敗] 227 */ 228 public void end( final boolean isOK ) { 229 boolean flag = Closer.stmtClose( pstmt ); 230 pstmt = null; 231 pMeta = null; // 5.1.1.0 (2009/11/11) 232 233 // 5.7.2.2 (2014/01/24) endSql の実? 234 Throwable th2 = null; 235 if( isOK && endSql != null ) { 236 try { execSql( endSql ); } catch (Throwable th) { th2 = th ; } 237 } 238 239 // 5.7.2.2 (2014/01/24) すべて異常がな??合?み、??る様に変更? 240 // if( isOK ) { 241 if( isOK && flag && th2 == null ) { 242 Closer.commit( connection ); 243 } 244 else { 245 Closer.rollback( connection ); 246 } 247 ConnectionFactory.remove( connection,dbid ); 248 249 if( !flag ) { 250 String errMsg = "ス??トメントをクローズ出来ません?; 251 throw new RuntimeException( errMsg ); 252 } 253 254 // 5.7.2.2 (2014/01/24) endSql の実行失敗時の処? 255 if( th2 != null ) { 256 String errMsg = "endSql の実行に失敗しました。sql=[" + endSql + "]" + CR 257 + th2.getMessage() + CR ; 258 throw new RuntimeException( errMsg,th2 ); 259 } 260 } 261 262 /** 263 * 引数の LineModel を??るメソ?です? 264 * 変換処?? LineModel を返します? 265 * 後続??行わな?????タのフィルタリングを行う場?は? 266 * null ??タを返します?つまり?null ??タは、後続??行わな? 267 * フラグの代わりにも使用して?す? 268 * なお?変換処?? LineModel と、オリジナルの LineModel が? 269 * 同?、コピ?(クローン)か?、各処?ソ??決めて?す? 270 * ドキュメントに明記されて???合?、副作用が問題になる?合?? 271 * ???とに自?コピ?(クローン)して下さ?? 272 * 273 * @og.rev 5.1.1.0 (2009/11/11) setObject に ParameterMetaData の getParameterType を渡す?(PostgreSQL対? 274 * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData setNull 対?PostgreSQL対? 275 * @og.rev 5.7.2.2 (2014/01/24) SQL実行エラーを少し詳細に出力します? 276 * 277 * @param data オリジナルのLineModel 278 * 279 * @return 処?換後?LineModel 280 */ 281 public LineModel action( final LineModel data ) { 282 count++ ; 283 // if( display ) { println( data.dataLine() ); } 284 try { 285 if( firstRow ) { 286 pstmt = makePrepareStatement( table,data ); 287 // 5.1.1.0 (2009/11/11) setObject に ParameterMetaData の getParameterType を渡す?(PostgreSQL対? 288 if( useParamMetaData ) { 289 pMeta = pstmt.getParameterMetaData(); 290 } 291 292 int size = cnstClm.length; 293 cnstClmNos = new int[size]; 294 for( int i=0; i<size; i++ ) { 295 cnstClmNos[i] = data.getColumnNo( cnstClm[i] ); 296 } 297 298 firstRow = false; 299 } 300 301 // 固定?置き換え?? 302 for( int j=0; j<cnstClmNos.length; j++ ) { 303 data.setValue( cnstClmNos[j],constVal[j] ); 304 } 305 306 // 5.1.1.0 (2009/11/11) setObject に ParameterMetaData の getParameterType を渡す?(PostgreSQL対? 307 if( useParamMetaData ) { 308 for( int i=0; i<clmNos.length; i++ ) { 309 int type = pMeta.getParameterType( i+1 ); 310 // 5.3.8.0 (2011/08/01) setNull 対? 311 // pstmt.setObject( i+1,data.getValue(clmNos[i]),type ); 312 Object val = data.getValue(clmNos[i]); 313 if( val == null || ( val instanceof String && ((String)val).isEmpty() ) ) { 314 pstmt.setNull( i+1, type ); 315 } 316 else { 317 pstmt.setObject( i+1, val, type ); 318 } 319 } 320 } 321 else { 322 for( int i=0; i<clmNos.length; i++ ) { 323 pstmt.setObject( i+1,data.getValue(clmNos[i]) ); 324 } 325 } 326 327 pstmt.execute(); 328 if( commitCnt > 0 && ( count%commitCnt == 0 ) ) { 329 Closer.commit( connection ); 330 } 331 } 332 catch (SQLException ex) { 333 // 5.7.2.2 (2014/01/24) SQL実行エラーを少し詳細に出力します? 334 String errMsg = "SQL を実行できませんでした? + CR 335 + "errMsg=[" + ex.getMessage() + "]" + CR 336 + "errCode=[" + ex.getErrorCode() + "] State=[" + ex.getSQLState() + "]" + CR 337 + "dbid=[" + dbid + "]" + CR 338 + "sql =[" + sql + "]" + CR 339 + "data=[" + data.dataLine() + "]" + CR ; 340 // String errMsg = "sql=[" + sql + "]" + CR 341 // + "errorCode=[" + ex.getErrorCode() + "] State=[" + ex.getSQLState() + "]" + CR ; 342 throw new RuntimeException( errMsg,ex ); 343 } 344 if( display ) { println( data.dataLine() ); } // 5.1.2.0 (2010/01/01) display の条件変更 345 return data; 346 } 347 348 /** 349 * ?で使用する PreparedStatement を作?します? 350 * 引数?? SQL また?、LineModel から作?した SQL より構築します? 351 * 352 * @og.rev 4.0.0.0 (2007/09/21) omitClms 属?を追? 353 * @og.rev 5.7.2.2 (2014/01/24) SQL実行エラーを少し詳細に出力します? 354 * 355 * @param table 処?象の??ブルID 356 * @param data 処?象のLineModel 357 * 358 * @return PreparedStatementオブジェク? 359 */ 360 private PreparedStatement makePrepareStatement( final String table,final LineModel data ) { 361 if( sql == null ) { 362 StringBuilder buf = new StringBuilder(); 363 String[] names = data.getNames(); 364 365 // カラ?取り除く?? 366 if( omitClms != null ) { 367 Set<String> set = new HashSet<String>(); 368 for( int i=0; i<names.length; i++ ) { 369 set.add( names[i] ); 370 } 371 for( int i=0; i<omitClms.length; i++ ) { 372 set.remove( omitClms[i] ); 373 } 374 names = set.toArray( new String[set.size()] ); 375 } 376 int size = names.length; 377 378 buf.append( "INSERT INTO " ).append( table ).append( " (" ); 379 buf.append( names[0] ); 380 for( int i=1; i<size; i++ ) { 381 buf.append( "," ).append( names[i] ); 382 } 383 buf.append( " ) VALUES ( ?" ); 384 for( int i=1; i<size; i++ ) { 385 buf.append( ",?" ); 386 } 387 buf.append( " )" ); 388 sql = buf.toString(); 389 390 // カラ?号を設定します? 391 clmNos = new int[size]; 392 for( int i=0; i<size; i++ ) { 393 clmNos[i] = data.getColumnNo( names[i] ); // 4.0.0.0 (2007/09/21) 394 } 395 } 396 else { 397 Formatter format = new Formatter( data ); 398 format.setFormat( sql ); 399 sql = format.getQueryFormatString(); 400 clmNos = format.getClmNos(); 401 } 402 403 final PreparedStatement ps ; 404 try { 405 ps = connection.prepareStatement( sql ); 406 } 407 catch (SQLException ex) { 408 // 5.7.2.2 (2014/01/24) SQL実行エラーを少し詳細に出力します? 409 String errMsg = "PreparedStatement を取得できませんでした? + CR 410 + "errMsg=[" + ex.getMessage() + "]" + CR 411 + "errCode=[" + ex.getErrorCode() + "] State=[" + ex.getSQLState() + "]" + CR 412 + "dbid =[" + dbid + "]" + CR 413 + "sql =[" + sql + "]" + CR 414 + "table=[" + table + "]" + CR 415 + "data =[" + data.dataLine() + "]" + CR ; 416 // String errMsg = "PreparedStatement を取得できませんでした? + CR 417 // + "sql=[" + sql + "]" + CR 418 // + "table=[" + table + "]" + CR 419 // + "nameLine=[" + data.nameLine() + "]" ; 420 throw new RuntimeException( errMsg,ex ); 421 } 422 423 return ps; 424 } 425 426 /** 427 * SQL処?実行します? 428 * 主に、initSql,endSqlの実行用です? 429 * ここでは、エラーが発生しても?connection は閉じません? 430 * ?的に、endメソ?で処?れるためです? 431 * 432 * @og.rev 5.7.2.2 (2014/01/24) 新規追? 433 * 434 * @param sql 実行するSQL? 435 */ 436 private void execSql( final String sql ) { 437 Statement stmt = null; 438 try { 439 stmt = connection.createStatement(); 440 stmt.execute( sql ); 441 } 442 catch (SQLException ex) { 443 // 5.7.2.2 (2014/01/24) SQL実行エラーを少し詳細に出力します? 444 String errMsg = "SQL を実行できませんでした? + CR 445 + "errMsg=[" + ex.getMessage() + "]" + CR 446 + "errCode=[" + ex.getErrorCode() + "] State=[" + ex.getSQLState() + "]" + CR 447 + "dbid=[" + dbid + "]" + CR 448 + "sql =[" + sql + "]" + CR ; 449 // String errMsg = "SQL を実行できませんでした? + CR 450 // + "DBID=" + dbid + CR 451 // + "SQL =" + sql ; 452 throw new RuntimeException( errMsg,ex ); 453 } 454 finally { 455 // connection は、endメソ?で処?れます? 456 Closer.stmtClose( stmt ); 457 } 458 } 459 460 /** 461 * プロセスの処?果のレポ?ト表現を返します? 462 * 処??ログラ?、?力件数、?力件数などの??です? 463 * こ???をそのまま、標準?力に出すことで、結果レポ?トと出来るよ? 464 * 形式で出してください? 465 * 466 * @return 処?果のレポ?? 467 */ 468 public String report() { 469 String report = "[" + getClass().getName() + "]" + CR 470 + TAB + "DBID : " + dbid + CR 471 + TAB + "Output Count : " + count ; 472 473 return report ; 474 } 475 476 /** 477 * こ?クラスの使用方法を返します? 478 * 479 * @return こ?クラスの使用方? 480 */ 481 public String usage() { 482 StringBuilder buf = new StringBuilder(); 483 484 buf.append( "Process_DBWriter は、上流から受け取ったデータをデータベ?スに書き込? ).append( CR ); 485 buf.append( "CainProcess インターフェースの実?ラスです?" ).append( CR ); 486 buf.append( CR ); 487 buf.append( "上?プロセスチェインの??タは上流から下流へと渡されます?)から" ).append( CR ); 488 buf.append( "受け取っ?LineModel を?に、データベ?スへの書き込みを行います?" ).append( CR ); 489 buf.append( CR ); 490 buf.append( "??タベ?ス接続?等?、ParamProcess のサブクラス(Process_DBParam)に" ).append( CR ); 491 buf.append( "設定された接?Connection)を使用します?" ).append( CR ); 492 buf.append( CR ); 493 buf.append( "引数??中に空白を含??合?、ダブルコー??ション(\"\") で括って下さ??" ).append( CR ); 494 buf.append( "引数??の ?』?前後には、空白は挟めません。??key=value の様に" ).append( CR ); 495 buf.append( "繋げてください? ).append( CR ); 496 buf.append( CR ); 497 buf.append( "SQL?は、{@DATE.YMDH}等?シス?変数が使用できます?" ).append( CR ); 498 buf.append( CR ).append( CR ); 499 buf.append( getArgument().usage() ).append( CR ); 500 501 return buf.toString(); 502 } 503 504 /** 505 * こ?クラスは、main メソ?から実行できません? 506 * 507 * @param args コマンド引数配? 508 */ 509 public static void main( final String[] args ) { 510 LogWriter.log( new Process_DBWriter().usage() ); 511 } 512 }