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.db; 017 018import org.opengion.fukurou.system.OgRuntimeException ; 019import org.opengion.fukurou.util.HybsDateUtil; 020import static org.opengion.fukurou.system.HybsConst.DB_BATCH_SIZE; // 6.9.4.1 (2018/04/09) 021 022import java.sql.PreparedStatement; 023import java.sql.ParameterMetaData; 024import java.sql.SQLException; 025import java.sql.Timestamp; 026import java.sql.ResultSet; // 7.4.1.0 (2021/04/23) 027 028/** 029 * PreparedStatementを利用した更新処理を行う、簡易的なクラスです。 030 * 031 * ParameterMetaDataの使用有無を指定することで、パラメータを処理する際に、 032 * sqlType を使用するかどうかを指定します。 033 * また、データ登録時のバッチサイズに基づいた処理を行っています。 034 * execute(String[]) で、行ごとのパラメータデータを渡します。 035 * 一番最後に、execEnd() を呼ぶことで、更新件数を返します。 036 * 更新件数を取得しない場合でも、このメソッドを呼んでください。 037 * 038 * このクラスは、マルチスレッドに対応していません。 039 * 040 * @version 6.9 041 * @author Kazuhiko Hasegawa 042 * @since JDK9.0, 043 */ 044public final class DBUpdater { 045 private final PreparedStatement pstmt ; 046 private final boolean usePMeta ; 047 private final int[] types ; 048 private final boolean[] isTime; // 7.2.9.1 (2020/10/23) メソッドを統合します。 049 private final boolean useSelect; // 7.4.1.0 (2021/04/23) 050 051 private int rowCnt; 052 private int updCnt; 053 054 /** 055 * PreparedStatement を指定して、インスタンスを作成します。 056 * 057 * 内部で、ParameterMetaData を作成して、sqlType を使用します。 058 * 059 * @param prmSize パラメータの個数 060 * @param pstmt PreparedStatementオブジェクト 061 */ 062 public DBUpdater( final int prmSize , final PreparedStatement pstmt ) { 063 this( prmSize , pstmt , true ); 064 } 065 066 /** 067 * PreparedStatement を指定して、インスタンスを作成します。 068 * 069 * 内部で、ParameterMetaData を作成して、sqlType を使用します。 070 * 071 * @param prmSize パラメータの個数 072 * @param pstmt PreparedStatementオブジェクト 073 * @param usePMeta sqlType を使用するかどうか [true:使用する/false:使用しない] 074 */ 075 public DBUpdater( final int prmSize , final PreparedStatement pstmt , final boolean usePMeta ) { 076 this( prmSize , pstmt , usePMeta, null ); 077 } 078 079 /** 080 * PreparedStatementと、sqlTypeの使用有無を指定して、インスタンスを作成します。 081 * 082 * usePMetaは、内部で、ParameterMetaData を作成して、sqlType を使用するかどうかを 083 * 指定します。ORACLEのようなタイプの 084 * 085 * @og.rev 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない 086 * 087 * @param prmSize パラメータの個数 088 * @param pstmt PreparedStatementオブジェクト 089 * @param usePMeta sqlType を使用するかどうか [true:使用する/false:使用しない] 090 * @param isTime sqlType を使用するかどうか [true:使用する/false:使用しない] 091 */ 092 public DBUpdater( final int prmSize , final PreparedStatement pstmt , final boolean usePMeta , final boolean[] isTime ) { 093 this( prmSize , pstmt , usePMeta, isTime , false ); 094 } 095 096 /** 097 * PreparedStatementと、sqlTypeの使用有無を指定して、インスタンスを作成します。 098 * 099 * usePMetaは、内部で、ParameterMetaData を作成して、sqlType を使用するかどうかを 100 * 指定します。ORACLEのようなタイプの 101 * 102 * @og.rev 7.2.9.1 (2020/10/23) isTimeのメソッドを統合します。 103 * @og.rev 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない 104 * 105 * @param prmSize パラメータの個数 106 * @param pstmt PreparedStatementオブジェクト 107 * @param usePMeta sqlType を使用するかどうか [true:使用する/false:使用しない] 108 * @param isTime sqlType を使用するかどうか [true:使用する/false:使用しない] 109 * @param useSelect true の場合は、UPDATE/INSERTではなく、SELECT します。 110 */ 111// public DBUpdater( final int prmSize , final PreparedStatement pstmt , final boolean usePMeta , final boolean[] isTime ) { 112 public DBUpdater( final int prmSize , final PreparedStatement pstmt , final boolean usePMeta , 113 final boolean[] isTime , final boolean useSelect ) { 114 this.usePMeta = usePMeta; 115 this.pstmt = pstmt; 116 this.isTime = isTime; // 7.2.9.1 (2020/10/23) メソッドを統合します。 117 this.useSelect = useSelect; // 7.4.1.0 (2021/04/23) 118 119 if( usePMeta ) { 120 types = new int[prmSize]; 121 122 try { 123 final ParameterMetaData pMeta = pstmt.getParameterMetaData(); 124 for( int j=0; j<prmSize; j++ ) { 125 types[j] = pMeta.getParameterType( j+1 ); // ややこしいが配列の個数と添え字の関係から、j と j+1 での処理となる。 126 } 127 } 128 catch( final SQLException ex ) { 129 final String errMsg = "ParameterMetaData の取得に失敗しました。" ; 130 throw new OgRuntimeException( errMsg,ex ); 131 } 132 } 133 else { 134 types = null; 135 } 136 } 137 138 /** 139 * データ配列を渡してPreparedStatementの引数に、値をセットします。 140 * 141 * オラクル系の場合は、そのまま、setObject を行えば、自動変換しますが、 142 * それ以外のDBでは、java.sql.Types を渡す必要があります。さらに、null 値も、setNullを使用します。 143 * 今は、pMeta が、null かどうかで、オラクル系か、どうかを判定するようにしています。 144 * 145 * ※ このメソッドでは、useSelect は使いません。 146 * 147 * @og.rev 7.2.9.1 (2020/10/23) isTimeのメソッドを統合します。 148 * @og.rev 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない 149 * 150 * @param values ?に割り当てる設定値 151 * 152 * @throws SQLException DB処理の実行に失敗した場合 153 */ 154 public void execute( final String[] values ) throws SQLException { 155 if( values != null && values.length > 0 ) { 156 rowCnt++; // 行番号(処理行数) 157 158 // ORACLE では、ParameterMetaDataは、使わない。 159 if( usePMeta ) { 160 for( int j=0; j<values.length; j++ ) { 161 final String val = values[j]; 162 if( val == null || val.isEmpty() ) { 163 pstmt.setNull( j+1, types[j] ); // JDBC のカラム番号は、1から始まる。 164 } 165 else { 166 pstmt.setObject( j+1,val,types[j] ); 167 } 168 } 169 } 170 else { 171 if( isTime == null ) { 172 for( int j=0; j<values.length; j++ ) { 173 final String val = values[j]; // JDBC のカラム番号は、1から始まる。 174 pstmt.setObject( j+1,val ); 175 } 176 } 177 else { 178 // Timestamp オブジェクトを登録する場合の特別版です。 179 // 過去のコーディングとの互換性の関係で、ParameterMetaData を使用しません。 180 for( int j=0; j<values.length; j++ ) { 181 final String val = values[j]; 182 if( isTime[j] && val != null && !val.isEmpty() ) { 183 // val は、yyyy-mm-dd hh:mm:ss[.f...] 形式でなければならない。 184 final Timestamp time = Timestamp.valueOf( HybsDateUtil.parseTimestamp( val ) ); 185 pstmt.setObject( j+1,time ); 186 } 187 else { 188 pstmt.setObject( j+1,val ); 189 } 190 } 191 } 192 } 193 pstmt.addBatch(); 194 195 if( rowCnt % DB_BATCH_SIZE == 0 ) { 196 final int[] execCnt = pstmt.executeBatch(); 197 // 6.9.4.1 (2018/04/09) 更新件数は、暫定的に、データ処理件数と同じとする。 198 updCnt += execCnt.length; 199 } 200 } 201 } 202 203// /** 204// * データ配列を渡してPreparedStatementの引数に、値をセットします。 205// * 206// * Timestamp オブジェクトを登録する場合の特別版です。 207// * 過去のコーディングとの互換性の関係で、ParameterMetaData を使用しません。 208// * 209// * @og.rev 7.2.9.1 (2020/10/23) isTimeのメソッドを統合します。 210// * 211// * @param values ?に割り当てる設定値 212// * @param isTime Timestampを設定するカラムの場合は、true 213// * 214// * @throws SQLException DB処理の実行に失敗した場合 215// */ 216// public void execute( final String[] values , final boolean[] isTime ) throws SQLException { 217// if( values != null && values.length > 0 ) { 218// rowCnt++; // 行番号(処理行数) 219// 220// for( int j=0; j<values.length; j++ ) { 221// final String val = values[j]; 222// if( isTime[j] && val != null && !val.isEmpty() ) { 223// // val は、yyyy-mm-dd hh:mm:ss[.f...] 形式でなければならない。 224// final Timestamp time = Timestamp.valueOf( HybsDateUtil.parseTimestamp( val ) ); 225// pstmt.setObject( j+1,time ); 226// } 227// else { 228// pstmt.setObject( j+1,val ); 229// } 230// } 231// 232// pstmt.addBatch(); 233// 234// if( rowCnt % DB_BATCH_SIZE == 0 ) { 235// final int[] execCnt = pstmt.executeBatch(); 236// 237// // 6.9.4.1 (2018/04/09) 更新件数は、暫定的に、データ処理件数と同じとする。 238// updCnt += execCnt.length; 239// } 240// } 241// } 242 243 /** 244 * データ配列を渡してPreparedStatementの引数に、値をセットします。 245 * 246 * useSelect=true の場合は、更新系ではなく検索系の処理を行います。 247 * ここでは、マージ処理を考慮しているため、検索結果が、0件か、それ以上かのみ判定します。 248 * 249 * @og.rev 7.2.9.1 (2020/10/23) TableUpdateParamTag のマージ(UPDATE,INSERT)対応 250 * @og.rev 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない 251 * 252 * @param values ?に割り当てる設定値 253 * @return 更新件数 254 * 255 * @throws SQLException DB処理の実行に失敗した場合 256 */ 257 public int update( final String[] values ) throws SQLException { 258 int rtnCnt = 0 ; 259 260 if( values != null && values.length > 0 ) { 261 rowCnt++; // 行番号(処理行数) 262 263 // ORACLE では、ParameterMetaDataは、使わない。 264 if( usePMeta ) { 265 for( int j=0; j<values.length; j++ ) { 266 final String val = values[j]; 267 if( val == null || val.isEmpty() ) { 268 pstmt.setNull( j+1, types[j] ); // JDBC のカラム番号は、1から始まる。 269 } 270 else { 271 pstmt.setObject( j+1,val,types[j] ); 272 } 273 } 274 } 275 else { 276 if( isTime == null ) { 277 for( int j=0; j<values.length; j++ ) { 278 final String val = values[j]; // JDBC のカラム番号は、1から始まる。 279 pstmt.setObject( j+1,val ); 280 } 281 } 282 else { 283 // Timestamp オブジェクトを登録する場合の特別版です。 284 // 過去のコーディングとの互換性の関係で、ParameterMetaData を使用しません。 285 for( int j=0; j<values.length; j++ ) { 286 final String val = values[j]; 287 if( isTime[j] && val != null && !val.isEmpty() ) { 288 // val は、yyyy-mm-dd hh:mm:ss[.f...] 形式でなければならない。 289 final Timestamp time = Timestamp.valueOf( HybsDateUtil.parseTimestamp( val ) ); 290 pstmt.setObject( j+1,time ); 291 } 292 else { 293 pstmt.setObject( j+1,val ); 294 } 295 } 296 } 297 } 298// return pstmt.executeUpdate(); 299 if( useSelect ) { // 7.4.1.0 (2021/04/23) 検索します。 300 // 8.0.0.0 (2021/08/20) spotbugs:Bug kind and pattern: OBL - OBL_UNSATISFIED_OBLIGATION 301 try( ResultSet resultSet = pstmt.executeQuery() ) { 302 if( resultSet.next() ) { 303 rtnCnt = resultSet.getInt(1); // select count(*) なら… 304 } 305 } 306 307 // final ResultSet resultSet = pstmt.executeQuery(); 308 // if( resultSet.next() ) { 309 // rtnCnt = resultSet.getInt(1); // select count(*) なら… 310 // } 311 } 312 else { 313 rtnCnt = pstmt.executeUpdate(); 314 } 315 } 316// return 0; 317 return rtnCnt; // 7.4.1.0 (2021/04/23) 318 } 319 320 /** 321 * データの最後の処理を行います。 322 * 323 * 具体的には、executeBatch() で、所定のバッチ数に届いていない場合の処理です。 324 * 325 * @return 更新件数 326 * @throws SQLException データベース処理で例外が発生した場合。 327 */ 328 public int execEnd() throws SQLException { 329 final int[] execCnt = pstmt.executeBatch(); 330 // 6.9.4.1 (2018/04/09) 更新件数は、暫定的に、データ処理件数と同じとする。 331 updCnt += execCnt.length; 332 333 return updCnt; 334 } 335}