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.hayabusa.db; 017 018import java.sql.ResultSet; 019import java.sql.ResultSetMetaData; 020import java.sql.SQLException; 021import java.sql.Types; 022import java.util.Locale; 023 024import org.opengion.fukurou.db.DBUtil; 025import org.opengion.fukurou.db.Transaction; 026import org.opengion.fukurou.db.TransactionReal; 027import org.opengion.fukurou.util.ApplicationInfo; 028import org.opengion.fukurou.util.ErrorMessage; 029import org.opengion.fukurou.util.StringUtil; 030import org.opengion.hayabusa.common.HybsSystem; 031import org.opengion.hayabusa.common.HybsSystemException; 032import org.opengion.hayabusa.resource.LabelData; 033import org.opengion.hayabusa.resource.ResourceManager; 034 035/** 036 * データベース関連の便利なメソッドを集めた簡易ユーティリティークラスです。 037 * 全てのメソッドは、static メソッドになっています。 038 * 039 * @og.rev 2.1.1.1 (2002/11/15) Serializable インターフェースを削除する。 040 * @og.rev 4.0.0.0 (2007/10/16) 名称変更(DBUtil ⇒ DBTableModelUtil) DBアクセス関係のメソッドはfukurou/db/DBUtilに移動 041 * @og.group DB/Shell制御 042 * 043 * @version 4.0 044 * @author Kazuhiko Hasegawa 045 * @since JDK5.0, 046 */ 047public final class DBTableModelUtil { 048 /** 049 * インスタンスを作らないので、コンストラクタは、private に設定します。 050 */ 051 private DBTableModelUtil() {} 052 053 /** 054 * 初期データベースに接続して、Queryを実行します。 055 * ステートメントと引数により、Prepared クエリーの検索のみ実行します。 056 * 結果は,DBTableModel として返されます。 057 * 058 * @og.rev 3.0.0.0 (2002/12/25) 新規追加 059 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定 060 * @og.rev 4.0.0.0 (2005/01/31) lang ⇒ ResourceManager へ変更 061 * @og.rev 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更 062 * 063 * @param stmt ステートメント文字列 064 * @param args オブジェクトの引数配列 065 * @param resource リソースマネージャー 066 * @param appInfo アプリ情報オブジェクト 067 * 068 * @return 検索結果の配列 069 */ 070 public static DBTableModel makeDBTable( final String stmt ,final String[] args, final ResourceManager resource, final ApplicationInfo appInfo ) { 071// return makeDBTable( stmt ,args,resource,appInfo,"DEFAULT" ); // 4.0.0 (2005/01/31) 072 return makeDBTable( stmt ,args,resource,appInfo,null ); 073 } 074 075 /** 076 * 検索するデータベースを指定して、Queryを実行します。 077 * ステートメントと引数により、Prepared クエリーの検索のみ実行します。 078 * 結果は,DBTableModel として返されます。 079 * 検索以外のSQLも実行できます。結果は、null を返します。 080 * 081 * @og.rev 3.0.0.0 (2002/12/25) 新規追加 082 * @og.rev 3.0.0.1 (2003/02/14) ヘッダー、フッター情報が null のときの処理追加。 083 * @og.rev 3.5.6.0 (2004/06/18) nullに対する無駄な比較を削除します。 084 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定 085 * @og.rev 4.0.0.0 (2005/01/31) lang ⇒ ResourceManager へ変更 086 * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応 087 * @og.rev 5.3.7.0 (2011/07/01) TransactionReal の引数変更 088 * @og.rev 5.3.8.0 (2011/08/01) Transaction発生箇所でclose() 089 * 090 * @param stmt ステートメント文字列 091 * @param args オブジェクトの引数配列 092 * @param resource リソースマネージャー 093 * @param appInfo アプリ情報オブジェクト 094 * @param dbid 接続先ID 095 * 096 * @return 検索結果の配列 097 */ 098 public static DBTableModel makeDBTable( final String stmt ,final String[] args , 099 final ResourceManager resource, final ApplicationInfo appInfo, final String dbid ) { 100 if( stmt == null || stmt.length() == 0 ) { return null; } 101 102 DBTableModel table = null; 103 104 Query query = QueryFactory.newInstance( "JDBCPrepared" ); 105 106 ErrorMessage errMessage = null; 107 Transaction tran = null; 108 try { 109// Transaction tran = new TransactionReal( dbid,appInfo ); // 5.1.9.0 (2010/08/01) Transaction 対応 110// Transaction tran = new TransactionReal( appInfo ); // 5.3.7.0 (2011/07/01) 引数変更 111 tran = new TransactionReal( appInfo ); // 5.3.8.0 (2011/08/01) Transaction発生箇所でclose() 112 query.setTransaction( dbid,tran ); // 5.1.9.0 (2010/08/01) Transaction 対応 113// query.setConnectionID( dbid ); 114 query.setResourceManager( resource ); // 4.0.0 (2005/01/31) 115 query.setStatement( stmt ); 116 query.execute( args ); 117 int errCode = query.getErrorCode(); 118 int executeCount = query.getExecuteCount(); 119 if( errCode < ErrorMessage.NG && executeCount >= 0 ) { // 異常以外の場合 120 table = query.getDBTableModel(); 121// if( query.getUpdateFlag() ) { query.commit(); } 122 if( query.isUpdate() ) { query.commit(); } 123 } 124 else { 125 errMessage = query.getErrorMessage(); 126 } 127 } 128 catch( HybsSystemException ex ) { 129 if( query != null ) { query.rollback(); } 130 throw ex; 131 } 132 finally { 133// if( query != null ) { query.close(); } 134 QueryFactory.close( query ); 135 if( tran != null ) { tran.close(); } // 5.3.8.0 (2011/08/01) Transaction発生箇所でclose() 136 } 137 138 if( errMessage != null ) { 139 throw new HybsSystemException( errMessage.toString() ); 140 } 141 142 return table; 143 } 144 145 /** 146 * 空の DBTableModelオブジェクトを作成します。 147 * これは、本来、ファクトリクラスで作成すべきですが、簡易作成メソッドとして 148 * DBUtil の static メソッドとして実装します。 149 * 150 * @og.rev 4.0.0.0 (2005/01/31) 新規追加 151 * 152 * @return DBTableModelオブジェクト 153 */ 154 public static DBTableModel newDBTable() { 155 return new DBTableModelImpl(); 156 } 157 158 /** 159 * カラム名の配列及びデータの2次元配列からDBテーブルモデルを作成します。 160 * カラム名がセットされていない若しくはデータがセットされていない場合は、nullを返します。 161 * 162 * @og.rev 4.2.1.0 (2008/04/26) 新規追加 163 * 164 * @param clms カラム名の配列 165 * @param vals 値の配列 166 * @param resource リソースマネージャー 167 * 168 * @return DBテーブルモデル 169 */ 170 public static DBTableModel makeDBTable( final String[] clms, final String[][] vals, final ResourceManager resource ) { 171 if( clms == null || clms.length == 0 172 || vals == null || vals.length == 0 || vals[0] == null || vals[0].length == 0 ) { 173 return null; 174 } 175 176 if( clms.length != vals[0].length ) { 177// throw new HybsSystemException( "キーのカラム数とデータのカラム数が一致していません" ); 178 String errMsg = "キーのカラム数とデータのカラム数が一致していません。" 179 + HybsSystem.CR 180 + " clms.length=[" + clms.length + "] vals.length=[" + vals[0].length + "]" 181 + " clms=" + StringUtil.array2csv( clms ) + HybsSystem.CR 182 + " vals=" + StringUtil.array2csv( vals[0] ) ; // 5.1.8.0 (2010/07/01) errMsg 修正 183 throw new HybsSystemException( errMsg ); 184 } 185 186 int numberOfColumns = clms.length; 187 DBTableModel table = newDBTable() ; 188 table.init( numberOfColumns ); 189 190 DBColumn[] dbColumn = new DBColumn[numberOfColumns]; 191 for( int column=0; column<numberOfColumns; column++ ) { 192 dbColumn[column] = resource.makeDBColumn( clms[column] ); 193 table.setDBColumn( column,dbColumn[column] ); 194 } 195 196 int numberOfRows = vals.length; 197 for( int row=0; row<numberOfRows; row++ ) { 198 table.addColumnValues( vals[row] ); 199 } 200 201 return table; 202 } 203 204 /** 205 * 検索結果オブジェクトからDBテーブルモデルを作成します。 206 * 検索結果オブジェクトまたはリソースオブジェクトがセットされていない場合は、nullを返します。 207 * 208 * @og.rev 5.3.6.0 (2011/06/01) 新規追加 209 * @og.rev 5.5.5.4 (2012/08/18) TIMESTAMP 型もCLOBと同様に処理を分ける。 210 * 211 * @param result 検索結果オブジェクト 212 * @param skipRowCount 読み飛ばし件数 213 * @param maxRowCount 最大検索件数 214 * @param resource リソースマネージャー 215 * 216 * @return DBテーブルモデル 217 * @throws SQLException データベースアクセスエラー 218 */ 219 public static DBTableModel makeDBTable( final ResultSet result, final int skipRowCount, final int maxRowCount, final ResourceManager resource ) throws SQLException { 220 if( result == null || resource == null ) { return null; } 221 222 ResultSetMetaData metaData = result.getMetaData(); 223 224 int numberOfColumns = metaData.getColumnCount(); 225 226 DBTableModel table = DBTableModelUtil.newDBTable() ; 227 table.init( numberOfColumns ); 228 229 // 項目名,項目タイプ,項目サイズ,書込みフラグを設定する。 230 DBColumn[] dbColumn = new DBColumn[numberOfColumns]; 231 232 // 3.8.5.0 (2006/03/02) CLOB/ROWID などのカラムかどうかを判定します。 233 boolean isOther = false; 234 int[] types = new int[numberOfColumns]; 235 236 for( int column=0; column<numberOfColumns; column++ ) { 237 String name = (metaData.getColumnLabel(column+1)).toUpperCase(Locale.JAPAN) ; 238 dbColumn[column] = resource.getDBColumn( name ); 239 if( dbColumn[column] == null ) { 240 LabelData labelData = resource.getLabelData( name ); 241 dbColumn[column] = makeDBColumn( name,labelData,metaData,column,resource.getLang() ); 242 } 243 table.setDBColumn( column,dbColumn[column] ); 244 245 // 3.8.5.0 (2006/03/02) CLOB カラムかどうかを判定します。 246 // 5.5.5.4 (2012/08/18) TIMESTAMP 型もCLOBと同様に処理を分ける。 247// types[column] = metaData.getColumnType(column+1); 248 int clmType = metaData.getColumnType(column+1); 249 types[column] = clmType; 250// if( types[column] == Types.CLOB || types[column] == Types.ROWID ) { // JDK 6.0以降 ROWID 251 if( clmType == Types.CLOB || clmType == Types.ROWID || clmType == Types.TIMESTAMP ) { // JDK 6.0以降 ROWID 252 isOther = true; 253 } 254 } 255 256 // データ部の設定 257 int numberOfRows = 0; 258 while( numberOfRows < skipRowCount && result.next() ) { 259 // 注意 resultSet.next() を先に判定すると必ず1件読み飛ばしてしまう。 260 numberOfRows ++ ; 261 } 262// resultSet.absolute( getSkipRowCount() ); 263 numberOfRows = 0; 264 265 // 3.8.5.1 (2006/05/08) 行列のループなので、 CLOB 使用可否でループを分ける。 266 if( isOther ) { 267 while( numberOfRows < maxRowCount && result.next() ) { 268 numberOfRows ++ ; 269 String[] columnValues = new String[numberOfColumns]; 270 for( int column=0; column<numberOfColumns; column++ ) { 271 // 5.3.6.0 (2011/06/01) メソッド化 272 columnValues[column] = DBUtil.getValue( result, column, types[column] ); 273 } 274 table.addColumnValues( columnValues ); 275 } 276 } 277 else { 278 while( numberOfRows < maxRowCount && result.next() ) { 279 numberOfRows ++ ; 280 String[] columnValues = new String[numberOfColumns]; 281 for( int column=0; column<numberOfColumns; column++ ) { 282 Object obj = result.getObject(column+1); 283 columnValues[column] = ( obj == null ? "" : String.valueOf( obj ) ); 284 } 285 table.addColumnValues( columnValues ); 286 } 287 } 288 289 // 最大件数が、超えた場合でかつ次のデータがある場合は、オーバーフロー 290 if( numberOfRows >= maxRowCount && result.next() ) { 291 table.setOverflow( true ); 292 } 293 294 return table; 295 } 296 297 /** 298 * 検索結果オブジェクトからエディット設定に基づいて変換されたDBテーブルモデルを作成します。 299 * 検索結果オブジェクトまたはリソースオブジェクトまたはエディット設定オブジェクトがセットされていない場合は、nullを返します。 300 * 301 * @og.rev 5.3.6.0 (2011/06/01) 新規追加 302 * 303 * @param result 検索結果オブジェクト 304 * @param skipRowCount 読み飛ばし件数 305 * @param maxRowCount 最大検索件数 306 * @param resource リソースマネージャー 307 * @param config エディット設定オブジェクト 308 * 309 * @return DBテーブルモデル 310 * @throws SQLException データベースアクセスエラー 311 */ 312 public static DBTableModel makeEditDBTable( final ResultSet result, final int skipRowCount, final int maxRowCount, final ResourceManager resource, final DBEditConfig config ) throws SQLException { 313 if( result == null || resource == null ) { return null; } 314 DBTableModel table = new DBTableModelEditor(); 315 ((DBTableModelEditor)table).create( result, skipRowCount, maxRowCount, resource, config ); 316 return table; 317 } 318 319 /** 320 * ResultSetMetaData から、DBColumn オブジェクトを作成します。 321 * 322 * DBColumn オブジェクト がリソースファイルに定義されていない場合に、 323 * データベースの検索結果のメタデータを利用して、DBColumn オブジェクトを 324 * 作成します。 325 * 326 * @og.rev 3.4.0.0 (2003/09/01) 表示パラメータ、編集パラメータ、文字パラメータの追加。 327 * @og.rev 3.4.0.2 (2003/09/05) DBType のデフォルト値を、'X' から 'XK' に変更します。 328 * @og.rev 3.6.0.7 (2004/11/06) DBColumn の official属性追加 329 * @og.rev 4.0.0.0 (2005/01/31) lang 変数を取得 330 * @og.rev 5.3.6.0 (2011/06/01) AbstractQueryから移動 331 * 332 * @param name カラム名 333 * @param labelData LabelDataオブジェクト 334 * @param metaData ResultSetMetaDataオブジェクト 335 * @param column カラム番号 336 * @param lang 言語 337 * 338 * @return DBColumnオブジェクト 339 */ 340 public static DBColumn makeDBColumn( final String name,final LabelData labelData, 341 final ResultSetMetaData metaData,final int column,final String lang ) { 342 final DBColumn dbColumn ; 343 344 try { 345 String clsName = type2ClassName( metaData.getColumnType(column+1) ); 346 int size = metaData.getColumnDisplaySize(column+1); 347 if( size == 0 ) { size = 60; } 348 boolean writable = metaData.isWritable(column+1); 349 String dbType = ( "NUMBER".equals( clsName )) ? "S9" : "XK" ; 350 String defValue = ( "NUMBER".equals( clsName )) ? "0" : "" ; 351 DBColumnConfig config = new DBColumnConfig( 352 lang, // 言語 353 name, // カラム名 354 labelData, // カラムのラベルデータオブジェクト 355 clsName , // カラムのクラスを文字列にした名称 356 String.valueOf( size ) , // カラムの文字桁数 357 String.valueOf( writable ) , // カラムが書き込み可能かどうか 358 null , // データの表示用レンデラー 359 null , // データの編集用エディター 360 null , // メニューの項目コードデータオブジェクト 361 dbType , // データのタイプ 362 defValue, // データのデフォルト値 363 null , // 表示用レンデラーのパラメータ 364 null , // 編集用エディターのパラメータ 365 null , // データのタイプのパラメータ 366 null , // カラムロール 367 false, // 正式なカラムオブジェクトかどうか 368 null // データベース接続先ID 369 ); 370 371 dbColumn = new DBColumn( config ); // 4.0.0 (2005/01/31) 372 373 } 374 catch( SQLException ex ) { 375 String errMsg = "DBColumn を作成できませんでした。name=[" + name + " , label=[" + labelData + "]"; 376 throw new HybsSystemException( errMsg,ex ); // 3.5.5.4 (2004/04/15) 引数の並び順変更 377 } 378 catch( RuntimeException ex2 ) { 379 String errMsg = "予期せぬエラーが発生しました。name=[" + name + " , label=[" + labelData + "]"; 380 throw new HybsSystemException( errMsg,ex2 ); // 3.6.0.0 (2004/09/17) 381 } 382 383 return dbColumn; 384 } 385 386 /** 387 * カラムのタイプを表現する文字列値を返します。 388 * 389 * この文字列を用いて、CCSファイルでタイプごとの表示方法を 390 * 指定することができます。 391 * 392 * @og.rev 2.1.1.1 (2002/11/15) その他のケースを、VARCHAR2 を返すように修正。 393 * @og.rev 4.0.0.0 (2006/01/31) CLOB を追加 394 * @og.rev 5.3.6.0 (2011/06/01) AbstractQueryから移動 395 * @og.rev 5.5.5.4 (2012/08/18) DECIMAL,TIMESTAMP を追加 396 * 397 * @param type タイプ番号 398 * 399 * @return カラムのタイプを表現する文字列値 400 * @see java.sql.Types 401 */ 402 private static String type2ClassName( final int type ) { 403 final String rtn ; 404 405 switch( type ) { 406 case Types.CHAR: 407 case Types.VARCHAR: 408 case Types.BIT: 409 rtn = "VARCHAR2"; break; 410 case Types.LONGVARCHAR: // 4.0.0 (2006/01/31) 411 rtn = "LONG"; break; 412 case Types.TINYINT: 413 case Types.SMALLINT: 414 case Types.INTEGER: 415 case Types.NUMERIC: 416 case Types.BIGINT: 417 case Types.FLOAT: 418 case Types.DOUBLE: 419 case Types.REAL: 420 case Types.DECIMAL: // 5.5.5.4 (2012/08/18) 421 rtn = "NUMBER"; break; 422 case Types.DATE: 423 case Types.TIMESTAMP: // 5.5.5.4 (2012/08/18) 424 rtn = "DATE"; break; 425 case Types.CLOB: // 4.0.0 (2006/01/31) 426 rtn = "CLOB"; break; 427 default: 428 rtn = "NONE"; break; 429 } 430 431 return rtn; 432 } 433}