    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.plugin.query;
    017
    018import org.opengion.hayabusa.db.AbstractQuery;
    019import org.opengion.hayabusa.db.DBTableModel;
    020import org.opengion.hayabusa.common.HybsSystem;
    021import org.opengion.hayabusa.common.HybsSystemException;
    022import org.opengion.fukurou.util.ErrorMessage;
    023import org.opengion.fukurou.util.StringUtil;
    024import org.opengion.fukurou.util.Closer;
    025import org.opengion.fukurou.util.HybsDateUtil;                  // 5.5.8.5 (2012/11/27)
    026import org.opengion.fukurou.model.Formatter;
    027
    028import java.sql.Connection;
    029import java.sql.PreparedStatement;
    030import java.sql.ParameterMetaData;
    031import java.sql.SQLException;
    032
    033/**
    034 * 引数引き当て(PreparedStatement) を利用した登録系Queryです。
    035 *
    036 * java.sql.PreparedStatement を用いて、データベース検索処理を行います。
    037 * 引数の指定方法は、DBTableModele のカラム名に対応する名称を、SQL文の[カラム名]形式で
    038 * 記述します。これを解析して、実際に実行する PreparedStatement に対応する文字列を
    039 * 作成します。
    040 * たとえば、INSERT INTO GEXX (CLM,NAME_JA,LABEL_NAME) VALUES ([CLM],[NAME_JA],[LABEL_NAME] )
    041 * と記述すれば、内部で、DBTableModele のカラム名に対応する値を取り出し、SQL文として、
    042 * INSERT INTO GEXX (CLM,NAME_JA,LABEL_NAME) VALUES (?,?,? ) を実行します。
    043 *
    044 * @og.formSample
    045 * ●使用例
    046 *
    047 *    ・QUERYを直接書く場合
    048 *    【entry.jsp】
    049 *        &lt;og:tableUpdate
    050 *            command   = &quot;{&#064;command}&quot;
    051 *            queryType = &quot;JDBCTableUpdate&quot;
    052 *        &gt;
    053 *            INSERT INTO GE41
    054 *                (CLM,NAME_JA,LABEL_NAME,KBSAKU,SYSTEM_ID,LANG,
    055 *                 FGJ,DYSET,DYUPD,USRSET,USRUPD,PGUPD)
    056 *            VALUES
    057 *                ([CLM],[NAME_JA],[LABEL_NAME],[KBSAKU],[SYSTEM_ID],[LANG],
    058 *                 &#39;1&#39;,&#39;{&#064;USER.YMDH}&#39;,&#39;{&#064;USER.YMDH}&#39;,&#39;{&#064;USER.ID}&#39;,&#39;{&#064;USER.ID}&#39;,&#39;{&#064;GUI.KEY}&#39;)
    059 *        &lt;/og:tableUpdate&gt;
    060 *
    061 * @og.rev 4.0.0.0 (2005/01/31) 新規作成
    062 * @og.group データ編集
    063 *
    064 * @version  4.0
    065 * @author   Kazuhiko Hasegawa
    066 * @since    JDK5.0,
    067 */
    068public class Query_JDBCTableUpdate extends AbstractQuery {
    069        //* このプログラムのVERSION文字列を設定します。   {@value} */
    070        private static final String VERSION = "5.6.9.4 (2013/10/31)" ;
    071
    072        /**
    073         * 引数配列付のクエリーを実行します。
    074         * 処理自体は, #execute() と同様に、各サブクラスの実装に依存します。
    075         * これは、PreparedQuery で使用する引数を配列でセットするものです。
    076         * select * from emp where deptno = ? and job = ? などの PreparedQuery の
    077         * [カラム名] 部分の引数を、DBTableModelから順番にセットしていきます。
    078         *
    079         * @og.rev 3.8.0.8 (2005/10/03) エラーメッセージの出力順をメッセージ＋Queryに変更します。
    080         * @og.rev 4.0.0.0 (2007/05/09) ParameterMetaData を使用したパラメータ設定追加。
    081         * @og.rev 4.0.0.0 (2007/09/25) isOracle から useParamMetaData に変更
    082         * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData を ConnectionFactory経由で取得。(PostgreSQL対応)、setNull 対応
    083         * @og.rev 5.5.5.4 (2012/08/18) useParamMetaData 処理を、ループの外に出す。(PostgreSQL対応)
    084         * @og.rev 5.5.5.4 (2012/08/18) DATE オブジェクトを登録できるようにする。
    085         * @og.rev 5.5.8.5 (2012/11/27) TIMESTAMP型でも処理できるようにします。
    086         * @og.rev 5.6.9.4 (2013/10/31) エラーメッセージに１行前の情報も出力します。
    087         *
    088         * @param   rowNo 選択された行番号配列(登録する対象行)
    089         * @param   table DBTableModelオブジェクト(登録する元データ)
    090         */
    091        @Override
    092        public void execute( final int[] rowNo, final DBTableModel table ) {
    093                PreparedStatement pstmt = null ;
    094//              ParameterMetaData pMeta = null ;                // 5.5.5.4 (2012/08/18) useParamMetaData 処理を、ループの外に出す。(
    095
    096                int row = 0;                    // エラー時に表示するエラー行番号
    097                try {
    098                        int executeCount = 0;   // 処理件数
    099                        Formatter form = new Formatter( table );
    100                        form.setFormat( getStatement() );
    101                        int[] clmNos = form.getClmNos();                // 引数の個数分の配列。カラム番号を保存
    102                        String query = form.getQueryFormatString();
    103                        int   cnt    = clmNos.length;                   // 引数の個数(カラムの個数ではありません。)
    104
    105                        // 5.5.5.4 (2012/08/18) Timestamp オブジェクトを登録できるようにする。
    106                        boolean useTimeStamp = false;
    107                        boolean[] isTime = new boolean[cnt];
    108                        for( int j=0; j<cnt; j++ ) {
    109                                // 5.5.8.5 (2012/11/27) TIMESTAMP型でも処理できるようにします。
    110//                              isTime[j] = "DATE".equalsIgnoreCase( table.getDBColumn( clmNos[j] ).getClassName() );
    111                                String clsName = table.getDBColumn( clmNos[j] ).getClassName();
    112                                isTime[j] = "DATE".equalsIgnoreCase( clsName ) || "TIMESTAMP".equalsIgnoreCase( clsName );
    113                                if( !useTimeStamp && isTime[j] ) { useTimeStamp = true; }       // isTime[j] == true 時に、一度だけ実行される。
    114                        }
    115
    116                        Connection conn = getConnection();
    117                        pstmt = conn.prepareStatement( query );
    118                        pstmt.setQueryTimeout( DB_MAX_QUERY_TIMEOUT );
    119                //      ((oracle.jdbc.OraclePreparedStatement)pstmt).setExecuteBatch(50);
    120                        // 4.0.0.0 (2007/09/25) isOracle から useParamMetaData に変更
    121//                      boolean useParamMetaData = ApplicationInfo.useParameterMetaData( conn );
    122                        boolean useParamMetaData = useParameterMetaData();      // 5.3.8.0 (2011/08/01)
    123
    124                        // 5.5.5.4 (2012/08/18) 以下、useParamMetaData、useTimeStamp、通常の３種類を、行列ループの外に出す。
    125                        // 5.5.5.4 (2012/08/18) useParamMetaData 処理を、ループの外に出す。(PostgreSQL対応)
    126                        if( useParamMetaData ) {
    127                                int[] types = new int[cnt];
    128                                ParameterMetaData pMeta = pstmt.getParameterMetaData();
    129                                for( int j=0; j<cnt; j++ ) {
    130                                        types[j] = pMeta.getParameterType( j+1 );       // ややこしいが配列の個数と添え字の関係から、j と j+1 での処理となる。
    131                                }
    132
    133                                for( int i=0; i<rowNo.length; i++ ) {
    134                                        row = rowNo[i];
    135                                        for( int j=0; j<cnt; j++ ) {
    136                                                String val = StringUtil.rTrim( table.getValue( row,clmNos[j] ) );
    137                                                if( val == null || val.isEmpty() ) {
    138                                                        pstmt.setNull( j+1, types[j] );
    139                                                }
    140                                                else {
    141                                                        pstmt.setObject( j+1, val, types[j] );
    142                                                }
    143                                        }
    144                                        executeCount += pstmt.executeUpdate();
    145                                }
    146                        }
    147                        // 5.5.5.4 (2012/08/18) PostgreSQL対応 以外のDBの場合
    148                        else {
    149                                // 5.5.5.4 (2012/08/18) Timestamp オブジェクトを登録する場合
    150                                if( useTimeStamp ) {
    151                                        for( int i=0; i<rowNo.length; i++ ) {
    152                                                row = rowNo[i];
    153                                                for( int j=0; j<cnt; j++ ) {
    154                                                        String val = StringUtil.rTrim( table.getValue( row,clmNos[j] ) );
    155                                                        if( isTime[j] && val != null && !val.isEmpty() ) {
    156                                                                // 5.5.8.5 (2012/11/27) val は、yyyy-mm-dd hh:mm:ss[.f...] 形式でなければならない。
    157//                                                              java.sql.Timestamp time = java.sql.Timestamp.valueOf( val );
    158                                                                java.sql.Timestamp time = java.sql.Timestamp.valueOf( HybsDateUtil.parseTimestamp( val ) );
    159                                                                pstmt.setObject( j+1,time );
    160                                                        }
    161                                                        else {
    162                                                                pstmt.setObject( j+1,val );
    163                                                        }
    164                                                }
    165                                                executeCount += pstmt.executeUpdate();
    166                                        }
    167                                }
    168                                // 5.5.5.4 (2012/08/18) その他：つまり、これが通常の処理
    169                                else {
    170                                        for( int i=0; i<rowNo.length; i++ ) {
    171                                                row = rowNo[i];
    172                                                for( int j=0; j<cnt; j++ ) {
    173                                                        String val = StringUtil.rTrim( table.getValue( row,clmNos[j] ) );
    174                                                        pstmt.setObject( j+1,val );
    175                                                }
    176                                                executeCount += pstmt.executeUpdate();
    177                                        }
    178                                }
    179                        }
    180//                      if( useParamMetaData ) { pMeta = pstmt.getParameterMetaData(); }
    181//                      for( int i=0; i<rowNo.length; i++ ) {
    182//                              row = rowNo[i];
    183//                              for( int j=0; j<cnt; j++ ) {
    184////                                    String val = table.getValue( row,clmNos[j] ) ;  // 5.3.8.0 (2011/08/01) 簡素化
    185//                                      String val = StringUtil.rTrim( table.getValue( row,clmNos[j] ) );
    186//                                      // 4.0.0.0 (2007/09/25) ParameterMetaData を使用したパラメータ設定追加
    187//                                      if( useParamMetaData ) {
    188//                                              int type = pMeta.getParameterType( j+1 );
    189//                                              // 5.3.8.0 (2011/08/01) setNull 対応
    190////                                            pstmt.setObject( j+1,StringUtil.rTrim( val ),type );
    191//                                              if( val == null || val.isEmpty() ) {
    192//                                                      pstmt.setNull( j+1, type );
    193//                                              }
    194//                                              else {
    195//                                                      pstmt.setObject( j+1, val, type );
    196//                                              }
    197//                                      }
    198//                                      else {
    199////                                            pstmt.setObject( j+1,StringUtil.rTrim( val ) );
    200//                                              pstmt.setObject( j+1,val );
    201//                                      }
    202//                              }
    203//                              executeCount += pstmt.executeUpdate();
    204//                      }
    205                        setExecuteCount( executeCount );
    206                        setErrorCode( ErrorMessage.OK );
    207                }
    208                catch (SQLException ex) {
    209                        setErrorCode( ErrorMessage.EXCEPTION );
    210                        String errMsg = ex.getMessage() + ":" + ex.getSQLState() + HybsSystem.CR
    211                                                + "  QUERY=" + getStatement() + HybsSystem.CR
    212                                                + "  ROW =[" + (row+1) + "]" + HybsSystem.CR
    213                                                + "  VALS=[" + StringUtil.array2csv( table.getValues(row) )+ "]" + HybsSystem.CR ;
    214                        // 5.6.9.4 (2013/10/31)
    215                        if( row > 0 ) {
    216                                errMsg = errMsg
    217                                                + "  ROW(-1) =[" + (row) + "]" + HybsSystem.CR
    218                                                + "  VALS(-1)=[" + StringUtil.array2csv( table.getValues(row-1) )+ "]" + HybsSystem.CR ;
    219                        }
    220                        rollback();
    221                        realClose();
    222                        throw new HybsSystemException( errMsg,ex );             // 3.5.5.4 (2004/04/15) 引数の並び順変更
    223                }
    224                finally {
    225                        Closer.stmtClose( pstmt );
    226                }
    227        }
    228}