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