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.xml; 017 018import org.opengion.fukurou.util.Closer; 019import org.opengion.fukurou.util.LogWriter; 020import org.opengion.fukurou.db.DBUtil; 021 022import java.io.Reader; 023import java.io.BufferedReader; 024import java.io.InputStreamReader; 025import java.io.FileInputStream; 026import java.util.Map; 027import java.util.List; 028import java.util.ArrayList; 029import java.util.regex.Pattern; 030import java.util.regex.Matcher; 031import java.util.Arrays; 032import java.util.Locale; 033 034import java.sql.DriverManager; 035import java.sql.Connection; 036import java.sql.Statement; 037import java.sql.PreparedStatement; 038import java.sql.ParameterMetaData; 039import java.sql.DatabaseMetaData; 040import java.sql.SQLException; 041 042/** 043 * このクラスは、オラクル XDKの oracle.xml.sql.dml.OracleXMLSave クラスと 044 * ほぼ同様の目的で使用できるクラスです。 045 * 拡張XDK形式のXMLファイルを読み込み、データベースに INSERT します。 046 * 047 * 拡張XDK形式の元となる オラクル XDK(Oracle XML Developer's Kit)については、以下の 048 * リンクを参照願います。 049 * <a href="http://otn.oracle.co.jp/software/tech/xml/xdk/index.html" target="_blank" > 050 * XDK(Oracle XML Developer's Kit)</a> 051 * 052 * このクラスでは、MAP を登録する[ setDefaultMap( Map ) ]ことにより、 053 * XMLファイルに存在しないカラムを初期値として設定することが可能になります。 054 * 例えば、登録日や、登録者、または、テンプレートより各システムID毎に 055 * 登録するなどです。 056 * 同様に、読み取った XMLファイルの情報を書き換える機能[ setAfterMap( Map ) ]メソッド 057 * により、カラムの値の置き換えも可能です。 058 * 059 * 拡張XDK形式の元となる オラクル XDK(Oracle XML Developer's Kit)については、以下の 060 * リンクを参照願います。 061 * <a href="http://otn.oracle.co.jp/software/tech/xml/xdk/index.html" target="_blank" > 062 * XDK(Oracle XML Developer's Kit)</a> 063 * 064 * 拡張XDK形式とは、ROW 以外に、SQL処理用タグ(EXEC_SQL)を持つ XML ファイルです。 065 * また、登録するテーブル(table)を ROWSETタグの属性情報として付与することができます。 066 * (大文字小文字に注意) 067 * これは、オラクルXDKで処理する場合、無視されますので、同様に扱うことが出来ます。 068 * この、EXEC_SQL は、それそれの XMLデータをデータベースに登録する際に、 069 * SQL処理を自動的に流す為の、SQL文を記載します。 070 * この処理は、イベント毎に実行される為、その配置順は重要です。 071 * このタグは、複数記述することも出来ますが、BODY部には、1つのSQL文のみ記述します。 072 * 073 * <ROWSET tableName="XX" > 074 * <EXEC_SQL> 最初に記載して、初期処理(データクリア等)を実行させる。 075 * delete from GEXX where YYYYY 076 * </EXEC_SQL> 077 * <MERGE_SQL> このSQL文で UPDATEして、結果が0件ならINSERTを行います。 078 * update GEXX set AA=[AA] , BB=[BB] where CC=[CC] 079 * </MERGE_SQL> 080 * <ROW num="1"> 081 * <カラム1>値1</カラム1> 082 * ・・・ 083 * <カラムn>値n</カラムn> 084 * </ROW> 085 * ・・・ 086 * <ROW num="n"> 087 * ・・・ 088 * </ROW> 089 * <EXEC_SQL> 最後に記載して、項目の設定(整合性登録)を行う。 090 * update GEXX set AA='XX' , BB='XX' where YYYYY 091 * </EXEC_SQL> 092 * <ROWSET> 093 * 094 * @version 4.0 095 * @author Kazuhiko Hasegawa 096 * @since JDK5.0, 097 */ 098public class HybsXMLSave implements TagElementListener { 099 /** システム依存の改行記号をセットします。 */ 100 private static final String CR = System.getProperty("line.separator"); 101 102 private String tableName = null; 103 private String[] keyColumns = null; 104 private Connection connection = null; 105 private PreparedStatement insPstmt = null; // INSERT用の PreparedStatement 106 private PreparedStatement updPstmt = null; // UPDATE用の PreparedStatement 107 private ParameterMetaData insMeta = null; 108 private ParameterMetaData updMeta = null; 109 private int insCnt = 0; 110 private int updCnt = 0; 111 private int delCnt = 0; 112 private int ddlCnt = 0; // 5.6.7.0 (2013/07/27) DDL文のカウンター 113 private Map<String,String> defaultMap = null; 114 private Map<String,String> afterMap = null; 115 private List<String> updClms = null; 116 private String[] insClms = null; 117 private String lastSQL = null; // 5.6.6.1 (2013/07/12) デバッグ用。最後に使用したSQL文 118 119 private final boolean useParamMetaData ; // 4.0.0.0 (2007/09/25) 120 121 // UPDATE時の [XXX] を取り出します。\w は、単語構成文字: [a-zA-Z_0-9]と同じ 122 private static final Pattern pattern = Pattern.compile( "\\[\\w*\\]" ); 123 124 // 5.6.9.2 (2013/10/18) EXEC_SQL のエラーを無視するかどうかを指定できます。 125 private boolean isExecErrException = true; // true は、エラー時に Exception を発行します。 126 127 /** 128 * コネクションを指定して、オブジェクトを構築します。 129 * テーブル名は、拡張XDK形式のROWSETタグのtableName属性に 130 * 記述しておく必要があります。 131 * 132 * @param conn データベース接続 133 */ 134 public HybsXMLSave( final Connection conn ) { 135 this( conn,null ); 136 } 137 138 /** 139 * コネクションとテーブル名を指定して、オブジェクトを構築します。 140 * ここで指定するテーブル名は、デフォルトテーブルという扱いです。 141 * 拡張XDK形式のROWSETタグのtableName属性にテーブル名が記述されている場合は、 142 * そちらが優先されます。 143 * 144 * @og.rev 4.0.0.0 (2007/09/25) ParameterMetaData を使用したパラメータ設定追加。 145 * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData を このクラスで直接取得する。(PostgreSQL対応) 146 * 147 * @param conn データベース接続 148 * @param table テーブル名(ROWSETタグのtable属性が未設定時に使用) 149 */ 150 public HybsXMLSave( final Connection conn,final String table ) { 151 connection = conn; 152 tableName = table; 153// useParamMetaData = ApplicationInfo.useParameterMetaData( conn ); 154 useParamMetaData = useParameterMetaData( connection ); // 5.3.8.0 (2011/08/01) 155 } 156 157 /** 158 * EXEC_SQL のエラー時に Exception を発行するかどうかを指定できます(初期値:true)。 159 * true を指定すると、エラー時には、 RuntimeException を throw します。 160 * false にすると、標準エラー出力にのみ、出力します。 161 * このフラグは、EXEC_SQL のみ有効です。それ以外のタブの処理では、エラーが発生すると 162 * その時点で、Exception を発行して、処理を終了します。 163 * 初期値は、true(Exception を発行する) です。 164 * 165 * @og.rev 5.6.9.2 (2013/10/18) 新規追加 166 * 167 * @param flag true:Exception を発行する/false:標準エラー出力に出力する 168 */ 169 public void onExecErrException( final boolean flag ) { 170 isExecErrException = flag; 171 } 172 173 /** 174 * <ROWSET> タグの一番最初に呼び出されます。 175 * ROWSET の属性である、table 属性と、dbid 属性 を、TagElement の 176 * get メソッドで取得できます。 177 * 取得時のキーは、それぞれ、"TABLE" と "DBID" です。 178 * 179 * @param tag タグエレメント 180 * @see org.opengion.fukurou.xml.TagElement 181 * @see HybsXMLHandler#setTagElementListener( TagElementListener ) 182 */ 183 public void actionInit( final TagElement tag ) { 184 String table = tag.get( "tableName" ); 185 if( table != null ) { tableName = table; } 186 } 187 188 /** 189 * <ROW> タグの endElement 処理毎に呼び出されます。 190 * この Listener をセットすることにより、行データを取得都度、 191 * TagElement オブジェクトを作成し、このメソッドが呼び出されます。 192 * 193 * @og.rev 4.0.0.0 (2007/05/09) ParameterMetaData を使用したパラメータ設定追加。 194 * @og.rev 4.0.0.0 (2007/09/25) isOracle から useParamMetaData に変更 195 * @og.rev 4.3.7.0 (2009/06/01) HSQLDB対応 196 * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData setNull 対応(PostgreSQL対応) 197 * @og.rev 5.6.6.1 (2013/07/12) lastSQL 対応。デバッグ用に、最後に使用したSQL文を残します。 198 * 199 * @param tag タグエレメント 200 * @see org.opengion.fukurou.xml.TagElement 201 * @see HybsXMLHandler#setTagElementListener( TagElementListener ) 202 */ 203 public void actionRow( final TagElement tag ) { 204 tag.setAfterMap( afterMap ); 205 206 String[] vals = null; // 5.6.6.1 (2013/07/12) デバッグ用 207 try { 208 // 更新SQL(MERGE_SQLタグ)が存在する場合の処理 209 int tempCnt = 0; 210 if( updPstmt != null ) { 211// String[] vals = tag.getValues( updClms ); 212 vals = tag.getValues( updClms ); // 5.6.6.1 (2013/07/12) デバッグ用 213 for( int j=0; j<vals.length; j++ ) { 214 // 4.3.7.0 (2009/06/01) HSQLDB対応。空文字の場合nullに置換え 215 if( vals[j] != null && vals[j].length() == 0 ){ 216 vals[j] = null; 217 } 218 219 // 4.0.0.0 (2007/09/25) ParameterMetaData を使用したパラメータ設定追加 220 if( useParamMetaData ) { 221 int type = updMeta.getParameterType( j+1 ); 222 // 5.3.8.0 (2011/08/01) setNull 対応 223// updPstmt.setObject( j+1,vals[j],type ); 224 String val = vals[j]; 225 if( val == null || val.isEmpty() ) { 226 updPstmt.setNull( j+1, type ); 227 } 228 else { 229 updPstmt.setObject( j+1, val, type ); 230 } 231 } 232 else { 233 updPstmt.setObject( j+1,vals[j] ); 234 } 235 236 } 237 tempCnt = updPstmt.executeUpdate(); 238 if( tempCnt > 1 ) { 239 String errMsg = "Update キーが重複しています。" 240 + "TABLE=[" + tableName + "] ROW=[" 241 + tag.getRowNo() + "]" + CR 242 + " SQL=[" + lastSQL + "]" + CR // 5.6.6.1 (2013/07/12) デバッグ用 243 + tag.toString() + CR 244 + Arrays.toString( vals ) + CR ; // 5.6.6.1 (2013/07/12) デバッグ用 245 throw new RuntimeException( errMsg ); 246 } 247 updCnt += tempCnt; 248 } 249 // 更新が 0件の場合は、INSERT処理を行います。 250 if( tempCnt == 0 ) { 251 // 初回INSERT時のタグより、DB登録SQL文を構築します。 252 if( insPstmt == null ) { 253 insClms = tag.getKeys(); 254// String sql = insertSQL( insClms,tableName ); 255// insPstmt = connection.prepareStatement( sql ); 256 lastSQL = insertSQL( insClms,tableName ); // 5.6.6.1 (2013/07/12) デバッグ用 257 insPstmt = connection.prepareStatement( lastSQL ); 258 // 4.0.0.0 (2007/09/25) ParameterMetaData を使用したパラメータ設定追加 259 if( useParamMetaData ) { insMeta = insPstmt.getParameterMetaData(); } 260 } 261// String[] vals = tag.getValues( insClms ); 262 vals = tag.getValues( insClms ); // 5.6.6.1 (2013/07/12) デバッグ用 263 for( int j=0; j<vals.length; j++ ) { 264 // 4.3.7.0 (2009/06/01) HSQLDB対応。空文字の場合nullに置換え 265 if( vals[j] != null && vals[j].length() == 0 ){ 266 vals[j] = null; 267 } 268 269 // 4.0.0.0 (2007/09/25) ParameterMetaData を使用したパラメータ設定追加 270 if( useParamMetaData ) { 271 int type = insMeta.getParameterType( j+1 ); 272 // 5.3.8.0 (2011/08/01) setNull 対応 273// insPstmt.setObject( j+1,vals[j],type ); 274 String val = vals[j]; 275 if( val == null || val.isEmpty() ) { 276 insPstmt.setNull( j+1, type ); 277 } 278 else { 279 insPstmt.setObject( j+1, val, type ); 280 } 281 } 282 else { 283 insPstmt.setObject( j+1,vals[j] ); 284 } 285 } 286 insCnt += insPstmt.executeUpdate(); 287 } 288 } 289 catch( SQLException ex ) { 290 String errMsg = "DB登録エラーが発生しました。" 291 + "TABLE=[" + tableName + "] ROW=[" 292 + tag.getRowNo() + "]" + CR 293 + " SQL=[" + lastSQL + "]" + CR // 5.6.6.1 (2013/07/12) デバッグ用 294 + tag.toString() + CR 295 + Arrays.toString( vals ) + CR // 5.6.6.1 (2013/07/12) デバッグ用 296 + ex.getMessage() + ":" + ex.getSQLState() + CR ; 297 throw new RuntimeException( errMsg,ex ); 298 } 299 } 300 301 /** 302 * <EXEC_SQL> タグの endElement 処理毎に呼び出されます。 303 * getBody メソッドを使用して、このタグのBODY部の文字列を取得します。 304 * この Listener をセットすることにより、EXEC_SQL データを取得都度、 305 * TagElement オブジェクトを作成し、このメソッドが呼び出されます。 306 * EXEC_SQL タグでは、delete文やupdate文など、特殊な前処理や後処理用の SQLと 307 * DDL(データ定義言語:Data Definition Language)の処理なども記述できます。 308 * ここでは簡易的に、何か実行された場合は、delete 処理と考え、削除カウントを加算し、 309 * 0件で帰ってきた場合に、DDLが実行されたと考え、DDLカウントを+1します。 310 * ただし、0件 delete も考えられるため、SQL文の先頭文字によるチェックは入れておきます。 311 * 312 * @og.rev 5.6.6.1 (2013/07/12) lastSQL 対応。デバッグ用に、最後に使用したSQL文を残します。 313 * @og.rev 5.6.7.0 (2013/07/27) DDL(データ定義言語:Data Definition Language)の処理件数追加 314 * @og.rev 5.6.9.2 (2013/10/18) EXEC_SQL のエラー時に Exception を発行するかどうかを指定 315 * 316 * @param tag タグエレメント 317 * @see org.opengion.fukurou.xml.TagElement 318 * @see HybsXMLHandler#setTagElementListener( TagElementListener ) 319 */ 320 public void actionExecSQL( final TagElement tag ) { 321 Statement execSQL = null ; 322 try { 323// String sql = tag.getBody(); 324 lastSQL = tag.getBody(); // 5.6.6.1 (2013/07/12) デバッグ用 325 execSQL = connection.createStatement(); 326// delCnt += execSQL.executeUpdate( sql ) ; 327 328 // 5.6.7.0 (2013/07/27) DDL(データ定義言語:Data Definition Language)の処理件数追加 329// delCnt += execSQL.executeUpdate( lastSQL ) ; 330 int cnt = execSQL.executeUpdate( lastSQL ) ; 331 if( cnt > 0 ) { delCnt += cnt; } // 件数が返れば、DDLでないため、削除数を加算 332 else { 333 String sql = lastSQL.trim().toUpperCase( Locale.JAPAN ); 334 if( !sql.startsWith( "DELETE" ) && !sql.startsWith( "INSERT" ) && !sql.startsWith( "UPDATE" ) ) { 335 ddlCnt ++ ; 336 } 337 } 338 } 339 catch( SQLException ex ) { 340 String errMsg = "DB登録エラーが発生しました。" 341 + "TABLE=[" + tableName + "] ROW=[" 342 + tag.getRowNo() + "]" + CR 343 + " SQL=[" + lastSQL + "]" + CR // 5.6.6.1 (2013/07/12) デバッグ用 344 + tag.toString() + CR 345 + ex.getMessage() + ":" + ex.getSQLState() + CR ; 346 347 // 5.6.9.2 (2013/10/18) EXEC_SQL のエラー時に Exception を発行するかどうかを指定 348 349 if( isExecErrException ) { 350 throw new RuntimeException( errMsg,ex ); 351 } 352 else { 353 System.err.println( errMsg ); 354 } 355 } 356 finally { 357 Closer.stmtClose( execSQL ); 358 } 359 } 360 361 /** 362 * <MERGE_SQL> タグの endElement 処理時に呼び出されます。 363 * getBody メソッドを使用して、このタグのBODY部の文字列を取得します。 364 * MERGE_SQLタグは、マージ処理したいデータ部よりも上位に記述しておく 365 * 必要がありますが、中間部に複数回記述しても構いません。 366 * このタグが現れるまでは、INSERT のみ実行されます。このタグ以降は、 367 * 一旦 UPDATE し、結果が 0件の場合は、INSERTする流れになります。 368 * 完全に INSERT のみであるデータを前半に、UPDATE/INSERTを行う 369 * データを後半に、その間に、MERGE_SQL タグを入れることで、無意味な 370 * UPDATE を避けることが可能です。 371 * この Listener をセットすることにより、MERGE_SQL データを取得都度、 372 * TagElement オブジェクトを作成し、このメソッドが呼び出されます。 373 * 374 * @og.rev 4.0.0.0 (2007/05/09) ParameterMetaData を使用したパラメータ設定追加。 375 * @og.rev 4.0.0.0 (2007/09/25) isOracle から useParamMetaData に変更 376 * @og.rev 5.6.6.1 (2013/07/12) lastSQL 対応。デバッグ用に、最後に使用したSQL文を残します。 377 * 378 * @param tag タグエレメント 379 * @see org.opengion.fukurou.xml.TagElement 380 * @see HybsXMLHandler#setTagElementListener( TagElementListener ) 381 */ 382 public void actionMergeSQL( final TagElement tag ) { 383 if( updPstmt != null ) { 384 String errMsg = "MERGE_SQLタグが、複数回記述されています。" 385 + "TABLE=[" + tableName + "] ROW=[" 386 + tag.getRowNo() + "]" + CR 387 + " SQL=[" + lastSQL + "]" + CR // 5.6.6.1 (2013/07/12) デバッグ用 388 + tag.toString() + CR; 389 throw new RuntimeException( errMsg ); 390 } 391 392 String orgSql = tag.getBody(); 393 Matcher matcher = pattern.matcher( orgSql ); 394 updClms = new ArrayList<String>(); 395 while( matcher.find() ) { 396 // ここでは、[XXX]にマッチする為、前後の[]を取り除きます。 397 updClms.add( orgSql.substring( matcher.start()+1,matcher.end()-1 ) ); 398 } 399// String sql = matcher.replaceAll( "?" ); 400 lastSQL = matcher.replaceAll( "?" ); // 5.6.6.1 (2013/07/12) デバッグ用 401 402 try { 403// updPstmt = connection.prepareStatement( sql ); 404 updPstmt = connection.prepareStatement( lastSQL ); 405 // 4.0.0.0 (2007/09/25) ParameterMetaData を使用したパラメータ設定追加 406 if( useParamMetaData ) { updMeta = updPstmt.getParameterMetaData(); } 407 } 408 catch( SQLException ex ) { 409 String errMsg = "Statement作成時にエラーが発生しました。" 410 + "TABLE=[" + tableName + "] ROW=[" 411 + tag.getRowNo() + "]" + CR 412 + " SQL=[" + lastSQL + "]" + CR // 5.6.6.1 (2013/07/12) デバッグ用 413 + tag.toString() + CR 414 + ex.getMessage() + ":" + ex.getSQLState() + CR ; 415 throw new RuntimeException( errMsg,ex ); 416 } 417 } 418 419 /** 420 * UPDATE,DELETE を行う場合の WHERE 条件になるキー配列 421 * このキーの AND 条件でカラムを特定し、UPDATE,DELETE などの処理を 422 * 行います。 423 * 424 * @param keyCols WHERE条件になるキー配列 425 */ 426 public void setKeyColumns( final String[] keyCols ) { 427 keyColumns = new String[keyCols.length]; 428 System.arraycopy( keyCols,0,keyColumns,0,keyColumns.length ); 429 } 430 431 /** 432 * XMLファイルを読み取る前に指定するカラムと値のペア(マップ)情報をセットします。 433 * 434 * このカラムと値のペアのマップは、オブジェクト構築前に設定される為、 435 * XMLファイルにキーが存在している場合は、値が書き変わります。(XML優先) 436 * XMLファイルにキーが存在していない場合は、ここで指定するMapの値が 437 * 初期設定値として使用されます。 438 * ここで指定する Map に LinkedHashMap を使用する場合、カラム順も 439 * 指定することが出来ます。 440 * 441 * @param map 初期設定するカラムデータマップ 442 * @see #setAfterMap( Map ) 443 */ 444 public void setDefaultMap( final Map<String,String> map ) { defaultMap = map; } 445 446 /** 447 * XMLファイルを読み取った後で指定するカラムと値のペア(マップ)情報をセットします。 448 * 449 * このカラムと値のペアのマップは、オブジェクト構築後に設定される為、 450 * XMLファイルのキーの存在に関係なく、Mapのキーと値が使用されます。(Map優先) 451 * null を設定した場合は、なにも処理されません。 452 * 453 * @param map 後設定するカラムデータマップ 454 * @see #setDefaultMap( Map ) 455 */ 456 public void setAfterMap( final Map<String,String> map ) { afterMap = map; } 457 458 /** 459 * データベースに追加処理(INSERT)を行います。 460 * 461 * 先に指定されたコネクションを用いて、指定のテーブルに INSERT します。 462 * 引数には、XMLファイルを指定したリーダーをセットします。 463 * コネクションは、終了後、コミットされます。(close されません。) 464 * リーダーのクローズは、ここでは行っていません。 465 * 466 * @og.rev 5.1.1.0 (2009/11/11) insMeta , updMeta のクリア(気休め) 467 * 468 * @param reader XMLファイルを指定するリーダー 469 */ 470 public void insertXML( final Reader reader ) { 471 try { 472 HybsXMLHandler handler = new HybsXMLHandler(); 473 handler.setTagElementListener( this ); 474 handler.setDefaultMap( defaultMap ); 475 476 handler.parse( reader ); 477 } 478 finally { 479 Closer.stmtClose( insPstmt ); 480 Closer.stmtClose( updPstmt ); 481 insPstmt = null; 482 updPstmt = null; 483 insMeta = null; // 5.1.1.0 (2009/11/11) 484 updMeta = null; // 5.1.1.0 (2009/11/11) 485 } 486 } 487 488 /** 489 * インサート用のSQL文を作成します。 490 * 491 * @param columns インサートするカラム名 492 * @param tableName インサートするテーブル名 493 * 494 * @return インサート用のSQL文 495 */ 496 private String insertSQL( final String[] columns,final String tableName ) { 497 if( tableName == null ) { 498 String errMsg = "tableName がセットされていません。" + CR 499 + "tableName は、コンストラクタで指定するか、ROWSETのtableName属性で" 500 + "指定しておく必要があります" + CR ; 501 throw new RuntimeException( errMsg ); 502 } 503 504 StringBuilder sql = new StringBuilder(); 505 sql.append( "INSERT INTO " ).append( tableName ); 506 sql.append( " ( " ); 507 sql.append( columns[0] ); 508 for( int i=1; i<columns.length; i++ ) { 509 sql.append( "," ).append( columns[i] ); 510 } 511 sql.append( " ) VALUES ( " ); 512 sql.append( "?" ); 513 for( int i=1; i<columns.length; i++ ) { 514 sql.append( "," ).append( "?" ); 515 } 516 sql.append( " )" ); 517 518 return sql.toString(); 519 } 520 521 /** 522 * データベースに追加した件数を返します。 523 * 524 * @return 登録件数 525 */ 526 public int getInsertCount() { return insCnt; } 527 528 /** 529 * データベースを更新した件数を返します。 530 * これは、拡張XDK形式で、MERGE_SQL タグを使用した場合の更新処理件数を 531 * 合計した値を返します。 532 * 533 * @return 更新件数 534 */ 535 public int getUpdateCount() { return updCnt; } 536 537 /** 538 * データベースに変更(更新、削除を含む)した件数を返します。 539 * これは、拡張XDK形式で、EXEC_SQL タグを使用した場合の実行件数を合計した 540 * 値を返します。 541 * よって、更新か、追加か、削除かは、判りませんが、通常 登録前に削除する 542 * ケースで使われることから、deleteCount としています。 543 * 544 * @return 変更件数(主に、削除件数) 545 */ 546 public int getDeleteCount() { return delCnt; } 547 548 /** 549 * データベースにDDL(データ定義言語:Data Definition Language)処理した件数を返します。 550 * これは、拡張XDK形式で、EXEC_SQL タグを使用した場合の実行件数を合計した 551 * 値を返します。 552 * EXEC_SQL では、登録前に削除する delete 処理も、EXEC_SQL タグを使用して実行しますが 553 * その処理と分けてカウントします。 554 * 555 * @og.rev 5.6.7.0 (2013/07/27) DDL(データ定義言語:Data Definition Language)の処理件数追加 556 * 557 * @return DDL(データ定義言語:Data Definition Language)処理した件数 558 */ 559 public int getDDLCount() { return ddlCnt; } 560 561 /** 562 * 実際に登録された テーブル名を返します。 563 * 564 * テーブル名は、拡張XDK形式のROWSETタグのtableName属性に 565 * 記述しておくか、コンストラクターで引数として渡します。 566 * 両方指定された場合は、ROWSETタグのtableName属性が優先されます。 567 * ここでの返り値は、実際に使用された テーブル名です。 568 * 569 * @return 変更件数(主に、削除件数) 570 */ 571 public String getTableName() { return tableName; } 572 573 /** 574 * この接続が、PreparedStatement#getParameterMetaData() を使用するかどうかを判定します。 575 * 本来は、ConnectionFactory#useParameterMetaData(String)を使うべきだが、dbid が無いため、直接取得します。 576 * 577 * @og.rev 5.3.8.0 (2011/08/01) 新規作成 ( ApplicationInfo#useParameterMetaData(Connection) からコピー ) 578 * @og.rev 5.6.7.0 (2013/07/27) ProductName は、DBUtil 経由で取得する。 579 * 580 * @param conn 接続先(コネクション) 581 * 582 * @return 使用する場合:true / その他:false 583 */ 584 private static boolean useParameterMetaData( final Connection conn ) { 585 586 String dbProductName = DBUtil.getProductName( conn ); 587 588 return "PostgreSQL".equalsIgnoreCase( dbProductName ) ; 589 590// try { 591// DatabaseMetaData meta = conn.getMetaData(); 592// String dbProductName = meta.getDatabaseProductName(); 593// 594// if( "PostgreSQL".equalsIgnoreCase( dbProductName ) ) { return true; } 595// 596// return false ; 597// } 598// catch( SQLException ex ) { 599// String errMsg = "DatabaseMetaData から、getDatabaseProductName を取得できませんでした。" 600// + ex.getMessage() + ":" + ex.getSQLState() ; 601// throw new RuntimeException( errMsg,ex ); 602// } 603 } 604 605 /** 606 * テスト用のメインメソッド 607 * 608 * Usage: java org.opengion.fukurou.xml.HybsXMLSave USER PASSWD URL TABLE FILE [ENCODE] [DRIVER] 609 * USER : DB接続ユーザー(GE) 610 * PASSWD : DB接続パスワード(GE) 611 * URL : DB接続JDBCドライバURL(jdbc:oracle:thin:@localhost:1521:HYBS 612 * TABLE : 登録するテーブルID(GE21) 613 * FILE : 登録するORACLE XDK 形式 XMLファイル(GE21.xml) 614 * [ENCODE]: ファイルのエンコード 初期値:UTF-8 615 * [DRIVER]: JDBCドライバー 初期値:oracle.jdbc.OracleDriver 616 * 617 * ※ ファイルが存在しなかった場合、FileNotFoundException を RuntimeException に変換して、throw します。 618 * ※ 指定のエンコードが存在しなかった場合、UnsupportedEncodingException を RuntimeException に変換して、throw します。 619 * 620 * @og.rev 5.1.1.0 (2009/12/01) MySQL対応 明示的に、TRANSACTION_READ_COMMITTED を指定する。 621 * @og.rev 5.6.7.0 (2013/07/27) DDL(データ定義言語:Data Definition Language)の処理件数追加 622 * 623 * @param args コマンド引数配列 624 * @throws ClassNotFoundException クラスを見つけることができなかった場合。 625 * @throws SQLException データベース接続エラーが発生した場合。 626 */ 627 public static void main( final String[] args ) 628 throws ClassNotFoundException , SQLException { 629 if( args.length < 5 ) { 630 LogWriter.log( "Usage: java org.opengion.fukurou.xml.HybsXMLSave USER PASSWD URL TABLE FILE [ENCODE] [DRIVER]" ); 631 LogWriter.log( " USER : DB接続ユーザー(GE)" ); 632 LogWriter.log( " PASSWD: DB接続パスワード(GE)" ); 633 LogWriter.log( " URL : DB接続JDBCドライバURL(jdbc:oracle:thin:@localhost:1521:HYBS)" ); 634 LogWriter.log( " TABLE : 登録するテーブルID(GE21)" ); 635 LogWriter.log( " FILE : 登録するORACLE XDK 形式 XMLファイル(GE21.xml)" ); 636 LogWriter.log( " [ ENCODE: ファイルのエンコード 初期値:UTF-8 ]" ); 637 LogWriter.log( " [ DRIVER: JDBCドライバー 初期値:oracle.jdbc.OracleDriver ]" ); 638 return ; 639 } 640 641 String user = args[0] ; 642 String passwd = args[1] ; 643 String url = args[2] ; 644 String table = args[3] ; 645 String file = args[4] ; 646 String encode = ( args.length == 6 ) ? args[5] : "UTF-8" ; 647 String driver = ( args.length == 7 ) ? args[6] : "oracle.jdbc.OracleDriver" ; 648 649 Class.forName(driver); 650 651 Connection conn = DriverManager.getConnection( url,user,passwd ); 652 Reader reader = null; 653 int insCnt; 654 int updCnt; 655 int delCnt; 656 int ddlCnt; // 5.6.7.0 (2013/07/27) DDL処理件数追加 657 try { 658 conn.setAutoCommit( false ); 659 conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); // 5.1.1.0 (2009/12/01) 660 HybsXMLSave save = new HybsXMLSave( conn,table ); 661 662 reader = new BufferedReader(new InputStreamReader( 663 new FileInputStream( file ) ,encode )); 664 save.insertXML( reader ); 665 insCnt = save.getInsertCount(); 666 updCnt = save.getUpdateCount(); 667 delCnt = save.getDeleteCount(); 668 ddlCnt = save.getDDLCount(); // 5.6.7.0 (2013/07/27) DDL処理件数追加 669 670 Closer.commit( conn ); 671 } 672 // FileNotFoundException , UnsupportedEncodingException 673 catch( java.io.FileNotFoundException ex ) { 674 String errMsg = "ファイルが存在しません。" + ex.getMessage() 675 + CR + "Table=[" + table + "] File =[" + file + "]" ; 676 throw new RuntimeException( errMsg,ex ); 677 } 678 catch( java.io.UnsupportedEncodingException ex ) { 679 String errMsg = "指定のエンコードが存在しません。" + ex.getMessage() 680 + CR + "Table=[" + table + "] Encode =[" + encode + "]" ; 681 throw new RuntimeException( errMsg,ex ); 682 } 683 finally { 684 Closer.ioClose( reader ); 685 Closer.connClose( conn ); 686 } 687 688 System.out.println( "XML File[" + file + "] Into [" + table + "] Table" ); 689 System.out.println( " Insert Count : [" + insCnt + "]" ); 690 System.out.println( " Update Count : [" + updCnt + "]" ); 691 System.out.println( " Delete Count : [" + delCnt + "]" ); 692 System.out.println( " DDL Count : [" + ddlCnt + "]" ); 693 } 694}