クラスファイルを動的に作成したい。ここに行きます... 指定された ResultSet を使用してメタデータを抽出する ResultSet に存在するすべての列のゲッター メソッドとセッター メソッドを使用してクラス ファイルを動的に構築したいと考えています。また、生成されたこのクラス ファイルを、後で使用するときにいつでも使用できるようにする必要があります。これを実装するためのより良い方法を提案してくれる人はいますか。また、これを実装するために利用可能な既存のjarファイルがあれば、それは役に立ちます。
8 に答える
おそらく、 Apache Beanutilsが要件に合っているのではないでしょうか?
Dynabeansのセクションを参照してください。
特に:
3.3 ResultSetDynaClass (ResultSet を DynaBeans でラップ)
DynaBean API の非常に一般的な使用例は、通常は JavaBeans として表示されない他の「もの」のコレクションをラップすることです。ラップすると便利な最も一般的なコレクションの 1 つは、JDBC ドライバーに SQL SELECT ステートメントの実行を要求したときに返される java.sql.ResultSet です。Commons BeanUtils は、結果セットの各行を DynaBean として表示するための標準メカニズムを提供します。これは、次の例に示すように利用できます。
Connection conn = ...;
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery
("select account_id, name from customers");
Iterator rows = (new ResultSetDynaClass(rs)).iterator();
while (rows.hasNext()) {
DynaBean row = (DynaBean) rows.next();
System.out.println("Account number is " +
row.get("account_id") +
" and name is " + row.get("name"));
}
rs.close();
stmt.close();
3.4 RowSetDynaClass (切断された ResultSet を DynaBeans として)
ResultSetDynaClass は、SQL クエリの結果を一連の DynaBean として表現するための非常に便利な手法ですが、重要な問題は、アプリケーションによって行が処理されている間、基礎となる ResultSet を開いたままにしておく必要があることです。これは、Struts フレームワークによって提供されるようなモデル-ビュー-コントローラ アーキテクチャで、モデル レイヤからビュー レイヤに情報を伝達する手段として ResultSetDynaClass を使用する機能を妨げます。は最終的に閉じられます (そして、基になる Connection が接続プールを使用している場合は、その接続プールに返されます)。
RowSetDynaClass クラスは、この問題に対する別のアプローチを表しています。このようなインスタンスを構築すると、基礎となるデータが、結果を表すメモリ内 DynaBeans のセットにコピーされます。もちろん、この手法の利点は、返された実際のデータを処理する前に、ResultSet (および対応するステートメント) をすぐに閉じることができることです。もちろん、欠点は、結果データをコピーするためのパフォーマンスとメモリのコストを支払わなければならず、結果データが使用可能なヒープ メモリに完全に収まらなければならないことです。多くの環境 (特に Web アプリケーション) では、通常、このトレードオフは非常に有益です。
追加の利点として、RowSetDynaClass クラスは java.io.Serializable を実装するように定義されているため、それ (および結果の各行に対応する DynaBeans) を簡単にシリアライズおよびデシリアライズできます (基礎となる列の値も同様である限り)。シリアル化可能)。したがって、RowSetDynaClass は、SQL クエリの結果をリモートの Java ベースのクライアント アプリケーション (アプレットなど) に送信するための非常に便利な方法を表します。
ただし、状況の音から、データベースクエリから返されたばかりの ResultSet の内容に基づいて、実行時にこのクラスを作成したいことを理解しています。これはまったく問題なく、バイトコード操作で実行できます。
しかし、これによってどのようなメリットが得られると思いますか? 他のコードはこのクラスのメソッドを呼び出すことができません (コンパイル時に存在しなかったため)。したがって、この生成されたクラスを実際に使用する唯一の方法は、リフレクションを介するか、親クラスのメソッドを介するか、実装されたインターフェース(ResultSetを拡張すると仮定します)。後者はバイトコード ウィービングなしで実行できます (インターフェイスの任意のランタイム実装の動的プロキシgetFoo
を参照してください)。前者を実行している場合、クラスを持ち、リフレクションを介してメソッドを機械的に呼び出す方法がどのように優れているかわかりません。呼び出すだけですresultSet.getString("foo")
-遅くなり、扱いにくくなり、型安全性が低下します。
目標を達成するために本当にクラスを作成しますか?
BCELを見たいと思うかもしれませんが、他のバイトコード操作ライブラリも利用できると思います。
Java 6 を使用している場合は、コードを記述して Java コンパイラを直接呼び出すことができます。
Files[] files1 = ... ; // input for first compilation task
Files[] files2 = ... ; // input for second compilation task
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> compilationUnits1 =
fileManager.getJavaFileObjectsFromFiles(Arrays.asList(files1));
compiler.getTask(null, fileManager, null, null, null, compilationUnits1).call();
Iterable<? extends JavaFileObject> compilationUnits2 =
fileManager.getJavaFileObjects(files2); // use alternative method
// reuse the same file manager to allow caching of jar files
compiler.getTask(null, fileManager, null, null, null, compilationUnits2).call();
fileManager.close();
次に、そのクラスをロードする必要がありますが、クラスローダーを使用すると簡単に実行できます。
悲しいことに、これはJavaでしなければならないことです。
C# では、'var' 型を使用するだけです。
私はそれが機能するはずの方法に混乱しています。そして、私はそれが可能だとは思わない. 理由は次のとおりです。
アプリケーションの残りの部分でクラス コードを使用する場合は、インターフェイス (またはリフレクションの多用) が必要です。つまり、事前に列の型を知っていることになり、生成されたクラスの目的が無効になります。
生成されたクラスは、実行時に別のクラスと衝突する可能性があります。SQL 呼び出しごとに新しいクラスを作成すると、同じ目的で別のクラスが作成されます。そして、これらはおそらく「equals」への通常の呼び出しさえ渡さないでしょう。以前に実行されたステートメントからクラスを検索する必要があります。また、柔軟性が失われたり、ヒープがクラスでいっぱいになったりします。
JSFでは、Beanとマップを交換可能に使用できることがわかりました。したがって、get / setterの完全なセットを作成するのではなく、ah:tableを作成するだけの結果を処理する場合は、各行のマップを含むリストを作成する方がはるかに簡単です。ここで、キーは列名(または数値)、値は列の内容です。
後でタイプセーフにするために関連性があることがわかった場合は、バックエンドコードをBeanで作り直し、JSFコードを変更しないでおくことができます。