この質問は Hibernate Annotation Placement Question に多少関連しています。
しかし、私はどちらがbetterか知りたいですか?プロパティ経由でアクセスするか、フィールド経由でアクセスしますか?それぞれの長所と短所は何ですか?
必要なときにいつでもアクセサにビジネスロジックを追加できるので、アクセサが好きです。以下に例を示します。
@Entity
public class Person {
@Column("nickName")
public String getNickName(){
if(this.name != null) return generateFunnyNick(this.name);
else return "John Doe";
}
}
また、別のライブラリをミックスに追加すると(JSON変換ライブラリ、BeanMapper、Dozer、またはgetter/setterプロパティに基づいた他のBeanマッピング/クローンライブラリ)、ライブラリが永続性と同期していることが保証されます。マネージャー(両方ともゲッター/セッターを使用)。
両方に引数がありますが、それらのほとんどは、「ロジックを追加する必要がある場合」または「xxxx breaks encapsulation」という特定のユーザー要件に由来します。しかし、誰も理論について本当にコメントしておらず、適切に論理的な議論をしている。
Hibernate/JPAがオブジェクトを永続化するときに実際に行っていること-まあ、オブジェクトのSTATEを永続化しています。つまり、簡単に再現できる方法で保存することを意味します。
カプセル化とは何ですか?カプセル化とは、アプリケーション/クライアントがデータに安全にアクセスするために使用できるインターフェースでデータ(または状態)をカプセル化することを意味し、一貫性と有効性を保ちます。
これはMS Wordのように考えてください。 MS Wordは、メモリ内のドキュメントのモデル(ドキュメントSTATE)を保持します。ユーザーがドキュメントを変更するために使用できるインターフェイスを提供します-ボタン、ツール、キーボードコマンドなどのセット。ただし、そのドキュメントを永続化(保存)することを選択すると、キープレスのセットではなく内部状態を保存します。生成に使用されるマウスクリック。
オブジェクトの内部状態を保存しても、カプセル化は破壊されません。そうしないと、カプセル化の意味とカプセル化の理由がわかりません。まさにオブジェクトのシリアル化のようなものです。
このため、ほとんどの場合、ACCESSORSではなくFIELDSを永続化することが適切です。これは、オブジェクトが保存されたとおりにデータベースからオブジェクトを正確に再作成できることを意味します。検証は必要ありません。これは、作成時に元のデータベースで行われたため、データベースに格納される前に行われたためです(ただし、神に禁じられていない限り、無効なデータをDBに格納しています!!!!)。同様に、値はオブジェクトが保存される前にすでに計算されているため、値を計算する必要はありません。オブジェクトは、保存される前の状態に見えるはずです。実際、ゲッター/セッターに追加のものを追加することにより、実際には増加オリジナルの正確なコピーではないものを再作成するリスクがあります。
もちろん、この機能は理由のために追加されました。アクセサーを永続化するための有効なユースケースが存在する場合がありますが、通常はまれです。例として、計算された値の永続化を避けたい場合がありますが、値のゲッターでオンデマンドで計算しない理由を質問したり、ゲッターで遅延的に初期化したりすることができます。個人的には、良いユースケースを考えることはできません。また、ここでの答えはどれも「ソフトウェアエンジニアリング」の答えを本当に与えません。
私はフィールドアクセスを好みます。その方法で、各プロパティにゲッター/セッターを提供することを強制されないからです。
Googleによる簡単な調査では、フィールドアクセスが大半であることが示唆されています(例: http://Java.dzone.com/tips/12-feb-jpa-20-why-accesstype )。
フィールドアクセスは、Springが推奨するイディオムだと思いますが、それをバックアップするためのリファレンスが見つかりません。
関連SO質問 があり、パフォーマンスを測定しようとし、「違いはない」という結論に達しました。
プロパティアクセサを使用する必要がある状況を次に示します。 8つの具象サブクラスに継承するための実装の良さを備えたGENERIC抽象クラスがあるとします。
public abstract class Foo<T extends Bar> {
T oneThing;
T anotherThing;
// getters and setters ommited for brevity
// Lots and lots of implementation regarding oneThing and anotherThing here
}
さて、このクラスに正確にどのように注釈を付けるべきでしょうか?答えは、この時点ではターゲットエンティティを指定できないため、フィールドアクセスまたはプロパティアクセスで注釈を付けることはできません。具体的な実装に注釈を付ける必要があります。ただし、永続プロパティはこのスーパークラスで宣言されているため、サブクラスではプロパティアクセスを使用する必要があります。
フィールドアクセスは、抽象ジェネリックスーパークラスを持つアプリケーションのオプションではありません。
私は、プロパティアクセッサを好み、使用する傾向があります。
foo.getId()
を呼び出すことができます(Hibernateを使用する場合、 HHH-3718 get解決済み)。欠点:
@Transient
があるかどうかを確認する必要があります。それは本当に特定のケースに依存します-両方のオプションが理由で利用可能です。 IMOは3つのケースに要約されます。
セッターで値を設定するだけでなく、暗号化や計算などを行う場合は、ゲッターでのアクセスではなくフィールドアクセス(プロパティアクセス)を強くお勧めします。
プロパティへのアクセスの問題は、オブジェクトがロードされるときにセッターも呼び出されることです。暗号化を導入するまで、これは私にとって長年うまくいきました。ユースケースでは、セッター内のフィールドを暗号化し、ゲッター内で復号化することを望みました。現在、プロパティアクセスの問題は、Hibernateがオブジェクトをロードしたときに、セッターを呼び出してフィールドにデータを取り込むため、暗号化された値を再度暗号化することでした。この投稿では、これにも言及しています。 Java Hibernate:呼び出し元に応じて異なるプロパティセット関数の動作
これは、フィールドアクセスとプロパティアクセスの違いを思い出すまで頭痛の種でした。これで、すべての注釈をプロパティアクセスからフィールドアクセスに移動し、正常に機能するようになりました。
次の理由から、フィールドアクセスを使用することを好みます。
プロパティアクセスは、equals/hashCodeおよび フィールドを直接参照する (非常に厄介なバグを引き起こす可能性がありますそのゲッターを介して反対)。これは、ゲッターにアクセスしたときにのみプロキシが初期化され、直接フィールドアクセスでは単にnullが返されるためです。
プロパティアクセスでは、すべての ユーティリティメソッド (addChild/removeChildなど)に@Transient
。
フィールドアクセスでは、ゲッターをまったく公開しないことで@Versionフィールドを非表示にできます。ゲッターはセッターの追加にもつながる可能性があり、version
フィールドを手動で設定しないでください(非常に厄介な問題につながる可能性があります)。すべてのバージョンの増分は、 OPTIMISTIC_FORCE_INCREMENT または PESSIMISTIC_FORCE_INCREMENT 明示的なロックを介してトリガーする必要があります。
フィールドを更新するとカプセル化が直接壊れるので、ORMがそれを行ったとしても、プロパティに注釈を付ける方が良いと思います。
これがどこにあなたを燃やすのかの素晴らしい例です:あなたはおそらく同じ場所(フィールドまたはプロパティのいずれか)でハイバネートバリデータと永続性のための注釈が欲しいでしょう。フィールドに注釈が付けられたHibernateバリデーターによる検証をテストする場合、エンティティのモックを使用して、単体テストをバリデーターだけに分離することはできません。痛い。
プロパティアクセスとフィールドアクセスは、遅延初期化に関して微妙に異なると思います。
2つの基本Beanについて次のマッピングを検討してください。
<hibernate-mapping package="org.nkl.model" default-access="field">
<class name="FieldBean" table="FIELD_BEAN">
<id name="id">
<generator class="sequence" />
</id>
<property name="message" />
</class>
</hibernate-mapping>
<hibernate-mapping package="org.nkl.model" default-access="property">
<class name="PropBean" table="PROP_BEAN">
<id name="id">
<generator class="sequence" />
</id>
<property name="message" />
</class>
</hibernate-mapping>
そして、次の単体テスト:
@Test
public void testFieldBean() {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
FieldBean fb = new FieldBean("field");
Long id = (Long) session.save(fb);
tx.commit();
session.close();
session = sessionFactory.openSession();
tx = session.beginTransaction();
fb = (FieldBean) session.load(FieldBean.class, id);
System.out.println(fb.getId());
tx.commit();
session.close();
}
@Test
public void testPropBean() {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
PropBean pb = new PropBean("prop");
Long id = (Long) session.save(pb);
tx.commit();
session.close();
session = sessionFactory.openSession();
tx = session.beginTransaction();
pb = (PropBean) session.load(PropBean.class, id);
System.out.println(pb.getId());
tx.commit();
session.close();
}
必要な選択の微妙な違いが表示されます。
Hibernate:
call next value for hibernate_sequence
Hibernate:
insert
into
FIELD_BEAN
(message, id)
values
(?, ?)
Hibernate:
select
fieldbean0_.id as id1_0_,
fieldbean0_.message as message1_0_
from
FIELD_BEAN fieldbean0_
where
fieldbean0_.id=?
0
Hibernate:
call next value for hibernate_sequence
Hibernate:
insert
into
PROP_BEAN
(message, id)
values
(?, ?)
1
つまり、fb.getId()
を呼び出すにはselectが必要ですが、pb.getId()
は必要ありません。
私はフィールドアクセッサを好みます。コードはずっときれいです。すべての注釈はクラスの1つのセクションに配置でき、コードははるかに読みやすくなります。
プロパティアクセサーに別の問題が見つかりました:クラスにgetXYZメソッドがあり、永続プロパティに関連付けられていると注釈されていない場合、hibernateはこれらのプロパティを取得しようとするsqlを生成し、非常に紛らわしいエラーメッセージを生成します。 2時間無駄になりました。私はこのコードを書きませんでした。私はこれまでフィールドアクセッサを使用してきましたが、この問題に遭遇したことはありません。
このアプリで使用されるHibernateバージョン:
<!-- hibernate -->
<hibernate-core.version>3.3.2.GA</hibernate-core.version>
<hibernate-annotations.version>3.4.0.GA</hibernate-annotations.version>
<hibernate-commons-annotations.version>3.1.0.GA</hibernate-commons-annotations.version>
<hibernate-entitymanager.version>3.4.0.GA</hibernate-entitymanager.version>
それは古いプレゼンテーションですが、ロッドは、プロパティアクセスに関する注釈が貧弱なドメインモデルを奨励し、注釈を付けるための「デフォルト」の方法であってはならないと示唆しています。
フィールドベースのアクセスを選択する最も重要な理由を要約してみましょう。さらに深く掘り下げたい場合は、私のブログでこの記事をお読みください: JPAとHibernateのアクセス戦略–フィールドまたはプロパティアクセスのどちらが良いですか?
フィールドベースのアクセスは、はるかに優れたオプションです。理由は次の5つです。
理由1:コードの読みやすさの向上
フィールドベースのアクセスを使用する場合、エンティティの属性にマッピングアノテーションを付けます。すべてのエンティティ属性の定義をクラスの最上部に配置すると、すべての属性とそれらのマッピングの比較的コンパクトなビューが得られます。
理由2:アプリケーションで呼び出されるべきではないgetterまたはsetterメソッドを省略します
フィールドベースのアクセスのもう1つの利点は、永続化プロバイダー(HibernateやEclipseLinkなど)がエンティティ属性の取得メソッドと設定メソッドを使用しないことです。つまり、ビジネスコードで使用してはならない方法を提供する必要はありません。これは、ほとんどの場合、 生成された主キー属性 またはバージョン列のsetterメソッドの場合です。永続性プロバイダーはこれらの属性の値を管理するため、プログラムで設定しないでください。
理由3:ゲッターおよびセッターメソッドの柔軟な実装
永続化プロバイダーはgetterおよびsetterメソッドを呼び出さないため、外部の要件を満たす必要はありません。これらのメソッドは任意の方法で実装できます。これにより、ビジネス固有の検証ルールを実装したり、追加のビジネスロジックをトリガーしたり、エンティティ属性を別のデータ型に変換したりできます。
たとえば、これを使用して、オプションの関連付けまたは属性をJava Optional
に ラップできます
理由4:ユーティリティメソッドを@Transient
としてマークする必要がない
フィールドベースのアクセス戦略のもう1つの利点は、ユーティリティメソッドに@Transient
の注釈を付ける必要がないことです。この注釈は、メソッドまたは属性がエンティティの永続状態の一部ではないことを永続プロバイダーに伝えます。また、フィールドタイプアクセスでは、永続状態がエンティティの属性によって定義されるため、JPA実装はエンティティのすべてのメソッドを無視します。
理由5:プロキシを使用する場合のバグを回避する
Hibernateは lazyly fetched to-one associations のプロキシを使用して、これらの関連付けの初期化を制御できるようにします。このアプローチは、ほとんどすべての状況でうまく機能します。ただし、プロパティベースのアクセスを使用すると、危険な落とし穴が生じます。
プロパティベースのアクセスを使用する場合、Hibernateはgetterメソッドを呼び出すときにプロキシオブジェクトの属性を初期化します。ビジネスコードでプロキシオブジェクトを使用する場合は常にそうです。しかし、かなり多くの equalsとhashCode実装 が属性に直接アクセスします。プロキシ属性に初めてアクセスする場合、これらの属性はまだ初期化されていません。
私はフィールドを好みますが、ゲッターに注釈を付けることを余儀なくされるような状況に遭遇しました。
Hibernate JPA実装では、@Embedded
はフィールドで機能しないようです。それで、それはゲッターに行かなければなりません。そして、それをゲッターに配置すると、さまざまな@Column
アノテーションもゲッターに配置する必要があります。 (ここでは、Hibernateがフィールドとゲッターを混在させたくないと考えています。)そして、@Column
を1つのクラスのゲッターに配置したら、おそらくそれを全体で行うのが理にかなっています。
デフォルトでは、JPAプロバイダーはエンティティフィールドの値にアクセスし、エンティティのJavaBeanプロパティアクセサー(ゲッター)およびミューテーター(セッター)メソッドを使用して、それらのフィールドをデータベース列にマップします。そのため、エンティティのプライベートフィールドの名前とタイプはJPAにとって重要ではありません。代わりに、JPAはJavaBeanプロパティアクセサーの名前と戻り値の型のみを調べます。 @javax.persistence.Access
アノテーションを使用してこれを変更できます。これにより、JPAプロバイダーが採用するアクセス方法を明示的に指定できます。
@Entity
@Access(AccessType.FIELD)
public class SomeEntity implements Serializable
{
...
}
AccessType列挙に使用できるオプションは、PROPERTY(デフォルト)およびFIELDです。 PROPERTYを使用すると、プロバイダーはJavaBeanプロパティメソッドを使用してフィールド値を取得および設定します。 FIELDは、プロバイダーがインスタンスフィールドを使用してフィールド値を取得および設定できるようにします。ベストプラクティスとして、特に強いる理由がない限り、デフォルトのままにしてJavaBeanプロパティを使用する必要があります。
これらのプロパティアノテーションは、プライベートフィールドまたはパブリックアクセサメソッドのいずれかに配置できます。 AccessType.PROPERTY
(デフォルト)を使用し、JavaBeanアクセサーの代わりにプライベートフィールドに注釈を付ける場合、フィールド名はJavaBeanプロパティ名と一致する必要があります。ただし、JavaBeanアクセサーに注釈を付ける場合、名前は一致する必要はありません。同様に、AccessType.FIELD
を使用し、フィールドの代わりにJavaBeanアクセサーに注釈を付ける場合、フィールド名もJavaBeanプロパティ名と一致する必要があります。この場合、フィールドに注釈を付ける場合、一致する必要はありません。一貫性を保ち、AccessType.PROPERTY
のJavaBeanアクセサーとAccessType.FIELD
のフィールドに注釈を付けることをお勧めします。
JPAプロパティアノテーションとJPAフィールドアノテーションを同じエンティティに混在させないでください。これを行うと、不特定の動作が発生し、エラーが発生する可能性が非常に高くなります。
フィールドアクセスを支持するもう1つのポイントは、そうしないとコレクションのセッターを公開することを余儀なくされることです。永続コレクションインスタンスをHibernateによって管理されていないオブジェクトに変更すると、データの一貫性が確実に失われるため、私にとっては悪い考えです。
そのため、デフォルトのコンストラクターで空の実装に初期化され、それらのゲッターのみを公開する保護フィールドとしてコレクションを持つことを好みます。その場合、clear()
、remove()
、removeAll()
などのような、Hibernateが変更を認識しないことのない管理された操作のみが可能です。
プロパティ経由のアクセスではなく、フィールド経由のアクセスを選択する必要があります。フィールドを使用すると、送受信されるデータを制限できます。 viaプロパティを使用すると、より多くのデータをホストとして送信し、G単位を設定することができます(ファクトリは、全体でほとんどのプロパティを設定します)。
ここで遅延初期化とフィールドアクセスを解決しました 1対1の休止状態:getId()オブジェクト全体を取得せずに
Hibernateのaccesstypeについても同じ質問があり、 いくつかの回答 が見つかりました。
エンティティBeanを作成し、ゲッターアノテーションを使用しました。私たちが遭遇した問題はこれです:いくつかのエンティティは、いつ更新できるかに関するいくつかのプロパティに対して複雑なルールを持っています。解決策は、各セッターに実際の値が変更されたかどうか、変更された場合は変更を許可するかどうかを決定するビジネスロジックを用意することでした。もちろん、Hibernateはいつでもプロパティを設定できるため、最終的に2つのセッターグループになりました。かなりい。
以前の投稿を読んで、エンティティ内からプロパティを参照すると、コレクションが読み込まれないという問題が発生する可能性があることもわかりました。
一番下の行、私は将来的にフィールドに注釈を付けることに傾くでしょう。
これについて考えて、メソッドアクセサを選択します
どうして?
フィールドとmethosアクセサーは同じであるが、後でロードフィールドに何らかのロジックが必要になった場合、フィールドに配置されたすべての注釈を移動して保存するため
よろしく
グラブハート
通常、BeanはPOJOであるため、とにかくアクセサーがあります。
したがって、質問は「どちらが良いですか?」ではなく、「いつフィールドアクセスを使用するか?」です。そして、答えは「フィールドにセッター/ゲッターが必要ないとき!」です。
クラスをきれいにするには、フィールドに注釈を入れてから@Access(AccessType.PROPERTY)を使用します
両方 :
EJB3仕様では、アクセスする要素タイプ、つまりプロパティアクセスを使用する場合はgetterメソッド、フィールドアクセスを使用する場合はフィールドにアノテーションを宣言する必要があります。
https://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#entity-mapping
AccessType.PROPERTY:EJB永続化実装は、JavaBean「setter」メソッドを介してクラスに状態をロードし、JavaBean「getter」メソッドを使用してクラスから状態を取得します。これがデフォルトです。
AccessType.FIELD:状態はロードされ、クラスのフィールドから直接取得されます。 JavaBeanの「getter」および「setter」を記述する必要はありません。