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