問題の簡単な説明:
- ビジネスユーザーの概念には、一連の仮想「クラス」があります。 たとえば、「サイト」、「フォルダ」、「ニュース」など。 これらの各クラスには、一連のフィールド(属性)があります。
- これまでのところ、クラスの継承はありません。フィールドはプリミティブなString / Integer / Long / Enum / Booleanに制限されています。
- 各クラスは、objects_sites、objects_news、objects_folderなどの個別のテーブルに書き込まれます。 テーブルには、常にオブジェクトIDとフィールドの列が含まれます。
- これらのオブジェクトの読み込みは、JPAが提供する必要なキャッシュ/トランザクション/遅延読み込みなどの機能を使用して、JPA(Hibernate)を介して動作するようにする必要があります。
このタスクを完了するために、次が使用されました。
- データベースは、MySQL 5.0、InnoDB、3つのデータベーススキーマです(ユーザータイプからシステムタイプを分離するために、異なるタイプが異なるスキーマに存在する場合があります)
- Sun JDK 6.0
- Tomcat 6 + JOTM 2.1.9 + Hibernate 3.5.0-Final ( パッチ適用済み )
- クラスを作成するために、 CGLib 2.2 (Hibernateに含まれています)とASM 3.2 (Hibernateに含まれている3.1)の束を使用しました
メタモデル
どこから始めますか。 すべては、オブジェクトメタモデルの構築から始まります(ビジネスモデルと混同しないでください)。 これまでのところ、次のモデルに焦点を合わせることが決定されました。
-構成(複雑なタイプ) -タイプ -単純型(simpletypetype-文字列/ long / int / enum、enumクラス) -ComplexType(属性、一部のプロパティ) -属性(名前、タイプ、一部のプロパティ、一部のJPAプロパティ)
同時に、テスト用および一般的な美しさのために、XMLスキーマ(このイデオロギーが基礎とされた)からこの構成を構築できるクラスが作成されました。 たとえば、これから:
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .
ところで、名前空間「http:// www.arptek.ru/core/meta」で追加の属性と要素を使用することに注意してください。 そのため、追加の属性は常に別の名前空間に配置し、要素は一般にxs:注釈/ xs:appinfoに配置する必要があります。 この回路を構成に変換するコードは、 ここにあります 。
Beanクラスの生成
生成コードは3つのタスクを実行します。
- 必要なタイプを考慮して、すべての属性のget / setメソッドを生成します。 これは、Hibernate側と特定の型の構造を知っているコード側の両方からデータにアクセスするために必要です。
- また、Fields内部クラスも生成し、そのインスタンスは各オブジェクトのコレクションに配置されます。 その結果、オブジェクトのフィールドを事前に知ることなく、フィールドのリストを操作できます。 これは、オブジェクトエディター(ほとんどすべてのタイプに1つ)を作成するのに便利です。
- また、JPAが正しく動作するための注釈をコードに提供することを忘れないでください
コードを解析する前の注意事項:
- Javaはバイトコードランタイムです。 バイトコードは、Javaマシンが(ほぼ)理解できる唯一のコードです。 クラスファイル(* .class)にバイナリ形式で記述されています。 通常、javacコンパイラによってバイトコードにコンパイルされるJavaファイル(* .java)から取得されます。
- バイトコードはスタックマシンの動作を制御する命令のセットです。 指示には、ローカル変数、定数、クラスフィールドのスタックへの読み込み、それらに対する操作(算術、論理、型変換)、クラスメソッドおよびインターフェイスの呼び出しが含まれます。
- すべての操作は、すでにスタック上にあるものに対して実行されます。 2つの数字を追加しますか? スタックにプッシュする必要があります。 オブジェクトメソッドを呼び出しますか? オブジェクトとそのパラメーターへの参照をスタックにプッシュする必要があります。
- 複雑なオブジェクトはヒープにプッシュされ、オブジェクトへのポインターのみがスタックにプッシュされます。 C / C ++をよく学んだことを願っています:-)
アセンブリ言語の特定のJavaコードに対応する簡単な操作を実行する方法に関する知識が必要です。 たとえば、コマンド
this.aを返します。
次のコードに変換する必要があります。
0 aload_0 1 getfield classname.a 4 lreturn
この場合、特定の操作コードは、引数を持つ型に依存します。 int / boolean / longの場合、異なる操作コードが使用されます。 しかし、良い点は、CGLibとASMはJavaマシン実装のこれらの機能を私たちから隠し、どのタイプが引数を持ち、どのオペレーションコードを使用するかを自分で制御することです。 たとえば、変数を定義した場合:
ce.declare_field(Constants.ACC_PROTECTED、fieldName、type、null);
その後、CGLib / ASMはそのタイプを記憶し、コマンドを使用してコードを生成します
codeEmitter.load_this(); codeEmitter.getfield(constantName); codeEmitter.return_value();
生成されたコードには、設定した変数のタイプで動作するオペランドが正確に含まれます。 そのため、クラスフィールドの値を返すのが高くなりました。 より正確には、このコードは2つの部分に分割できます。 フィールド値をスタックにプッシュする:
codeEmitter.load_this(); codeEmitter.getfield(constantName);
スタックのトップ値を返します:
codeEmitter.return_value();
関数の最初の引数をスタックに追加します(何かにさらに割り当てるため)。
codeEmitter.load_arg(0);
しかし! 問題は、putfield操作中に、スタックの最上部に値が必要であり、その下にフィールド所有者オブジェクトへの参照があることです。 そのため、最初にオブジェクトへの参照を取得し、次に引数をスタックに追加してから、フィールドに値を割り当てます。
codeEmitter.load_this(); codeEmitter.load_arg(0); codeEmitter.putfield(fieldName); codeEmitter.return_value();
メソッド呼び出し。 これを行うには、スタックにオブジェクトと引数への参照が必要です(上記の引数)。
codeEmitter.invoke_virtual(parentClass、新しい署名( getterName、fieldValueType、TYPES_EMPTY));
この場合、invoke_staticを使用して静的メソッドを呼び出し、invoke_interfaceを使用してインターフェイス(parentClass-interface)で定義されたメソッドを呼び出し、invoke_specialを使用してコンストラクターを呼び出します(メソッド名は「<init>」)。 ところで、オブジェクトの作成とコンストラクターの呼び出しは別の操作であり、何かによって非常にうまく分離できます。 ここでは、たとえば、LinkedHashMapを作成します。
ローカルローカル= e.make_local(TYPE_LINKED_HASH_MAP); e.new_instance(TYPE_LINKED_HASH_MAP); e.dup(); e.push(fieldClasses.size()); e.push(10); e.push(1f); e.invoke_constructor(TYPE_LINKED_HASH_MAP、SIG_MAP_INIT_INT_FLOAT); e.store_local(ローカル);
マップを保存するローカル変数を準備します。 次に、ヒープ上に新しいオブジェクトを作成します(メモリを割り当てます)。 次に、dup()オペレーションを使用してスタックの一番上の引数を複製します-コンストラクターを呼び出した後、リンクの1つが残され、値を保存する必要があるためです。 また、コンストラクター呼び出しの引数をスタックに配置し(型を厳密に監視すると、autoconversion int-> floatは機能しません)、invoke_constructorを使用してコンストラクターを呼び出します(invoke_specialに変換されます)。 最後の行-結果をローカル変数に保存します。 その後、スタックは空になります。
クラスの生成:
*このソースコードは、 ソースコードハイライターで強調表示されました。
- ClassWriter cw = 新しい DebuggingClassWriter(
- ClassWriter.COMPUTE_FRAMES);
- ClassEmitter ce = 新しい ClassEmitter(cw);
- Type [] interfaces = getInterfacesTypes(complexType);
- ce.begin_class(Constants.V1_6、Constants.ACC_PUBLIC、className、
- Type.getType(complexType.getSuperClass())、インターフェース、
- Constants.SOURCE_FILE);
- if (complexType.isJpaEntity()){
- ce.visitAnnotation(ANNOTATION_DESCRIPTOR_ENTITY、 true )
- .visitEnd();
- }
- if (StringUtils.isNotEmpty(complexType.getJpaTableName())){
- final AnnotationVisitor av = ce.visitAnnotation(
- ANNOTATION_DESCRIPTOR_TABLE、 true );
- av.visit( "name" 、complexType.getJpaTableName());
- av.visitEnd();
- }
- {
- if (options.generateFieldAccessors){
- final String fieldsMapFieldName = "$ fields $" ;
- ce.declare_field(Constants.ACC_PRIVATE
- | Constants.ACC_FINAL、fieldsMapFieldName、
- TYPE_MAP、 null );
- generateConstructorWithFieldsInit(mainClass、ce、
- fieldClasses、fieldsMapFieldName);
- generateGetFieldsMethod(ce、fieldsMapFieldName);
- generateGetFieldMethod(ce、fieldsMapFieldName);
- } else {
- EmitUtils.null_constructor(ce);
- }
- for (属性属性:complexType.getAttributes()){
- processAttribute(ce、属性);
- }
- }
- ce.end_class();
- {
- バイト [] bs = cw.toByteArray();
- log.info( "生成されたクラス '" + className + "' of" + bs.length
- + "bytes" );
- result.put(className、bs);
- }
最初と最後の行は、チェーンClassEmitter-> ClassWriter-> ByteArrayOutputStream-> bytesを編成します。 クラスコードを生成するときは、ClassEmitterを使用し、CodeEmitterを使用するメソッド、およびアノテーションがない場合は、AnnotationVisitorを使用します(visitEnd()で忘れずに閉じることも重要です)。
クラスを作成するには、そのクラスの名前、バージョン、親クラス、インターフェースなどを選択します。通常のクラスを別の言語で記述しているかのように選択します。 ただし、通常のJavaコードとは異なり、このようなコードはコンパイルできますが、お金を稼ぐことはできません-何かを忘れたり、正しく実行しなかったためです。 たとえば、プロシージャからリターンコードを追加するのを忘れた場合...クラスは生成されますが、機能しません。 つまり、コンパイル段階でそのようなコードのエラーを追跡することは不可能であり、テストケースを実行する必要があります。 すでにアセンブラの認識の準備ができている人はすでにそれについて知っていますが:)
行11から21は、JPAクラスにエンティティとテーブルの注釈を追加します。 時間を大幅に節約できる重要なポイントは、visitAnnotationメソッドが文字列を受け入れますが、アノテーションクラスの名前ではなく、内部Java形式の説明を受け入れることです。 たとえば、javax.persistence.Entityの場合、「Ljavax / persistence / Entity;」になります。 ただし、Typeクラスを使用して簡単に取得できます(ほぼすべての行で使用されます)。
1 プライベート スタティック 最終文字列ANNOTATION_DESCRIPTOR_ENTITY = Type.getType(
2エンティティ。 class ).getDescriptor();
36行目は、単純なコンストラクターを追加するために必要です(別のケースでは少し後で)。 ループ39-41では、新しいフィールドを追加し、各属性のメソッドとデフォルトの定数値を取得/設定します。
*このソースコードは、 ソースコードハイライターで強調表示されました。
- if (attribute.getType()instanceof EmptyType)
- 帰る
- String fieldName = attribute.getFieldName();
- 文字列getterName = attribute.getGetterName();
- 文字列setterName = attribute.getSetterName();
- final String javaMethodNamePart = WordUtils.capitalize(
- attribute.getAttrName()、 new char [] { '_' })。replace( "_" 、 "" );
- final Type type = getTypeFromXmlSchemaTypeName(attribute.getType());
- if (fieldName == null )
- fieldName = "m $" + StringUtils.uncapitalize(javaMethodNamePart);
- if (getterName == null )
- getterName = "get" + javaMethodNamePart;
- if (setterName == null )
- setterName = "set" + javaMethodNamePart;
- 最後のブール値jpaId = attribute.isJpaId();
- final String strDefaultValue;
- if (!jpaId){
- strDefaultValue = attribute.getStrDefaultValue();
- } else {
- strDefaultValue = null ;
- }
- if (strDefaultValue == null ){
- ce.declare_field(Constants.ACC_PRIVATE、fieldName、type、 null );
- {
- //ゲッターを作成します
- CodeEmitter codeEmitter = ce.begin_method(Constants.ACC_PUBLIC、
- 新しい署名(getterName、type、TYPES_EMPTY)、
- TYPES_EMPTY);
- continueAttributeJpaColumnName(属性、codeEmitter);
- continueAttributeJpaLob(属性、codeEmitter);
- continueAttributeJpaId(属性、codeEmitter);
- codeEmitter.load_this();
- codeEmitter.getfield(fieldName);
- codeEmitter.return_value();
- codeEmitter.end_method();
- }
- {
- // nullセーフセッターを作成します
- CodeEmitter codeEmitter = ce.begin_method(Constants.ACC_PUBLIC、
- 新しい署名(setterName、Type.VOID_TYPE、
- 新しいタイプ[] {type})、TYPES_EMPTY);
- codeEmitter.load_this();
- codeEmitter.load_arg(0);
- codeEmitter.putfield(fieldName);
- codeEmitter.return_value();
- codeEmitter.end_method();
- }
- } else {
- final Object defaultValue = toValue(type、strDefaultValue);
- final String constantName = "DEFAULT_" + fieldName;
- ce.declare_field(Constants.ACC_PROTECTED | Constants.ACC_FINAL
- | Constants.ACC_STATIC、constantName、type、 null );
- {
- CodeEmitter staticHook = ce.getStaticHook();
- EmitUtils.push_object(staticHook、defaultValue);
- staticHook.putfield(constantName);
- }
- ce.declare_field(Constants.ACC_PROTECTED、fieldName、type、 null );
- {
- //ゲッターを作成します
- 最終署名署名= 新しい署名(getterName、type、
- TYPES_EMPTY);
- 最終的なCodeEmitter codeEmitter = ce.begin_method(
- Constants.ACC_PUBLIC、署名、TYPES_EMPTY);
- continueAttributeJpaColumnName(属性、codeEmitter);
- continueAttributeJpaLob(属性、codeEmitter);
- codeEmitter.load_this();
- codeEmitter.getfield(fieldName);
- ラベルifNull = codeEmitter.make_label();
- codeEmitter.ifnull(ifNull);
- {
- codeEmitter.load_this();
- codeEmitter.getfield(fieldName);
- codeEmitter.return_value();
- }
- codeEmitter.mark(ifNull);
- {
- codeEmitter.load_this();
- codeEmitter.getfield(constantName);
- codeEmitter.return_value();
- }
- codeEmitter.end_method();
- }
- {
- //セッターを作成します
- 最終署名署名= 新しい署名(setterName、
- Type.VOID_TYPE、 新しいタイプ[] {type});
- 最終的なCodeEmitter codeEmitter = ce.begin_method(
- Constants.ACC_PUBLIC、署名、TYPES_EMPTY);
- codeEmitter.load_this();
- codeEmitter.load_arg(0);
- codeEmitter.putfield(fieldName);
- codeEmitter.return_value();
- codeEmitter.end_method();
- }
- }
フィールドが「空」の場合、チェック1-2が必要です。 このようなフィールドを使用すると、同じテーブルに保存されないが、たとえば独自の状態を管理する属性をクラスに含めることができます。 行4〜18では、変数が初期化され、内部変数とメソッドの将来の名前が定義されます。 コンピューターによって生成されるメソッドと変数には、「$」記号を使用するのが一般的ですが、ユーザーコードでは使用しないことをお勧めします。 もちろん、get / setメソッドはHibernateとビジネスコードから呼び出されるため、この記号はありません。
JPA IDにはデフォルト値が使用されないため、念のため、23〜27行目で設定されていないことを確認してください。 デフォルト値が設定されていて、設定されていない場合のコード生成は異なります。 存在しない場合は、もちろん、より単純で簡単です。フィールド自体は30行目でクラスに追加され、32-46行目でgetメソッドが設定され、48-58行目で追加されます。
デフォルト値のケースには以下が必要です。
- デフォルト値で定数を定義します(60〜61行目)
- さらに、複雑なタイプ(ブール/整数/ Longを含む)の場合、定数を初期化するために静的コードを使用する必要があります。 通常のコードを書くとき、これは必要ありません-コンパイラが私たちのためにそれをします。 ただし、ここでは手動で行う必要があります。 66〜70行目で、StaticHookに新しい行を追加します。 次に、クラスの操作を終了すると、StaticHookのすべての行がクラスの静的初期化ブロックに転送されます(ほとんどの場合、詳細に関心がある人は、実際に何が起こるかを分解して確認できます)。
- setメソッドは非常に簡単です(104〜116行目)
- しかし、getメソッドにブランチを追加しました。 そうでない場合があります-クラスコンストラクターの変数をデフォルト値で初期化する場合(以下のコンストラクターの操作方法)。 しかし、クラス内の変数がnullに設定されている場合、定数値が返されるようにしたかったのです。 ブランチを整理するために、新しいリンク(ラベル)を作成します。 その上で、値がゼロの場合、遷移を実行します。 次に、条件分岐コマンドを使用します(89行目)。 もう一度-今は条件をチェックしていません-この条件のテストをメソッドコードに追加しています。 条件が満たされると、マークが付けられたコード内の場所への遷移があります(95行目)。 行われていない場合、通常のブランチ91-93が実行されます。 さらに、各コード実行ブランチには、結果値の戻りコードが含まれています。
フィールドセットアクセス
上記のコードは、(願わくば)「bean」(get / setフィールドとメソッドを持つクラス)を正常に生成しました。 ただし、getField(name)とgetFields()の2つのメソッドを生成する必要があります。 これらのメソッドは、コレクションを介してオブジェクトのフィールドへのアクセスを許可する必要がありますが、ネイティブメソッドを呼び出してシステムの速度を低下させないためには、同時にリフレクションを使用しないでください。
これを行うには、オブジェクトの各属性に対して、2つのメソッドgetValue()およびsetValue()を含むクラスを追加で生成します。これらのメソッドは、これらの呼び出しをメインクラスに委任します。 このようなクラスの生成は非常に簡単ですが、クラスコンストラクターが空ではなく、単一のパラメーター(親オブジェクトへのリンク)を受け入れるという点で興味深いものです。
*このソースコードは、 ソースコードハイライターで強調表示されました。
- final String holderFieldName = "$ parent $" ;
- ce.declare_field(Constants.ACC_PRIVATE | Constants.ACC_FINAL、
- HolderFieldName、parentClass、 null );
- {
- CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC、
- 新しい署名( "<init>" 、Type.VOID_TYPE、
- 新しいタイプ[] {parentClass})、 null );
- e.load_this();
- e.super_invoke_constructor();
- e.load_this();
- e.load_arg(0);
- e.putfield(holderFieldName);
- e.return_value();
- e.end_method();
- }
リンク自体は$ parent $変数に保存されます。 get / setメソッドは非常に簡単に行われます:
*このソースコードは、 ソースコードハイライターで強調表示されました。
- {
- //ゲッターを作成します
- CodeEmitter codeEmitter = ce.begin_method(Constants.ACC_PUBLIC、
- 新しい署名( "getValue" 、fieldValueType、TYPES_EMPTY)、
- TYPES_EMPTY);
- codeEmitter.load_this();
- codeEmitter.getfield(holderFieldName);
- codeEmitter.invoke_virtual(parentClass、 新しい署名(
- getterName、fieldValueType、TYPES_EMPTY));
- codeEmitter.return_value();
- codeEmitter.end_method();
- }
- {
- // nullセーフセッターを作成します
- CodeEmitter codeEmitter = ce.begin_method(Constants.ACC_PUBLIC、
- 新しい署名( "setValue" 、Type.VOID_TYPE、
- 新しいタイプ[] {fieldValueType})、TYPES_EMPTY);
- codeEmitter.load_this();
- codeEmitter.getfield(holderFieldName);
- codeEmitter.load_arg(0);
- codeEmitter.invoke_virtual(parentClass、 新しい署名(
- setterName、Type.VOID_TYPE、
- 新しいタイプ[] {fieldValueType}));
- codeEmitter.return_value();
- codeEmitter.end_method();
- }
各属性のこれらのクラスはループで生成され、Map <Attribute、String = class name>に保存されます。
*このソースコードは、 ソースコードハイライターで強調表示されました。
- ClassWriter cw = 新しい DebuggingClassWriter(
- ClassWriter.COMPUTE_FRAMES);
- ClassEmitter ce = 新しい ClassEmitter(cw);
- 文字列fieldClassName = processAttributeField(className、
- mainClass、ce、属性);
- fieldClasses.put(属性、ce.getClassType());
なんで? 後で親クラスのコンストラクターで子オブジェクトのインスタンスを作成し、それらを内部フィールド$ fields $に配置するには:
*このソースコードは、 ソースコードハイライターで強調表示されました。
- public void generateConstructorWithFieldsInit(最終タイプmainClass、
- ClassEmitter ce、Map <Attribute、Type> fieldClasses、
- final String fieldsMapFieldName){
- CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC、TypeUtils
- .parseConstructor( "" )、 null );
- e.load_this();
- e.super_invoke_constructor();
- ローカルローカル= e.make_local(TYPE_LINKED_HASH_MAP);
- e.new_instance(TYPE_LINKED_HASH_MAP);
- e.dup();
- e.push(fieldClasses.size());
- e.push(1f);
- e.invoke_constructor(TYPE_LINKED_HASH_MAP、SIG_MAP_INIT_INT_FLOAT);
- e.store_local(ローカル);
- for (エントリ<属性、タイプ> fieldTypes:fieldClasses.entrySet()){
- e.load_local(ローカル);
- e.push(fieldTypes.getKey()。getAttrName());
- // = new <...> Field(this);
- e.new_instance(fieldTypes.getValue());
- e.dup();
- e.load_this();
- e.invoke_constructor(fieldTypes.getValue()、 新しい署名( "<init>" 、
- Type.VOID_TYPE、 新しいタイプ[] {mainClass}));
- // $ fields $ .put(fieldName、field);
- e.invoke_virtual(TYPE_LINKED_HASH_MAP、SIG_MAP_PUT);
- }
- e.load_this();
- e.load_local(ローカル);
- e.invoke_static(TYPE_COLLECTIONS、SIG_COLLECTIONS_UNMODIFIABLEMAP);
- e.putfield(fieldsMapFieldName);
- e.return_value();
- e.end_method();
- }
最初の行では、LinkedHashMapを作成します(同時に、Genericを使用してパラメーター化を行います-アセンブラーにはほとんどありません)。 次に、子Fieldsオブジェクトを入力し、念のため、Collections.unmodifiableMapでラップして、最終フィールドに保存します。 ちなみに、最後のメソッドはinvoke_staticを使用して呼び出され、コンストラクターでもreturn_value操作で完了しますが、仕様(4〜5行目)ではVOIDを返します。
最後に、getField(name)メソッドとgetFields()メソッドを作成します。これらのメソッドにTransientアノテーションを忘れずに提供し、JPAがこれらをオブジェクトフィールドと見なさないようにします。
*このソースコードは、 ソースコードハイライターで強調表示されました。
- public void generateGetFieldMethod(ClassEmitter ce、
- final String fieldsMapFieldName){
- CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC、
- SIG_FIELDSHOLDER_GETFIELD、 null );
- e.visitAnnotation(ANNOTATION_DESCRIPTOR_TRANSIENT、 true ).visitEnd();
- e.load_this();
- e.getfield(fieldsMapFieldName);
- e.load_arg(0);
- e.checkcast(TYPE_OBJECT);
- e.invoke_interface(TYPE_MAP、SIG_MAP_GET);
- e.checkcast(TYPE_FIELD);
- e.return_value();
- e.end_method();
- }
- public void generateGetFieldsMethod(ClassEmitter ce、
- final String fieldsMapFieldName){
- CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC、
- SIG_FIELDSHOLDER_GETFIELDS、 null );
- e.visitAnnotation(ANNOTATION_DESCRIPTOR_TRANSIENT、 true ).visitEnd();
- e.load_this();
- e.getfield(fieldsMapFieldName);
- e.return_value();
- e.end_method();
- }
受信したクラスをロードする
各クラスのバイト[]を生成した後、それらをメモリにロードする必要があります。 これは、ClassLoaderを定義することで実行されます(または、URLClassLoaderを使用してディスクに保存し、そこからロードできます)。
*このソースコードは、 ソースコードハイライターで強調表示されました。
- 保護された Map <String、Class <?>> loadClasses(
- 最終マップ<String、 バイト []>生成){
- 最終スレッドcurrentThread = Thread.currentThread();
- final ClassLoader classLoader = currentThread.getContextClassLoader();
- final Map <String、Class <?>> classes = new ClassLoader(classLoader){
- 保護された Map <String、Class <?>> loadClasses(){
- Map <String、Class <?>> result = new LinkedHashMap <String、Class <?>>(
- generated.size());
- for (エントリ<String、 バイト []>エントリ:generated.entrySet()){
- final String className = entry.getKey();
- 最終バイト [] byteCode = entry.getValue();
- log.info( "Defining class" + className + "..." );
- final Class <?> cls = defineClass(className、byteCode、0、
- byteCode.length);
- result.put(cls.getName()、cls);
- }
- 結果を返す ;
- }
- } .loadClasses();
- クラスを返す ;
- }
ところで、このClassLoaderを忘れてはいけません-現在のClassLoaderがパラメーターとして渡されたクラスをロードできない場合は動作しないため、Hibernateでこれらのクラスをロードするために後で必要になります。
実用化
さらなる研究のために興味深いかもしれないいくつかのリンク:
- 実際のクラス構成の例
- 構成をメタ構成に変換し、クラスを生成し、エンティティクラスのコレクションを作成するクラス
- EntityFactory読み込みクラス
- JPAを介してデータベースからクラスをロードする役割を担うクラス
- このエンジンで実行するサイトの例 (私のものではありません:))
結果として
- データ操作はHibernateで処理されるようになりました。 ロードと保存を処理します。 ロード時のバッチ操作のサポート、キャッシュおよびトランザクションのサポートがあります。 常に少ないバイクが良いです。
- Hibernateはキャッシュにも関与しています(ただし、クラスター化されたソフト参照キャッシュに基づいています)。 JConsoleからキャッシュステータスを監視できます。
- データのロードの最適化により生産性が向上しました(1つのSQLで複数のオブジェクトのデータベースから一度にバッチロードする可能性があります)
- ビジネスコードははるかに便利で楽しく書けるようになりました:)