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