web-dev-qa-db-ja.com

Hibernateが引数コンストラクターを必要としないのはなぜですか?

引数なしのコンストラクターは必須です(Hibernateなどのツールは、このコンストラクターでリフレクションを使用してオブジェクトをインスタンス化します)。

私はこの手波状の答えを得ましたが、誰かがさらに説明できますか?ありがとう

90
unj2

Hibernate、およびリフレクションを介してオブジェクトを作成する一般的なコードは、 Class<T>.newInstance() を使用して、クラスの新しいインスタンスを作成します。このメソッドは、オブジェクトをインスタンス化するために、引数なしのパブリックコンストラクターを必要とします。ほとんどのユースケースでは、引数なしのコンストラクタを指定しても問題はありません。

シリアライゼーションはコンストラクターを呼び出さずにオブジェクトを作成するためにjvmマジックを使用するため、引数なしのコンストラクターがなくても回避できるシリアライゼーションに基づくハックがあります。ただし、これはすべてのVMで使用できるわけではありません。たとえば、 XStream は、引数なしのパブリックコンストラクターを持たないオブジェクトのインスタンスを作成できますが、特定のVMでのみ使用可能ないわゆる「拡張」モードで実行することによってのみ可能です。 (詳細については、リンクを参照してください。)Hibernateの設計者は、すべてのVMとの互換性を維持することを確実にし、そのようなトリックを回避し、引数なしのコンストラクタを必要とする公式にサポートされるリフレクションメソッドClass<T>.newInstance()を使用します。

125
mdma

Hibernateはオブジェクトをインスタンス化します。したがって、それらをインスタンス化できる必要があります。引数なしのコンストラクタがない場合、Hibernateはインスタンス化する方法how、つまりどの引数を渡すかを知りません。

hibernate documentation 言う:

4.1.1。引数なしのコンストラクターを実装する

HibernateがConstructor.newInstance()を使用してインスタンス化できるように、すべての永続クラスにはデフォルトのコンストラクター(非パブリックにすることができます)が必要です。 Hibernateでのランタイムプロキシ生成のために、少なくともパッケージの可視性を備えたデフォルトコンストラクターを用意することをお勧めします。

44
Bozho

みなさん、ごめんなさい。しかし、Hibernateはnotがクラスにパラメーターなしのコンストラクターが必要であることを要求します。 JPA 2.0仕様 では必須であり、これはJPAに代わって非常に不十分です。 JAXBのような他のフレームワークもこれを必要としますが、これもこれらのフレームワークに代わって非常に不自由です。

(実際、JAXBはエンティティファクトリを許可しますが、これらのファクトリを単独でインスタンス化することを要求し、--guess what-- parameterless constructorを必要とします。これは、私の本では、工場を許可します;それはどのように不自由です!)

しかし、Hibernateはそのようなものを必要としません。

Hibernateはインターセプトメカニズムをサポートしています(ドキュメントの 「Interceptor」を参照 )。これにより、必要なコンストラクターパラメーターを使用してオブジェクトをインスタンス化できます。

基本的には、休止状態を設定するときに_org.hibernate.Interceptor_インターフェイスを実装するオブジェクトを渡すと、そのインスタンスの新しいインスタンスが必要になるたびに、そのインターフェイスのinstantiate()メソッドが呼び出されます。あなたのオブジェクトなので、そのメソッドの実装はあなたが好きな方法でオブジェクトをnewできます。

私はプロジェクトでそれをしました、そしてそれは魅力のように働きます。このプロジェクトでは、可能な限りJPAを使用して処理を行い、他に選択肢がない場合にのみインターセプターなどのHibernate機能を使用します。

Hibernateは、エンティティクラスごとにINFO: HHH000182: No default (no-argument) constructor for classおよび_class must be instantiated by Interceptor_を通知する情報メッセージを起動時に発行するため、Hibernateは多少安全ではないようですが、その後、インターセプターによってインスタンス化します、それで満足です。

ツールの質問の「なぜ」部分に答えるには、Hibernate以外で、答えは「まったく正当な理由はない」であり、これは休止状態インターセプターの存在によって証明されます。クライアントオブジェクトをインスタンス化するための同様のメカニズムをサポートできたツールは数多くありますが、サポートしていないため、オブジェクトを自分で作成するため、パラメーターなしのコンストラクターが必要です。これらのツールの作成者は、無知なアプリケーションプログラマーが使用する魔法に満ちたフレームワークを作成する忍者システムプログラマーであると考えているため、これが起こっていると信じたいと思います。 ... Factory Pattern のような高度な構造が必要です。 (さて、私は誘惑そう思います。私は実際そう思いません。冗談を言っています。)

36
Mike Nakis

休止状態は、フィールドまたはプロパティアクセス戦略をサポートするORMフレームワークです。ただし、コンストラクタベースのマッピングはサポートしていません。 -のようないくつかの問題のため

クラスに多くのコンストラクタが含まれているとどうなりますか

public class Person {

    private String name;
    private Integer age;

    public Person(String name, Integer age) { ... }
    public Person(String name) { ... }
    public Person(Integer age) { ... }

}

ご覧のとおり、Hibernateはどのコンストラクターを呼び出すべきかを推測できないため、矛盾の問題に対処します。たとえば、保存されているPersonオブジェクトを取得する必要があるとします

Person person = (Person) session.get(Person.class, <IDENTIFIER>);

Personオブジェクトを取得するためにHibernateが呼び出すコンストラクタはどれですか?見えますか?

最後に、リフレクションを使用することにより、Hibernateは引数なしのコンストラクターを介してクラスをインスタンス化できます。だからあなたが電話するとき

Person person = (Person) session.get(Person.class, <IDENTIFIER>);

Hibernateは次のようにPersonオブジェクトをインスタンス化します

Person.class.newInstance();

APIドキュメントによると

クラスは、empty引数リストを持つnew式によってあたかもインスタンス化されます

ストーリーのモラル

Person.class.newInstance();

に似ています

new Person();

他に何もない

33
Arthur Ronald

実際には、引数が0のコンストラクターを持たないクラスをインスタンス化できます。クラスのコンストラクターのリストを取得し、1つを選択して、偽のパラメーターでそれを呼び出すことができます。

これは可能ですが、問題なく動作するはずですが、それはかなり奇妙だということに同意する必要があります。

Hibernateのようにオブジェクトを構築します(0-argコンストラクターを呼び出して、おそらくReflectionを介してインスタンスのフィールドを直接変更します。おそらく、セッターを呼び出す方法を知っています)。 Java-新しいオブジェクトが目的のオブジェクトになるように、適切なパラメーターを使用してコンストラクターを呼び出します。オブジェクトをインスタンス化してから、それを変更するのはやや「反Java」(または、反純粋な理論上のJava)だと思います-そして、間違いなく、直接フィールド操作を介してこれを行うと、カプセル化とすべての豪華なカプセル化のものになります。

これを行う適切な方法は、適切なコンストラクタを使用してデータベース行の情報からオブジェクトをインスタンス化する方法をHibernateマッピングで定義することだと思います...しかし、これはより複雑になります-つまり、Hibernateは両方ともより複雑な場合、マッピングはより複雑になり、すべてがより「純粋」になります。そして、これが現在のアプローチよりも有利だとは思わない(「適切な方法」で物事を行うことについて良いと感じる以外は)。

そうは言っても、Hibernateのアプローチはあまり「クリーン」ではないため、0引数のコンストラクタを持つ義務は厳密には必要ありませんが、純粋に「適切な方法」でそれをやったと思いますが、要件をある程度理解できます「合理的な理由によるとはいえ」そのかなり前に「適切な方法」から外れた理由。

6
alex

Hibernateは(リフレクションを介して)クエリの結果としてインスタンスを作成する必要があり、Hibernateはそのためにエンティティの引数なしのコンストラクターに依存するため、引数なしのコンストラクターを提供する必要があります。明確ではないものは何ですか?

5
Pascal Thivent

名前を変更する/名前付けの競合、コンストラクター内の未定義ロジックを使用して、パラメーター化されたコンストラクターの任意のパラメーターにデータを一致させようとするよりも、リフレクションを介してパラメーターなしのコンストラクターでオブジェクトを作成し、リフレクションを介してそのプロパティをデータで満たす方がはるかに簡単ですオブジェクトのプロパティと一致しないパラメータセットなど。

多くのORMおよびシリアライザーには、リフレクションによるパラメーター化されたコンストラクターが非常に脆弱であり、パラメーターレスコンストラクターがアプリケーションの安定性と開発者に対するオブジェクトの動作の制御の両方を提供するため、パラメーターレスコンストラクターが必要です。

2
Kaerber

Hibernateは、遅延読み込みにプロキシを使用します。コンストラクタを定義しない場合、またはプライベートにする場合、プロキシメカニズムに依存しないものがいくつか動作する可能性があります。たとえば、クエリAPIを使用してオブジェクトを(コンストラクタなしで)直接ロードします。

ただし、session.load method()を使用すると、コンストラクターが利用できないため、プロキシジェネレーターlibからInstantiationExceptionが発生します。

この男は同様の状況を報告しました:

http://kristian-domagala.blogspot.com/2008/10/proxy-instantiation-problem-from.html

1
haps10

静的および非静的内部クラスの違いを説明するJava言語仕様のこのセクションを確認してください: http://Java.Sun.com/docs/books/jls /third_edition/html/classes.html#8.1.

静的な内部クラスは、概念的には.Javaファイルで宣言された通常の一般クラスと変わりません。

HibernateはProjectインスタンスから独立してProjectPKをインスタンス化する必要があるため、ProjectPKは静的な内部クラスであるか、独自の.Javaファイルで宣言する必要があります。

参照 org.hibernate.InstantiationException:デフォルトコンストラクターなし

0
Amitābha