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.fileexec; 017 018import java.sql.Connection; 019import java.sql.ResultSet; 020import java.sql.PreparedStatement; 021import java.sql.ParameterMetaData; 022import java.sql.SQLException; 023import java.sql.SQLIntegrityConstraintViolationException; // 7.4.1.0 (2021/04/23) 024 025import java.util.Map; 026import java.util.List; 027import java.util.ArrayList; 028import java.util.Arrays; 029 030import org.apache.tomcat.jdbc.pool.DataSource; 031import org.apache.tomcat.jdbc.pool.PoolProperties; 032 033import org.opengion.fukurou.system.HybsConst; // 7.2.3.1 (2020/04/17) 034import org.opengion.fukurou.db.Transaction; // 7.4.2.0 (2021/05/14) 035 036/** 037 * データベース処理を行う、簡易的なユーティリティークラスです。 038 * staticメソッドしか持っていません。 039 * sql文を execute( query ) する事により、データベースに書き込みます。 040 * 041 * このクラスは、マルチスレッドに対して、安全です。 042 * 043 * @version 4.0 044 * @author Kazuhiko Hasegawa 045 * @since JDK5.0, 046 */ 047public final class DBUtil { 048 private static final XLogger LOGGER= XLogger.getLogger( DBUtil.class.getSimpleName() ); // ログ出力 049 050 /** データベースのキーワード {@value} */ 051 public static final String DATABASE_KEY = "DATABASE"; 052 053 /** 接続先URL {@value} */ 054 public static final String URL_KEY = "REALM_URL"; 055 /** ドライバー {@value} */ 056 public static final String DRIVER_KEY = "REALM_DRIVER"; 057 /** ユーザーID {@value} */ 058 public static final String NAME_KEY = "REALM_NAME"; 059 /** パスワード {@value} */ 060 public static final String PASSWORD_KEY = "REALM_PASSWORD"; 061 062 /** データベースリトライの待ち時間(ミリ秒) {@value} */ 063 public static final int CONN_SLEEP_TIME = 2000 ; // 6.8.2.2 (2017/11/02) コネクションの獲得まで、2秒待つ 064 /** データベースリトライ回数 {@value} */ 065 public static final int CONN_RETRY_COUNT = 10 ; // 6.8.2.2 (2017/11/02) コネクションの獲得まで、10回リトライする。 066 /** データベースValid タイムアウト時間(秒) {@value} */ 067 public static final int CONN_VALID_TIMEOUT = 10 ; // 6.8.2.2 (2017/11/02) コネクションのValidチェックのタイムアウト時間。 068 069 /** データ検索時のフェッチサイズ {@value} */ 070 public static final int DB_FETCH_SIZE = 251 ; 071 072 private static final DataSource DATA_SOURCE = new DataSource(); 073 074 private static boolean readyFlag ; // 準備が出来た場合は、true 075 private static boolean oracleFlag ; // 接続先がORACLEの場合は、true 076 077 private static final int BUFFER_MIDDLE = 200 ; 078 079 /** 080 * インスタンスを作成させないため、private 化します。 081 */ 082 private DBUtil() {} 083 /** 084 * 引数を指定せず、オブジェクトを作成します。 085 * 086 * System.getProperty より取得し、さらに、そこから取得できなかった 087 * 場合は、環境変数から、取得します。 088 * 089 * @og.rev 7.2.3.1 (2020/04/17) System.getenv → HybsConst.getenv 変更(サービス化対応) 090 * 091 * @see #URL_KEY 092 */ 093 public static void init() { 094// init( System.getProperty( URL_KEY , System.getenv( URL_KEY ) ) , 095// System.getProperty( DRIVER_KEY , System.getenv( DRIVER_KEY ) ) , 096// System.getProperty( NAME_KEY , System.getenv( NAME_KEY ) ) , 097// System.getProperty( PASSWORD_KEY , System.getenv( PASSWORD_KEY ) ) 098// ); 099 init( HybsConst.getenv( URL_KEY ) , 100 HybsConst.getenv( DRIVER_KEY ) , 101 HybsConst.getenv( NAME_KEY ) , 102 HybsConst.getenv( PASSWORD_KEY ) 103 ); 104 } 105 106 /** 107 * 接続先URL、ドライバー、ユーザーID、パスワードなどを含んだMapを指定して、オブジェクトを作成します。 108 * 109 * Mapに指定のキーが含まれない場合は、System.getProperty より取得し、さらに、そこから取得できなかった 110 * 場合は、環境変数から、取得します。 111 * 112 * @og.rev 7.2.3.1 (2020/04/17) System.getenv → HybsConst.getenv 変更(サービス化対応) 113 * 114 * @param prmMap 必要情報を含んだMapオブジェクト 115 * @see #URL_KEY 116 */ 117 public static void init( final Map<String,String> prmMap ) { 118// init( prmMap.getOrDefault( URL_KEY , System.getProperty( URL_KEY , System.getenv( URL_KEY ) ) ) , 119// prmMap.getOrDefault( DRIVER_KEY , System.getProperty( DRIVER_KEY , System.getenv( DRIVER_KEY ) ) ) , 120// prmMap.getOrDefault( NAME_KEY , System.getProperty( NAME_KEY , System.getenv( NAME_KEY ) ) ) , 121// prmMap.getOrDefault( PASSWORD_KEY , System.getProperty( PASSWORD_KEY , System.getenv( PASSWORD_KEY ) ) ) 122// ); 123 init( prmMap.getOrDefault( URL_KEY , HybsConst.getenv( URL_KEY ) ) , 124 prmMap.getOrDefault( DRIVER_KEY , HybsConst.getenv( DRIVER_KEY ) ) , 125 prmMap.getOrDefault( NAME_KEY , HybsConst.getenv( NAME_KEY ) ) , 126 prmMap.getOrDefault( PASSWORD_KEY , HybsConst.getenv( PASSWORD_KEY ) ) 127 ); 128 } 129 130 /** 131 * 接続先URL、ドライバー、ユーザーID、パスワードを指定して、オブジェクトを作成します。 132 * 133 * params は、必ず、4つ必要です。 134 * 135 * @param params 接続先URL、ドライバー、ユーザーID、パスワード 136 * @see #isReady() 137 */ 138 public static void init( final String... params ) { 139 if( readyFlag ) { 140 // MSG0024 = すでに、接続先設定は完了しています。[{0}] 141 throw MsgUtil.throwException( "MSG0024" , DATA_SOURCE ); 142 } 143 144 if( params == null || params.length != 4 ) { 145 // MSG0027 = 接続先設定情報が不足しています。[{0}] 146 throw MsgUtil.throwException( "MSG0027" , Arrays.toString( params ) ); 147 } 148 149 final PoolProperties pp = new PoolProperties(); 150 pp.setUrl( params[0] ); 151 pp.setDriverClassName( params[1] ); 152 pp.setUsername( params[2] ); 153 pp.setPassword( params[3] ); 154 155 DATA_SOURCE.setPoolProperties( pp ); 156 readyFlag = true; 157 158 oracleFlag = params[0] != null && params[0].startsWith( "jdbc:oracle" ); 159 } 160 161 /** 162 * DataSourceの初期化が完了していれば、true を返します。 163 * 164 * 初期化は、#init(String...) メソッドの呼び出して、完了します。 165 * #crear() で、未完了に戻ります。 166 * 167 * @return 初期化が完了しているかどうか 168 * @see #init(String...) 169 */ 170 public static boolean isReady() { return readyFlag; } 171 172 /** 173 * 接続先がORACLEかどうかを返します。 174 * 175 * ORACLE の場合は、true を返します。 176 * 177 * @return 接続先がORACLEかどうか[true:ORACLE false:その他] 178 */ 179 public static boolean isOracle() { return oracleFlag; } 180 181 /** 182 * DataSource から、Connectionを取得して、返します。 183 * 184 * @og.rev 6.8.2.2 (2017/11/02) コネクションの再取得をリトライします。 185 * @og.rev 7.2.5.0 (2020/06/01) DB処理の実行に失敗のエラーは、3回までは、何も出さない。 186 * 187 * @return DataSourceから、Connectionを取得して、返します。 188 * @throws SQLException SQLエラーが発生した場合 189 */ 190 public static Connection getConnection() throws SQLException { 191 if( !readyFlag ) { 192 // // MSG0025 = 接続先設定が完了していません。 193 // throw MsgUtil.throwException( "MSG0025" , "getConnection() Error!!" ); 194 init(); 195 } 196 197 SQLException errEX = null; 198 for( int i=0; i<CONN_RETRY_COUNT; i++ ) { 199 try { 200 final Connection conn = DATA_SOURCE.getConnection(); 201 conn.setAutoCommit( false ); 202 203 if( conn.isValid( CONN_VALID_TIMEOUT ) ) { return conn; } 204 } 205 catch( final SQLException ex ) { 206 if( i >= 3 ) { // とりあえず3回までは、何も出さない 207// // MSG0019 = DB処理の実行に失敗しました。メッセージ=[{0}]。\n\tquery=[{1}]\n\tvalues={2} 208// MsgUtil.errPrintln( "MSG0019" , ex.getMessage() ); 209 // 7.2.5.0 (2020/06/01) MSG0019 = DB処理の実行に失敗しました。\n\tquery=[{0}]\n\tvalues={1} 210 MsgUtil.errPrintln( "MSG0019" , ex.getMessage() , "" ); 211 } 212 213 errEX = ex ; 214 try{ Thread.sleep( CONN_SLEEP_TIME ); } catch( final InterruptedException ex2 ){} 215 } 216 } 217 218 final String errMsg = errEX == null ? "COUNT Over" : errEX.getMessage() ; 219// // MSG0019 = DB処理の実行に失敗しました。メッセージ=[{0}]。\n\tquery=[{1}]\n\tvalues={2} 220// throw MsgUtil.throwException( errEX , "MSG0019" , errMsg , "getConnection" , "" ); 221 // 7.2.5.0 (2020/06/01) MSG0019 = DB処理の実行に失敗しました。\n\tquery=[{0}]\n\tvalues={1} 222 throw MsgUtil.throwException( errEX , "MSG0019" , errMsg , "getConnection" ); 223 } 224 225 /** 226 * データ配列を渡してPreparedStatementの引数に、値をセットします。 227 * 228 * オラクル系の場合は、そのまま、setObject を行えば、自動変換しますが、 229 * それ以外のDBでは、java.sql.Types を渡す必要があります。さらに、null 値も、setNullを使用します。 230 * 今は、pMeta が、null かどうかで、オラクル系か、どうかを判定するようにしています。 231 * 232 * @param pstmt PreparedStatementオブジェクト 233 * @param values ?に割り当てる設定値 234 * @param pMeta オラクル系以外のDBに対して、type指定する場合に使用する ParameterMetaDataオブジェクト 235 * 236 * @throws SQLException DB処理の実行に失敗した場合 237 */ 238 private static void setObject( final PreparedStatement pstmt , final String[] values , final ParameterMetaData pMeta ) throws SQLException { 239 if( values != null && values.length > 0 ) { 240 // ORACLE では、ParameterMetaDataは、使わない。 241 if( pMeta == null ) { 242 int clmNo = 1; // JDBC のカラム番号は、1から始まる。 243 for( int i=0; i<values.length; i++ ) { 244 final String val = values[i]; 245 pstmt.setObject( clmNo++,val ); 246 } 247 } 248 else { 249 int clmNo = 1; // JDBC のカラム番号は、1から始まる。 250 for( int i=0; i<values.length; i++ ) { 251 final int type = pMeta.getParameterType( clmNo ); 252 final String val = values[i]; 253 if( val == null || val.isEmpty() ) { 254 pstmt.setNull( clmNo++, type ); 255 } 256 else { 257 pstmt.setObject( clmNo++,val,type ); 258 } 259 } 260 } 261 } 262 } 263 264 /** 265 * データ配列を渡して実際のDB処理を実行します。 266 * 267 * ここでは、1行だけ処理するための簡易メソッドを提供します。 268 * 269 * @param query 実行するSQL文 270 * @param values ?に割り当てる設定値 271 * @return ここでの処理件数 272 * 273 * @throws RuntimeException Connection DB処理の実行に失敗した場合 274 */ 275 public static int execute( final String query , final String... values ) { 276// final List<String[]> list = new ArrayList<>(); 277// list.add( values ); 278// 279// return execute( query,list ); 280 281 int execCnt = 0; 282 283 // try-with-resources 文 (AutoCloseable) 284 try( Connection conn = getConnection() ) { 285 // try-with-resources 文 は、close()してから、commit()後に、catch節が呼ばれる。 286 try( PreparedStatement pstmt = conn.prepareStatement( query ) ) { 287 // ORACLE では、ParameterMetaDataは、使わない。 288 final ParameterMetaData pMeta = oracleFlag ? null : pstmt.getParameterMetaData(); 289 290 setObject( pstmt , values , pMeta ); 291 execCnt = pstmt.executeUpdate(); // 1回なので、+= の必要性は無い。 292 293 conn.commit(); 294 } 295 catch( final SQLException ex ) { 296 conn.rollback(); 297 conn.setAutoCommit(true); 298 throw ex; 299 } 300 } 301 catch( final SQLException ex ) { 302 // MSG0019 = DB処理の実行に失敗しました。\n\tquery=[{0}]\n\tvalues={1} 303 throw MsgUtil.throwException( ex , "MSG0019" , query , Arrays.toString( values ) ); 304 } 305 306 return execCnt; 307 } 308 309 /** 310 * データ配列を渡して実際のDB処理を実行します。 311 * 312 * ここでは、1行だけ処理するための簡易メソッドを提供します。 313 * 314 * @og.rev 7.4.2.0 (2021/05/14) 外部から指定するTransactionオブジェクト 対応 315 * 316 * @param tarn 外部から指定するTransactionオブジェクト 317 * @param query 実行するSQL文 318 * @param values ?に割り当てる設定値 319 * @return ここでの処理件数 320 * 321 * @throws RuntimeException Connection DB処理の実行に失敗した場合 322 */ 323 public static int execute( final Transaction tarn , final String query , final String... values ) { 324 int execCnt = 0; 325 326// // try-with-resources 文 (AutoCloseable) 327// try( Connection conn = tarn.getConnection( null ) ) { 328 final Connection conn = tarn.getConnection( null ); 329// try { 330 // try-with-resources 文 は、close()してから、commit()後に、catch節が呼ばれる。 331 try( PreparedStatement pstmt = conn.prepareStatement( query ) ) { 332 // ORACLE では、ParameterMetaDataは、使わない。 333 final ParameterMetaData pMeta = oracleFlag ? null : pstmt.getParameterMetaData(); 334 335 setObject( pstmt , values , pMeta ); 336 execCnt = pstmt.executeUpdate(); // 1回なので、+= の必要性は無い。 337 338 tarn.commit(); 339 } 340 catch( final SQLException ex ) { 341 tarn.rollback(); 342 // conn.setAutoCommit(true); 343 // throw ex; 344 // MSG0019 = DB処理の実行に失敗しました。\n\tquery=[{0}]\n\tvalues={1} 345 throw MsgUtil.throwException( ex , "MSG0019" , query , Arrays.toString( values ) ); 346 } 347 // } 348 // catch( final SQLException ex ) { 349 // // MSG0019 = DB処理の実行に失敗しました。\n\tquery=[{0}]\n\tvalues={1} 350 // throw MsgUtil.throwException( ex , "MSG0019" , query , Arrays.toString( values ) ); 351 // } 352 353 return execCnt; 354 } 355 356 /** 357 * データ配列のListを渡して実際のDB処理を実行します。 358 * 359 * データ配列は、1行分のデータに対する設定値の配列です。 360 * これは、keys で指定した並び順と一致している必要があります。 361 * 362 * @og.rev 7.4.1.0 (2021/04/23) 途中で整合性制約違反が発生した場合に継続するかどうかを指定できるようにします。 363 * 364 * @param query 実行するSQL文 365 * @param list ?に割り当てる設定値 366 * @return ここでの処理件数 367 * 368 * @throws RuntimeException Connection DB処理の実行に失敗した場合 369 */ 370 public static int execute( final String query , final List<String[]> list ) { 371 return execute( query,list,true ); // 互換性確保。true でエラー発生時に、Exception を throw する。 372 } 373 374 /** 375 * データ配列のListを渡して実際のDB処理を実行します。 376 * 377 * データ配列は、1行分のデータに対する設定値の配列です。 378 * これは、keys で指定した並び順と一致している必要があります。 379 * 380 * 処理の途中で整合性制約違反が発生した場合に継続するかどうかを指定できるフラグを追加しています。 381 * false に設定した場合は、エラーが発生しても処理を継続して、commit します。(7.4.1.0 (2021/04/23) ) 382 * 383 * @og.rev 6.8.1.5 (2017/09/08) LOGGER.debug 情報の追加 384 * @og.rev 7.4.1.0 (2021/04/23) 途中で整合性制約違反が発生した場合に継続するかどうかを指定できるようにします。 385 * 386 * @param query 実行するSQL文 387 * @param list ?に割り当てる設定値 388 * @param useErrro false に設定すると、途中で整合性制約違反が発生しても処理を継続する。 389 * @return ここでの処理件数 390 * 391 * @throws RuntimeException Connection DB処理の実行に失敗した場合 392 */ 393 public static int execute( final String query , final List<String[]> list , final boolean useErrro ) { 394 LOGGER.debug( () -> "execute query=" + query ); 395 396 String[] debugLine = null; 397 int execCnt = 0; 398 399 // try-with-resources 文 (AutoCloseable) 400 try( Connection conn = getConnection() ) { 401 // try-with-resources 文 は、close()してから、commit()後に、catch節が呼ばれる。 402 try( PreparedStatement pstmt = conn.prepareStatement( query ) ) { // 更新系なので、setFetchSize は不要。 403 404 // ORACLE では、ParameterMetaDataは、使わない。 405 final ParameterMetaData pMeta = oracleFlag ? null : pstmt.getParameterMetaData(); 406 407 for( final String[] values : list ) { 408 debugLine = values; 409 LOGGER.debug( () -> "execute values=" + Arrays.toString( values ) ); 410 setObject( pstmt , values , pMeta ); 411 412 // 7.4.1.0 (2021/04/23) 途中で整合性制約違反が発生した場合に継続するかどうかを指定できるようにします。 413 try { 414 execCnt += pstmt.executeUpdate(); 415 } 416 catch( final SQLIntegrityConstraintViolationException ex ) { 417 if( useErrro ) { throw ex; } 418 else { 419 // MSG0033 = 整合性制約違反が発生しました。\n\tメッセージ=[{0}]\n\tquery=[{1}]\n\tvalues={2} 420 MsgUtil.errPrintln( "MSG0033" , ex.getMessage() , query , Arrays.toString( debugLine ) ); 421 } 422 } 423 } 424 425 conn.commit(); 426 } 427 catch( final SQLException ex ) { 428 conn.rollback(); 429 conn.setAutoCommit(true); 430 throw ex; 431 } 432 } 433 catch( final SQLException ex ) { 434// // MSG0019 = DB処理の実行に失敗しました。メッセージ=[{0}]。\n\tquery=[{1}]\n\tvalues={2} 435// throw MsgUtil.throwException( ex , "MSG0019" , ex.getMessage() , query , Arrays.toString( debugLine ) ); 436 // 7.2.5.0 (2020/06/01) MSG0019 = DB処理の実行に失敗しました。\n\tquery=[{0}]\n\tvalues={1} 437 throw MsgUtil.throwException( ex , "MSG0019" , query , Arrays.toString( debugLine ) ); 438 } 439 440 return execCnt; 441 } 442 443 /** 444 * データ配列のListを渡して実際のDB処理を実行します。(暫定メソッド) 445 * 446 * これは、updQueryで、更新してみて、0件の場合、insQuery で追加処理を行います。 447 * 448 * データ配列は、1行分のデータに対する設定値の配列です。 449 * これは、keys で指定した並び順と一致している必要があります。 450 * 451 * 処理の途中で整合性制約違反が発生した場合に継続するかどうかを指定できるフラグを追加しています。 452 * false に設定した場合は、エラーが発生しても処理を継続して、commit します。 (7.4.1.0 (2021/04/23) ) 453 * 454 * @og.rev 7.4.1.0 (2021/04/23) 途中で整合性制約違反が発生した場合に継続するかどうかを指定できるようにします。 455 * 456 * @param insQuery 追加するSQL文 457 * @param updQuery 更新するSQL文 458 * @param insList ?に割り当てる設定値 459 * @param updList ?に割り当てる設定値 460 * @return ここでの処理件数 461 * 462 * @throws RuntimeException Connection DB処理の実行に失敗した場合 463 */ 464 public static int execute( final String insQuery , final String updQuery , final List<String[]> insList , final List<String[]> updList ) { 465 return execute( insQuery,updQuery,insList,updList,true ); // 互換性確保。true でエラー発生時に、Exception を throw する。 466 } 467 468 /** 469 * データ配列のListを渡して実際のDB処理を実行します。(暫定メソッド) 470 * 471 * これは、updQueryで、更新してみて、0件の場合、insQuery で追加処理を行います。 472 * 473 * データ配列は、1行分のデータに対する設定値の配列です。 474 * これは、keys で指定した並び順と一致している必要があります。 475 * 476 * @og.rev 6.8.1.5 (2017/09/08) LOGGER.debug 情報の追加 477 * @og.rev 7.4.1.0 (2021/04/23) 途中で整合性制約違反が発生した場合に継続するかどうかを指定できるようにします。 478 * 479 * @param insQuery 追加するSQL文 480 * @param updQuery 更新するSQL文 481 * @param insList ?に割り当てる設定値 482 * @param updList ?に割り当てる設定値 483 * @param useErrro false に設定すると、途中で整合性制約違反が発生しても処理を継続する。 484 * @return ここでの処理件数 485 * 486 * @throws RuntimeException Connection DB処理の実行に失敗した場合 487 */ 488 public static int execute( final String insQuery , final String updQuery , final List<String[]> insList , final List<String[]> updList , final boolean useErrro ) { 489 LOGGER.debug( () -> "execute insQuery=" + insQuery + " , updQuery=" + updQuery ); 490 491 String[] debugLine = null; 492 String query = null; 493 494 int execCnt = 0; 495 496 // try-with-resources 文 (AutoCloseable) 497 try( Connection conn = getConnection() ) { 498 // try-with-resources 文 は、close()してから、commit()後に、catch節が呼ばれる。 499 try( PreparedStatement inPstmt = conn.prepareStatement( insQuery ); 500 PreparedStatement upPstmt = conn.prepareStatement( updQuery ) ) { 501 502 // ORACLE では、ParameterMetaDataは、使わない。 503 final ParameterMetaData inpMeta = oracleFlag ? null : inPstmt.getParameterMetaData(); 504 final ParameterMetaData uppMeta = oracleFlag ? null : upPstmt.getParameterMetaData(); 505 506 for( int i=0; i<updList.size(); i++ ) { // 更新処理と、挿入処理は、同じ数のListを用意する。 507 query = updQuery; 508 // 更新処理を行う。 509 final String[] upVals = updList.get(i); 510 debugLine = upVals; 511 setObject( upPstmt , upVals , uppMeta ); 512 513 int cnt = upPstmt.executeUpdate(); 514 515 if( cnt <= 0 ) { // 更新が無い、つまり、追加対象 516 query = insQuery; 517 // 挿入処理を行う。 518 final String[] inVals = insList.get(i); 519 debugLine = inVals; 520 setObject( inPstmt , inVals , inpMeta ); 521 522 LOGGER.debug( () -> "execute INSERT=" + Arrays.toString( inVals ) ); 523 524 // 7.4.1.0 (2021/04/23) 途中で整合性制約違反が発生した場合に継続するかどうかを指定できるようにします。 525 try { 526 cnt = inPstmt.executeUpdate(); 527 } 528 catch( final SQLIntegrityConstraintViolationException ex ) { 529 if( useErrro ) { throw ex; } 530 else { 531 cnt = 0; // エラー時に設定されないと、-1 のままなので。 532 // MSG0033 = 整合性制約違反が発生しました。\n\tメッセージ=[{0}]\n\tquery=[{1}]\n\tvalues={2} 533 MsgUtil.errPrintln( "MSG0033" , ex.getMessage() , insQuery , Arrays.toString( debugLine ) ); 534 } 535 } 536 } 537 else { // 元々、このelse は必要ない。UPDATE は、先に処理済 538 LOGGER.debug( () -> "execute UPDATE=" + Arrays.toString( upVals ) ); 539 } 540 541 execCnt += cnt; 542 } 543 conn.commit(); 544 } 545 catch( final SQLException ex ) { 546 conn.rollback(); 547 conn.setAutoCommit(true); 548 throw ex; 549 } 550 } 551 catch( final SQLException ex ) { 552// // MSG0019 = DB処理の実行に失敗しました。メッセージ=[{0}]。\n\tquery=[{1}]\n\tvalues={2} 553// throw MsgUtil.throwException( ex , "MSG0019" , ex.getMessage() , query , Arrays.toString( debugLine ) ); 554 // 7.2.5.0 (2020/06/01) MSG0019 = DB処理の実行に失敗しました。\n\tquery=[{0}]\n\tvalues={1} 555 throw MsgUtil.throwException( ex , "MSG0019" , query , Arrays.toString( debugLine ) ); 556 } 557 558 return execCnt; 559 } 560 561 /** 562 * 検索するデータベースを指定して、Queryを実行します(Transaction 対応)。 563 * 564 * ステートメントと引数により、Prepared クエリーの検索のみ実行します。 565 * 結果は、すべて文字列に変換されて格納されます。 566 * 567 * @param query ステートメント文字列 568 * @param args オブジェクトの引数配列 569 * 570 * @return 検索結果のリスト配列(結果が無ければ、サイズゼロのリスト) 571 * @throws RuntimeException DB検索処理の実行に失敗した場合 572 * @og.rtnNotNull 573 */ 574 public static List<String[]> dbQuery( final String query , final String... args ) { 575 // try-with-resources 文 (AutoCloseable) 576 try( Connection conn = getConnection() ) { 577 // try-with-resources 文 は、close()してから、commit()後に、catch節が呼ばれる。 578 try( PreparedStatement pstmt = conn.prepareStatement( query ) ) { 579 // ORACLE では、ParameterMetaDataは、使わない。 580 final ParameterMetaData pMeta = oracleFlag ? null : pstmt.getParameterMetaData(); 581 // 6.4.3.2 (2016/02/19) args が null でなく、length==0 でない場合のみ、処理する。 582 setObject( pstmt , args , pMeta ); 583 584 if( pstmt.execute() ) { 585 try( ResultSet resultSet = pstmt.getResultSet() ) { 586 return resultToArray( resultSet ); 587 } 588 } 589 conn.commit(); 590 } 591 catch ( final SQLException ex ) { 592 conn.rollback(); 593 conn.setAutoCommit(true); 594 throw ex; 595 } 596 } 597 catch ( final SQLException ex ) { 598// // MSG0019 = DB処理の実行に失敗しました。メッセージ=[{0}]。\n\tquery=[{1}]\n\tvalues={2} 599// throw MsgUtil.throwException( ex , "MSG0019" , ex.getMessage() , query , Arrays.toString( args ) ); 600 // 7.2.5.0 (2020/06/01) MSG0019 = DB処理の実行に失敗しました。\n\tquery=[{0}]\n\tvalues={1} 601 throw MsgUtil.throwException( ex , "MSG0019" , query , Arrays.toString( args ) ); 602 } 603 604 return new ArrayList<String[]>(); 605 } 606 607 /** 608 * 検索するデータベースを指定して、Queryを実行します(Transaction 対応)。 609 * 610 * ステートメントと引数により、Prepared クエリーの検索のみ実行します。 611 * 結果は、すべて文字列に変換されて格納されます。 612 * 613 * @og.rev 7.4.2.0 (2021/05/14) 外部から指定するTransactionオブジェクト 対応 614 * 615 * @param tarn 外部から指定するTransactionオブジェクト 616 * @param query ステートメント文字列 617 * @param args オブジェクトの引数配列 618 * 619 * @return 検索結果のリスト配列(結果が無ければ、サイズゼロのリスト) 620 * @throws RuntimeException DB検索処理の実行に失敗した場合 621 * @og.rtnNotNull 622 */ 623 public static List<String[]> dbQuery( final Transaction tarn , final String query , final String... args ) { 624 final Connection conn = tarn.getConnection( null ); 625 626 // try-with-resources 文 (AutoCloseable) 627 // try( Connection conn = tarn.getConnection( null ) ) { 628 // try-with-resources 文 は、close()してから、commit()後に、catch節が呼ばれる。 629 try( PreparedStatement pstmt = conn.prepareStatement( query ) ) { 630 // ORACLE では、ParameterMetaDataは、使わない。 631 final ParameterMetaData pMeta = oracleFlag ? null : pstmt.getParameterMetaData(); 632 // 6.4.3.2 (2016/02/19) args が null でなく、length==0 でない場合のみ、処理する。 633 setObject( pstmt , args , pMeta ); 634 635 if( pstmt.execute() ) { 636 try( ResultSet resultSet = pstmt.getResultSet() ) { 637 return resultToArray( resultSet ); 638 } 639 } 640 tarn.commit(); 641 } 642 catch ( final SQLException ex ) { 643 tarn.rollback(); 644 // conn.setAutoCommit(true); 645 // throw ex; 646 // 7.2.5.0 (2020/06/01) MSG0019 = DB処理の実行に失敗しました。\n\tquery=[{0}]\n\tvalues={1} 647 throw MsgUtil.throwException( ex , "MSG0019" , query , Arrays.toString( args ) ); 648 } 649 // } 650 // catch ( final SQLException ex ) { 651 // // 7.2.5.0 (2020/06/01) MSG0019 = DB処理の実行に失敗しました。\n\tquery=[{0}]\n\tvalues={1} 652 // throw MsgUtil.throwException( ex , "MSG0019" , query , Arrays.toString( args ) ); 653 // } 654 655 return new ArrayList<String[]>(); 656 } 657 658 /** 659 * ResultSet より、結果の文字列配列を作成します。 660 * 661 * 結果は、すべて文字列に変換されて格納されます。 662 * 移動したメソッドで使われているのでこれも移動 663 * 664 * @param resultSet ResultSetオブジェクト 665 * 666 * @return ResultSetの検索結果リスト配列 667 * @throws java.sql.SQLException データベース・アクセス・エラーが発生した場合 668 * @og.rtnNotNull 669 */ 670 public static List<String[]> resultToArray( final ResultSet resultSet ) throws SQLException { 671 final ArrayList<String[]> data = new ArrayList<>(); 672 673 final ResultSetValue rsv = new ResultSetValue( resultSet ); 674 675 while( rsv.next() ) { 676 data.add( rsv.getValues() ); 677 } 678 679 return data; 680 } 681 682 /** 683 * データをインサートする場合に使用するSQL文を作成します。 684 * 685 * これは、key に対応した ? 文字列で、SQL文を作成します。 686 * 実際の値設定は、この、キーの並び順に応じた値を設定することになります。 687 * conKeysとconValsは、固定値のキーと値です。 688 * conKeys,conVals がnullの場合は、これらの値を使用しません。 689 * 690 * @param table テーブルID 691 * @param keys 設定値に対応するキー配列 692 * @param conKeys 固定値の設定値に対応するキー配列 693 * @param conVals 固定値に対応する値配列 694 * @return インサートSQL 695 * @og.rtnNotNull 696 */ 697 public static String getInsertSQL( final String table , final String[] keys , final String[] conKeys , final String[] conVals ) { 698 final String[] vals = new String[keys.length]; 699 Arrays.fill( vals , "?" ); 700 701 final boolean useConst = conKeys != null && conVals != null && conKeys.length == conVals.length && conKeys.length > 0 ; 702 703 final StringBuilder sql = new StringBuilder( BUFFER_MIDDLE ) 704 .append( "INSERT INTO " ).append( table ) 705 .append( " ( " ) 706 .append( String.join( "," , keys ) ); 707 708 if( useConst ) { 709 sql.append( ',' ).append( String.join( "," , conKeys ) ); 710 } 711 712 sql.append( " ) VALUES ( " ) 713 .append( String.join( "," , vals ) ); 714 715 if( useConst ) { 716 sql.append( ",'" ).append( String.join( "','" , conVals ) ).append( '\'' ); 717 } 718 719 return sql.append( " )" ).toString(); 720 } 721 722 /** 723 * データをアップデートする場合に使用するSQL文を作成します。 724 * 725 * これは、key に対応した ? 文字列で、SQL文を作成します。 726 * 実際の値設定は、この、キーの並び順に応じた値を設定することになります。 727 * WHERE 文字列は、この、? も含めたWHERE条件の文字列を渡します。 728 * WHERE条件の場合は、この、?に、関数を設定したり、条件を指定したり、 729 * 色々なケースがあるため、単純にキーだけ指定する方法では、対応範囲が 730 * 限られるためです。 731 * conKeysとconValsは、固定値のキーと値です。 732 * conKeys,conVals,where がnullの場合は、これらの値を使用しません。 733 * 734 * @og.rev 7.2.5.0 (2020/06/01) UPDATEで、? を含むキーワードを、処理できるようにします。 735 * 736 * @param table テーブルID 737 * @param keys 設定値に対応するキー配列 738 * @param conKeys 固定値の設定値に対応するキー配列 739 * @param conVals 固定値に対応する値配列(VARCHARのみ) 740 * @param where WHERE条件式 741 * @return アップデートSQL 742 * @og.rtnNotNull 743 */ 744 public static String getUpdateSQL( final String table , final String[] keys , final String[] conKeys , final String[] conVals , final String where ) { 745 final boolean useConst = conKeys != null && conVals != null && conKeys.length == conVals.length && conKeys.length > 0 ; 746 747 final StringBuilder sql = new StringBuilder( BUFFER_MIDDLE ) 748 .append( "UPDATE " ).append( table ).append( " SET " ); 749// .append( String.join( " = ? ," , keys ) ) // key[0] = ? , ・・・ = ? , key[n-1] という文字列が作成されます。 750// .append( " = ? " ); // 最後の key[n-1] の後ろに、 = ? という文字列を追加します。 751 for( final String key : keys ) { 752 sql.append( key ); 753 if( ! key.contains( "?" ) ) { 754 sql.append( " = ? " ); // key = ? という文字列が作成されます。 755 } 756 sql.append( ',' ); 757 } 758 sql.deleteCharAt( sql.length() - 1 ); // 最後の一文字(,)を削除します。 759 760 if( useConst ) { 761 for( int i=0; i<conKeys.length; i++ ) { 762 sql.append( ',' ).append( conKeys[i] ).append( " = '" ).append( conVals[i] ).append( "' " ); 763 } 764 } 765 766 if( where != null && !where.isEmpty() ) { 767 sql.append( " WHERE " ).append( where ); // WHERE条件は、? に関数が入ったりするため、予め文字列を作成しておいてもらう。 768 } 769 770 return sql.toString(); 771 } 772 773 /** 774 * データをデリートする場合に使用するSQL文を作成します。 775 * 776 * これは、key に対応した ? 文字列で、SQL文を作成します。 777 * 実際の値設定は、この、キーの並び順に応じた値を設定することになります。 778 * WHERE 文字列は、この、? も含めたWHERE条件の文字列を渡します。 779 * WHERE条件の場合は、この、?に、関数を設定したり、条件を指定したり、 780 * 色々なケースがあるため、単純にキーだけ指定する方法では、対応範囲が 781 * 限られるためです。 782 * 783 * @param table テーブルID 784 * @param where 設定値に対応するキー配列(可変長引数) 785 * @return デリートSQL 786 * @og.rtnNotNull 787 */ 788 public static String getDeleteSQL( final String table , final String where ) { 789 final StringBuilder sql = new StringBuilder( BUFFER_MIDDLE ) 790 .append( "DELETE FROM " ).append( table ); 791 792 if( where != null && !where.isEmpty() ) { 793 sql.append( " WHERE " ).append( where ); // WHERE条件は、? に関数が入ったりするため、予め文字列を作成しておいてもらう。 794 } 795 796 return sql.toString(); 797 } 798}