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