    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) コネクションの獲得まで、２秒待つ
    062        /** データベースリトライ回数 {@value} */
    063        public static final int CONN_RETRY_COUNT = 10 ;         // 6.8.2.2 (2017/11/02) コネクションの獲得まで、１０回リトライする。
    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 は、必ず、４つ必要です。
    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処理の実行に失敗のエラーは、３回までは、何も出さない。
    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 ) {  // とりあえず３回までは、何も出さない
    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 のカラム番号は、１から始まる。
    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 のカラム番号は、１から始まる。
    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         * ここでは、１行だけ処理するための簡易メソッドを提供します。
    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         * データ配列は、１行分のデータに対する設定値の配列です。
    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で、更新してみて、０件の場合、insQuery で追加処理を行います。
    364         *
    365         * データ配列は、１行分のデータに対する設定値の配列です。
    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}