    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 のカラム番号は、１から始まる。
    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 のカラム番号は、１から始まる。
    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 のカラム番号は、１から始まる。
    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 のカラム番号は、１から始まる。
    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}