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


103

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

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


7
参考までに:The no-argument constructor is a requirement 間違っているの主張と、これがなぜそうであるかを説明するために進むすべての回答は、これが実際にそうであるかどうかを疑うことなく、間違っています(報奨金を受け取ったとしても受け入れられた回答を含む)。この回答を参照してください:stackoverflow.com/a/29433238/773113
Mike Nakis '27 / 04/15

2
JPAのプロバイダーとしてhibernateを使用している場合は必須です。
アマルゴビナス

1
@MikeNakisあなたは間違ったマイクです。Hibernateは、JPA(Amalgovinus)のプロバイダーとしてhibernateを使用している場合、オブジェクトをインスタンス化するデフォルトのコンストラクターを必要とします。それ以外の場合、HibernateはCaused by: org.hibernate.InstantiationException: No default constructor for entity: : hibernate.tutorial.Student、私が遭遇したばかりの場合と同様にレポートします
Mushy

@Mushy質問は「hibernate」および「orm」でタグ付けされています。「jpa」でタグ付けされていません。質問にはJPAについての言及はありません。
Mike Nakis 2017年

1
@MikeNakis私はマイクに同意しますが、Hibernateは「JPA」の実装として使用され、「JPA」または「ORM」がない場合は使用されません。したがって、Hibernateは「JPA」を実装しているという前提があります。
Mushy 2017年

回答:


138

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

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


31
参考:コンストラクタは公開する必要はありません。それはパッケージの可視性を持つことができ、HibernateはそのsetAccessible(true)上にあるべきです。
グレイ

操作に必要なフィールドを設定するために、デフォルト以外のコンストラクターでカスタムUserTypeを作成できますか?
L-Samuels

1
参考までObjectInputStreamに、sun.reflect.ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToGetInstanceOf, Object.class.getConstructor()).newInstance()デフォルトのコンストラクターなしでオブジェクトをインスタンス化する場合(JDK1.6 for Windows)に沿って何かを行います
SamYonnou

再:It can have package visibility and Hibernate should setAccessible(true)。DOESは、itリフレクション経由でインスタンス化されるクラスを意味ですか?そして、どういうHibernate should setAccessible(true)意味ですか?
ケビンメレディス14

Objenesisはこれを行い、spring-dataやmockitoなどの多くのフレームワークで広く使用されていますgithub.com/easymock/objenesis
ltfishie

46

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

Hibernateのドキュメントは言います:

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

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


6
コンストラクターの可視性については、JPA v2.0を使用している場合、JSR-317で次のように通知されていることに注意してください引数なしのコンストラクターはpublicまたはprotectedである必要があります
ホセ・Andias

@Bozhoこんにちは、私は内部的にhibernateがConstructor.newInstance()を使用してオブジェクトをインスタンス化する場合、どのようにhibernateがセッターを定義せずにフィールドに値を設定するのか疑問がありますか?
Vikas Verma

引数のないパブリックコンストラクターを持つ@Embeddable非プライベートサブクラスに対してこの警告が表示される理由がわかりません...
Amalgovinus

Constructor.newInstance()は引数を取り、問題(実際には問題ではない)がそれらの引数をマッピングしています。hibernateがこの問題を解決しなかった理由はわかりません。比較のために:ジャクソンの@JsonCreatorアノテーションはこれを行い、不変オブジェクトの多くの利点がありました。
drrob


43

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

(実際には、JAXBはエンティティファクトリを許可していると思われますが、それ自体でこれらのファクトリをインスタンス化することを主張し、それらに--guesswhat-- パラメータレスコンストラクタが必要であることを要求します。 !)

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

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

基本的に、何をするかというと、Hibernateをセットアップするときに、org.hibernate.Interceptorインターフェースを実装するオブジェクトにそれを渡し、Hibernateがinstantiate()自分のオブジェクトの新しいインスタンスを必要とするときはいつでも、そのインターフェースのメソッドを呼び出すため、そのメソッドの実装でnew好きなようにオブジェクトを作成します。

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

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

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


1
最後に、それを手に入れる人!オブジェクトのインスタンス化プロセスを不明瞭にするこれらのフレームワーク(適切な依存関係の注入とリッチオブジェクトの動作にとって非常に重要です)を扱うよりも多くの時間を費やしました。さらに、Javaリフレクションを使用すると、newInstance()を使用せずにオブジェクトを作成できます。メソッドgetDeclaredConstructorsは、JDK 1.1以降、リフレクションAPIに含まれています。JPA仕様設計者がこれを怠っていたことは恐ろしいことです。
drrob

これは間違っています。Hibernateは永続化のためのJPAプロバイダとして使用されている場合、それはデフォルトコンストラクタが必要ですそれ以外の場合は、以下のすさまじいCaused by: org.hibernate.InstantiationException: No default constructor for entity: : hibernate.tutorial.Student最近ので、発生したjavax.persistence.*;利用されているとだけ org.hibernate作成するときSession, SessionFactory, and Configuration
マッシュ状

2
@Mushyこれは完全に正しいです。これは、a)質問が休止状態に関するもので、JPAが1つも言及されていないため、b)回答の2番目の文で、休止状態ではなくてもJPAがデフォルトのコンストラクタを必要とすることを明示的に述べているためです。
Mike Nakis 2017年

36

hibernateは、フィールドまたはプロパティアクセス戦略をサポートする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ドキュメントによると

クラスは、空の引数リストを持つ新しい式のようにインスタンス化されます

この話の教訓

Person.class.newInstance();

と類似しています

new Person();

他には何もない


1
これは、この質問に関して私が見つけた最も優れた説明です。私が見つけた答えのほとんどは本っぽい専門用語を使用しており、誰もそれをあなたのように柔軟な方法で説明していませんでした。あなたに感謝し、感謝します!
ダークナイト

1
これはHibernateチームの推論かもしれません。しかし実際には、(1)アノテーションを要求するか、コンストラクターが1つしかない場合はデフォルト以外のコンストラクターのみを使用し、(2)class.getDeclaredConstructorsを使用することで問題を解決できます。そして、Class.newInstance()の代わりにConstructor.newInstance()を使用します。Java 8以前は、XML /注釈での適切なマッピングが必要でしたが、完全に実行可能です。
drrob

わかりましたので、hibernateはデフォルトのコンストラクターからオブジェクトを作成し、フィールドのセッターnameage?そうでない場合は、後で別のコンストラクタを使用しますか?
tryHard

2
@tryingHardはい、インスタンス化されると、Hibernateはセッターまたはフィールドを使用します-アクセス戦略によって異なります。デフォルトでは、Idアノテーションの配置により、デフォルトのアクセス戦略が提供されます。docs.jboss.org/hibernate/orm/5.1/userguide/html_single/chapters/…を
Arthur Ronald

6

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

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

Hibernateが行う方法でオブジェクトを構築すると(0-argコンストラクターを呼び出し、おそらくReflectionを介してインスタンスのフィールドを直接変更する可能性があります。おそらく、setterを呼び出す方法を知っています)、オブジェクトがJava-適切なパラメーターを使用してコンストラクターを呼び出し、新しいオブジェクトが目的のオブジェクトになるようにします。オブジェクトをインスタンス化してから変更することは、多少「アンチJava」(または、アンチピュア理論的なJava)だと思います。そして、直接フィールド操作を介してこれを行うと、カプセル化と、そのすべての素晴らしいカプセル化に行き着きます。 。

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

そうは言っても、Hibernateのアプローチがあまり「クリーン」ではないことを考えると、0引数のコンストラクターを持つ義務は厳密には必要ありませんが、要件は多少理解できますが、純粋に「適切な方法」でそれを行ったと思います「理由は、その前に(妥当な理由ではあるが)「適切な方法」から逸脱したとき。


5

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


privateコンストラクターはどのような条件下で正しくありませんか?私が見ているjava.lang.InstantiationExceptionとさえprivate私のJPAエンティティのコンストラクタ。リファレンス
Kevin Meredith 14

空のコンストラクターなしで(ただし、argsコンストラクターを使用して)クラスを試しましたが、うまくいきました。hibernateから「INFO:HHH000182:クラスのデフォルト(引数なし)コンストラクターはなく、クラスはInterceptorによってインスタンス化する必要があります」という情報を取得しましたが、例外はなく、オブジェクトはDBから正常に受信されました。
lijepダム2017

2

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

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


リッチドメインオブジェクトにする必要がある可能性のあるものに完全な可変性を強制することは、まだより脆弱であると主張します(エンティティが機能しないデータのバッグである必要がある場合は、ORMとは言えません-欲しいからここにいますコンストラクタですが、代わりに豊富なセッター呼び出しの未定義の順序があります)...しかし、+ 1は、argsを使用してコンストラクタでリフレクションを実行できることを認識しているためです:)
drrob

2

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

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

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

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


0

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

静的内部クラスは、概念的には、.javaファイルで宣言された通常の一般クラスと同じです。

HibernateはProjectインスタンスに依存せずにProjectPKをインスタンス化する必要があるため、ProjectPKは静的内部クラスであるか、独自の.javaファイルで宣言される必要があります。

リファレンスorg.hibernate.InstantiationException:デフォルトのコンストラクターなし

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.