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 */
016 package org.opengion.fukurou.db;
017
018 import org.opengion.fukurou.util.StringUtil;
019 import org.opengion.fukurou.util.ApplicationInfo;
020 import org.opengion.fukurou.util.Closer;
021 import org.opengion.fukurou.model.Formatter;
022 import org.opengion.fukurou.model.ArrayDataModel;
023
024 import java.sql.Connection;
025 import java.sql.PreparedStatement;
026 import java.sql.ParameterMetaData;
027 import java.sql.SQLException;
028
029 import java.util.Arrays;
030
031 /**
032 * DBTableModel インターフェースを継承した TableModel の実?ラスです?
033 * sql? execute( query ) する事により,??タベ?スを検索した結果?
034 * DBTableModel に割り当てます?
035 *
036 * メソ?を宣?て??
037 * DBTableModel インターフェースは?データベ?スの検索結果(Resultset)をラ??する
038 * インターフェースとして使用して下さ??
039 *
040 * @og.rev 5.2.2.0 (2010/11/01) パッケージ移?hayabusa.db ?fukurou.db)
041 * @og.group ??/Shell制御
042 *
043 * @version 4.0
044 * @author Kazuhiko Hasegawa
045 * @since JDK5.0,
046 */
047 public class DBSimpleTable {
048
049 /** シス?依存?改行記号をセ?します? */
050 private static final String CR = System.getProperty("line.separator");
051
052 private final String[] names ; // ??タ配?に対応するカラ???names)
053 private String[] keys = null; // 登録に使用するカラ?ー配?(keys)
054 private int[] keysNo = null; // 登録に使用するカラ?ー配?番号
055 private String table = null; // 登録??ブル?
056 private String where = null; // where 条件式[カラ?]を含?
057 private int[] whereNo = null; // [カラ?]に対応するデータ配?番号
058 private String[] constrain = null; // key に対応した制?件
059
060 private String connID = null; // 登録に使用するコネクションID
061 private boolean useWhere = false; // where が設定されると true にセ?されます?
062
063 private Connection conn = null;
064 private PreparedStatement pstmt = null;
065 private ParameterMetaData pMeta = null; // 5.1.2.0 (2010/01/01) setObject に、Type を渡す?(PostgreSQL対?
066 private String query = null; // エラーメ?ージ用の変数
067 private int execCnt = 0;
068 private ApplicationInfo appInfo = null; // 3.8.7.0 (2006/12/15)
069 private boolean useParamMetaData = false; // 5.1.2.0 (2010/01/01) setObject に、Type を渡す?(PostgreSQL対?
070
071 /**
072 * ??タ配?のカラ?称配?を指定してオブジェクトを構築します?
073 *
074 * @param nm カラ?称配?
075 * @throws RuntimeException tbl ?null の場?
076 */
077 public DBSimpleTable( final String[] nm ) {
078 if( nm == null ) {
079 String errMsg = "??タ配?のカラ?称に null は設定できません?;
080 throw new RuntimeException( errMsg );
081 }
082
083 names = new String[nm.length];
084 System.arraycopy( nm,0,names,0,names.length );
085 }
086
087 /**
088 * 登録に使用するカラ?ー配?(keys)を登録します?
089 *
090 * 引数のkey配??null の場合?、names と同じカラ?称配?(names)が使用されます?
091 * キー配?(keys)は、?しか登録できません。また?addConstrain等?メソ?
092 * 呼び出しを先に実行すると、カラ?称配?(names)が設定されてしま??
093 * そ?後にこ?メソ?を呼び出すとエラーが発生します?
094 *
095 * @param key 登録カラ?称配?
096 * @see #addConstrain( String ,String )
097 * @throws RuntimeException すでに キー配?(keys)が登録済み/作?済みの場?
098 */
099 public void setKeys( final String[] key ) {
100 if( keys != null ) {
101 String errMsg = "すでに キー配?(keys)が登録済みです?";
102 throw new RuntimeException( errMsg );
103 }
104
105 if( key != null ) {
106 int size = key.length;
107 keys = new String[size];
108 System.arraycopy( key,0,keys,0,size );
109
110 constrain = new String[size];
111 Arrays.fill( constrain,"?" );
112
113 keysNo = new int[size];
114 for( int i=0; i<size; i++ ) {
115 int address = findAddress( names,keys[i] );
116 if( address >= 0 ) {
117 keysNo[i] = address;
118 }
119 else {
120 String errMsg = "?? key は、カラ???names)に存在しません"
121 + " key[" + i + "]=" + key[i]
122 + " names=" + StringUtil.array2csv( names ) ;
123 throw new RuntimeException( errMsg );
124 }
125 }
126 }
127 }
128
129 /**
130 * カラ?称配?(names)と同じキー配?(keys)を作?します?
131 *
132 * これは、キー配?(keys) が作?されなかった?合?処?す?
133 * keys ?null の場合?み、??実行します?
134 *
135 * @see #setKeys( String[] )
136 */
137 private void makeKeys() {
138 // キー配?(keys) が未設定?場合?、カラ?称配?(names)が設定されます?
139 if( keys == null ) {
140 keys = names;
141 int size = keys.length;
142
143 constrain = new String[size];
144 Arrays.fill( constrain,"?" );
145
146 keysNo = new int[size];
147 for( int i=0; i<size; i++ ) {
148 keysNo[i] = i;
149 }
150 }
151 }
152
153 /**
154 * Insert/Update/Delete 時?登録する??ブル?
155 *
156 * @param tbl ??ブル?
157 * @throws RuntimeException tbl ?null の場?
158 */
159 public void setTable( final String tbl ) {
160 if( tbl == null ) {
161 String errMsg = "table に null は設定できません?; // 5.1.8.0 (2010/07/01) errMsg 修正
162 throw new RuntimeException( errMsg );
163 }
164
165 table = tbl;
166 }
167
168 /**
169 * ??タベ?スの接続?IDを設定します?
170 *
171 * @param conn 接続?ID
172 */
173 public void setConnectionID( final String conn ) {
174 connID = conn;
175 }
176
177 /**
178 * アクセスログ取得?為,ApplicationInfoオブジェクトを設定します?
179 *
180 * @og.rev 3.8.7.0 (2006/12/15) 新規追?
181 *
182 * @param appInfo アプリ??オブジェク?
183 */
184 public void setApplicationInfo( final ApplicationInfo appInfo ) {
185 this.appInfo = appInfo;
186 }
187
188 /**
189 * Insert/Update/Delete 時? PreparedStatement の引数(?)制?
190 *
191 * 制?件(val)は、そのまま引数に使用されます?通常? で表され?
192 * パラメータに、文字長を制限する?合?SUBSTRB( ?,1,100 ) と?
193 * val 変数を与えます?
194 * また?キー?に対して、?を?登録した??合にも?使用できます?
195 * 例えば、NVAL( ?,? ) のような場合?キー?に値?つを割り当てます?
196 * 値配?の並び??、キー配?(keys)に対する(?の個数)に対応します?
197 * 注意:カラ?称配?(names)ではありません。また?先にキー配?(keys)を登録
198 * しておかな?、キー配?登録時にエラーが発生します?
199 * 制?件は、??るQUERYに対して適用されます?で?
200 * key また?、val ?null の場合?、RuntimeException ?Throwします?
201 *
202 * @param key 制?かけるキー
203 * @param val 制?件?
204 * @see #setKeys( String[] )
205 * @throws RuntimeException key また?、val ?null の場?
206 */
207 public void addConstrain( final String key,final String val ) {
208 if( key == null || val == null ) {
209 String errMsg = "key また?、val に null は設定できません?
210 + " key=[" + key + "] , val=[" + val + "]" ;
211 throw new RuntimeException( errMsg );
212 }
213
214 // キー配?(keys)が未設?null)の場合?、カラ?称配?(names)を割り当てます?
215 if( keys == null ) { makeKeys(); }
216
217 // 制?件のアドレスは、カラ?称配?(names)でなく?キー配?(keys)を使用します?
218 int address = findAddress( keys,key );
219 if( address >= 0 ) {
220 constrain[address] = val;
221 }
222 else {
223 String errMsg = "?? key は、キー配?(keys)に存在しません"
224 + " key=[" + key + "] , val=[" + val + "]"
225 + " keys=" + StringUtil.array2csv( keys ) ;
226 throw new RuntimeException( errMsg );
227 }
228 }
229
230 /**
231 * Update/Delete 時?キーとなるWHERE 条件のカラ?を設定します?
232 *
233 * 通常の WHERE 句の書き方と同じで、カラ???names)に対応する設定?(values)の値?
234 * 割り当てたい?に[カラ?] を記述します???の場合?設定?をセ?する
235 * ときに、シングルコー??ションを使用しますが、[カラ?]で?する?合??
236 * そ?前後に?')シングルコー??ションは、不要です?
237 * WHERE条件は、登録に使用するキー配?(keys)に現れな?件で行を特定することがあります?で
238 * カラ?称配?(names)を?にカラ?のアドレスを求めます?
239 * [カラ?]は? に置き換えて、PreparedStatement として、実行される形式に変換されます?
240 * 例:FGJ='1' and CLM=[CLM] and SYSTEM_ID in ([SYSID],'**')
241 *
242 * @og.rev 4.3.4.0 (2008/12/01) キー配?(keys)が未設?null)の場合?、カラ?称配?(names)を割り当て?
243 * @og.rev 5.0.2.0 (2009/11/01) バグ修正(keysは??タセ?のキーなので、where句のカラ?含まれて入?わけではな?
244 *
245 * @param wh WHERE条件のカラ?
246 * @throws RuntimeException [カラ?]がカラ???names)に存在しな???
247 */
248 public void setWhere( final String wh ) {
249
250 if( wh != null ) {
251 // キー配?(keys)が未設?null)の場合?、カラ?称配?(names)を割り当てます?
252 // if( keys == null ) { makeKeys(); }
253
254 // ArrayDataModel data = new ArrayDataModel( keys );
255 // 5.0.2.0 (2009/11/01)
256 ArrayDataModel data = new ArrayDataModel( names );
257 Formatter format = new Formatter( data );
258 format.setFormat( wh );
259 where = format.getQueryFormatString();
260 whereNo = format.getClmNos();
261
262 // StringBuilder buf = new StringBuilder( wh.length() );
263 //
264 // // 注意:[ と ] が隣接したケースでは処?きません?
265 // String wh2 = wh.replace( '[',']' );
266 // CSVTokenizer token = new CSVTokenizer( wh2,']',false );
267 // int cnt = token.countTokens() / 2 ;
268 //// String format = null;
269 // whereNo = new int[ cnt ];
270 // for( int i=0; i<cnt; i++ ) {
271 //// format = token.nextToken();
272 //// buf.append( format);
273 // buf.append( token.nextToken() ); // format
274 // String clm = token.nextToken();
275 // // カラ?称配?(names)を?にカラ?のアドレスを求めます?
276 // int address = findAddress( names,clm );
277 // if( address >= 0 ) { whereNo[i] = address; }
278 // else {
279 // String errMsg = "[" + clm + "]が?カラ???names)に存在しません? + CR
280 // + " names=" + StringUtil.array2csv( names );
281 // throw new RuntimeException( errMsg );
282 // }
283 // buf.append( "?" ); // [カラ? ?? に置き換?
284 // }
285 //// format = token.nextToken();
286 //// buf.append( format );
287 // buf.append( token.nextToken() ); // format
288 //
289 // where = buf.toString();
290 }
291 else {
292 where = null;
293 }
294 }
295
296 /**
297 * ??タをインサートする?合に使用するSQL?作?します?
298 *
299 * @return インサー?QL
300 */
301 private String getInsertSQL() {
302 // キー配?(keys)が未設?null)の場合?、カラ?称配?(names)を割り当てます?
303 if( keys == null ) { makeKeys(); }
304
305 StringBuilder sql = new StringBuilder();
306 sql.append( "INSERT INTO " ).append( table );
307 sql.append( " ( " );
308 sql.append( keys[0] );
309 for( int i=1; i<keys.length; i++ ) {
310 sql.append( "," ).append( keys[i] );
311 }
312 sql.append( " ) VALUES ( " );
313 sql.append( constrain[0] );
314 for( int i=1; i<keys.length; i++ ) {
315 sql.append( "," ).append( constrain[i] );
316 }
317 sql.append( " )" );
318
319 useWhere = false;
320
321 return sql.toString();
322 }
323
324 /**
325 * ??タをア????トする?合に使用するSQL?作?します?
326 *
327 * @return ア?????QL
328 */
329 private String getUpdateSQL() {
330 // キー配?(keys)が未設?null)の場合?、カラ?称配?(names)を割り当てます?
331 if( keys == null ) { makeKeys(); }
332
333 StringBuilder sql = new StringBuilder();
334 sql.append( "UPDATE " ).append( table ).append( " SET " );
335 sql.append( keys[0] ).append( " = " ).append( constrain[0] );
336
337 for( int i=1; i<keys.length; i++ ) {
338 sql.append( " , " );
339 sql.append( keys[i] ).append( " = " ).append( constrain[i] );
340 }
341
342 if( where != null && where.length() > 0 ) {
343 sql.append( " WHERE " ).append( where );
344 useWhere = true;
345 }
346 else {
347 useWhere = false;
348 }
349
350 return sql.toString();
351 }
352
353 /**
354 * ??タをデリートする?合に使用するSQL?作?します?
355 *
356 * @og.rev 5.0.2.0 (2009/11/01) バグ修正(削除時?keysは?な?
357 *
358 * @return ?ー?QL
359 */
360 private String getDeleteSQL() {
361 // キー配?(keys)が未設?null)の場合?、カラ?称配?(names)を割り当てます?
362 // if( keys == null ) { makeKeys(); }
363 // 5.0.2.0 (2009/11/01)
364 keys = new String[0];
365
366 StringBuilder sql = new StringBuilder();
367 sql.append( "DELETE FROM " ).append( table );
368
369 if( where != null && where.length() > 0 ) {
370 sql.append( " WHERE " ).append( where );
371 useWhere = true;
372 }
373 else {
374 useWhere = false;
375 }
376
377 return sql.toString();
378 }
379
380 /**
381 * Insert 処??開始を宣?ます?
382 * ??、コネクションを接続して、PreparedStatementオブジェクトを作?します?
383 * こ?メソ?と、close() メソ?は?セ?で処?てください?
384 *
385 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得?為,ApplicationInfoオブジェクトを設?
386 * @og.rev 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す?(PostgreSQL対?
387 * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData ?ConnectionFactory経由で取得?(PostgreSQL対?
388 *
389 * @throws SQLException Connection のオープンに失敗した??
390 */
391 public void startInsert() throws SQLException {
392 execCnt = 0;
393 query = getInsertSQL();
394 conn = ConnectionFactory.connection( connID,appInfo );
395 pstmt = conn.prepareStatement( query );
396 // 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す?(PostgreSQL対?
397 // useParamMetaData = ApplicationInfo.useParameterMetaData( conn );
398 useParamMetaData = ConnectionFactory.useParameterMetaData( connID ); // 5.3.8.0 (2011/08/01)
399 if( useParamMetaData ) {
400 pMeta = pstmt.getParameterMetaData();
401 }
402 }
403
404 /**
405 * Update 処??開始を宣?ます?
406 * ??、コネクションを接続して、PreparedStatementオブジェクトを作?します?
407 * こ?メソ?と、close() メソ?は?セ?で処?てください?
408 *
409 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得?為,ApplicationInfoオブジェクトを設?
410 * @og.rev 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す?(PostgreSQL対?
411 * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData ?ConnectionFactory経由で取得?(PostgreSQL対?
412 *
413 * @throws SQLException Connection のオープンに失敗した??
414 */
415 public void startUpdate() throws SQLException {
416 execCnt = 0;
417 query = getUpdateSQL();
418 conn = ConnectionFactory.connection( connID,appInfo );
419 pstmt = conn.prepareStatement( query );
420 // 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す?(PostgreSQL対?
421 // useParamMetaData = ApplicationInfo.useParameterMetaData( conn );
422 useParamMetaData = ConnectionFactory.useParameterMetaData( connID ); // 5.3.8.0 (2011/08/01)
423 if( useParamMetaData ) {
424 pMeta = pstmt.getParameterMetaData();
425 }
426 }
427
428 /**
429 * Delete 処??開始を宣?ます?
430 * ??、コネクションを接続して、PreparedStatementオブジェクトを作?します?
431 * こ?メソ?と、close() メソ?は?セ?で処?てください?
432 *
433 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得?為,ApplicationInfoオブジェクトを設?
434 * @og.rev 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す?(PostgreSQL対?
435 * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData ?ConnectionFactory経由で取得?(PostgreSQL対?
436 *
437 * @throws SQLException Connection のオープンに失敗した??
438 */
439 public void startDelete() throws SQLException {
440 execCnt = 0;
441 query = getDeleteSQL();
442 conn = ConnectionFactory.connection( connID,appInfo );
443 pstmt = conn.prepareStatement( query );
444 // 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す?(PostgreSQL対?
445 // useParamMetaData = ApplicationInfo.useParameterMetaData( conn );
446 useParamMetaData = ConnectionFactory.useParameterMetaData( connID ); // 5.3.8.0 (2011/08/01)
447 if( useParamMetaData ) {
448 pMeta = pstmt.getParameterMetaData();
449 }
450 }
451
452 /**
453 * ??タ配?を渡して実際のDB処?実行します?
454 *
455 * こ?処??前に、startXXXX をコールしておき、INSER,UPDATE,DELETEのどの
456 * 処?行うか?宣?ておく?があります?
457 * 戻り?は、この処?の処?数です?
458 * ?件数は、close( boolean ) 時に取得します?
459 *
460 * @og.rev 4.0.0.0 (2007/11/28) SQLException をきちんと伝播させます?
461 * @og.rev 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す?(PostgreSQL対?
462 * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData 時? setNull 対?PostgreSQL対?
463 *
464 * @param values カラ???names) に対応する設定?配?
465 *
466 * @return ここでの処?数
467 *
468 * @see #close( boolean )
469 * @throws SQLException Connection のクロースに失敗した??
470 * @throws RuntimeException Connection DB処??実行に失敗した??
471 */
472 public int execute( final String[] values ) throws SQLException {
473 final int cnt;
474 try {
475 int clmNo = 1; // JDBC のカラ?号は?から始まる?
476
477 // 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す?(PostgreSQL対?
478 if( useParamMetaData ) {
479 // keys に値を割り当てます?
480 for( int i=0; i<keys.length; i++ ) {
481 int type = pMeta.getParameterType( clmNo );
482 // 5.3.8.0 (2011/08/01) setNull 対?
483 // pstmt.setObject( clmNo++,values[keysNo[i]],type );
484 String val = values[keysNo[i]];
485 if( val == null || val.isEmpty() ) {
486 pstmt.setNull( clmNo++, type );
487 }
488 else {
489 pstmt.setObject( clmNo++,val,type );
490 }
491 }
492
493 // where 条件を使用する場合?、?を割り当てます?
494 if( useWhere ) {
495 for( int i=0; i<whereNo.length; i++ ) {
496 int type = pMeta.getParameterType( clmNo );
497 // 5.3.8.0 (2011/08/01) setNull 対?
498 // pstmt.setObject( clmNo++,values[whereNo[i]],type );
499 String val = values[whereNo[i]];
500 if( val == null || val.isEmpty() ) {
501 pstmt.setNull( clmNo++, type );
502 }
503 else {
504 pstmt.setObject( clmNo++,val,type );
505 }
506 }
507 }
508 }
509 else {
510 // keys に値を割り当てます?
511 for( int i=0; i<keys.length; i++ ) {
512 pstmt.setObject( clmNo++,values[keysNo[i]] );
513 }
514
515 // where 条件を使用する場合?、?を割り当てます?
516 if( useWhere ) {
517 for( int i=0; i<whereNo.length; i++ ) {
518 pstmt.setObject( clmNo++,values[whereNo[i]] );
519 }
520 }
521 }
522
523 cnt = pstmt.executeUpdate();
524 execCnt += cnt;
525 }
526 catch (SQLException ex) {
527 Closer.stmtClose( pstmt );
528 pMeta = null; // 5.1.2.0 (2010/01/01)
529 if( conn != null ) {
530 conn.rollback();
531 ConnectionFactory.remove( conn,connID );
532 conn = null;
533 }
534 String errMsg = "DB処??実行に失敗しました? + CR
535 + " query=[" + query + "]" + CR
536 + " values=" + StringUtil.array2csv( values );
537 // throw new RuntimeException( errMsg );
538 throw new RuntimeException( errMsg ,ex );
539 }
540 return cnt;
541 }
542
543 /**
544 * DB処?クロースします?
545 *
546 * 引数には、commit させる?合?、true を?rollback させる?合?、false をセ?します?
547 * 戻り?は、今まで処?れた合計データ件数です?
548 * こ?処??、SQLException を?部で RuntimeException に変換して?為、catch ??
549 * 不要ですが、? finally ?呼び出してください。そ?な?、リソースリークの
550 * 原因になります?
551 *
552 * @og.rev 5.1.2.0 (2010/01/01) pMeta のクリア
553 *
554 * @param commitFlag コミットフラグ [true:commitする/false:rollbacする]
555 *
556 * @return 今までの合計??数
557 */
558 public int close( final boolean commitFlag ) {
559 if( conn != null ) {
560 try {
561 if( commitFlag ) { conn.commit(); }
562 else { conn.rollback(); }
563 }
564 catch (SQLException ex) {
565 ConnectionFactory.remove( conn,connID );
566 conn = null;
567 String errMsg = "DB処?確?COMMIT)できませんでした? + CR
568 + " query=[" + query + "]" + CR ;
569 throw new RuntimeException( errMsg,ex );
570 }
571 finally {
572 Closer.stmtClose( pstmt );
573 pMeta = null; // 5.1.2.0 (2010/01/01)
574 ConnectionFactory.close( conn,connID );
575 conn = null;
576 }
577 }
578
579 return execCnt;
580 }
581
582 /**
583 * ??配?中の値とマッチするアドレスを検索します?
584 * ??配?がソートされて??、バイナリサーチが使えません。よって?
585 * 総当りでループ検索して?す?
586 * 総数が多い場合??くなる為、???にセ?して使用することを検討く???
587 *
588 * @param data ターゲ?の??配?中
589 * @param key 検索する??
590 *
591 * @return ターゲ?の添え?存在しな??合??1)
592 */
593 private int findAddress( final String[] data,final String key ) {
594 int address = -1;
595 if( data != null && key != null ) {
596 for( int i=0; i<data.length; i++ ) {
597 if( key.equalsIgnoreCase( data[i] ) ) {
598 address = i;
599 break;
600 }
601 }
602 }
603 return address;
604 }
605 }