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