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 *        <og:tableUpdate
050 *            command   = "{@command}"
051 *            queryType = "JDBCTableUpdate"
052 *        >
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 *                 '1','{@USER.YMDH}','{@USER.YMDH}','{@USER.ID}','{@USER.ID}','{@GUI.KEY}')
059 *        </og:tableUpdate>
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) エラーメッセージに1行前の情報も出力します。
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、通常の3種類を、行列ループの外に出す。
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}