2

次の例を考えます (部門 - プロジェクト):

部門には次のプロパティがあります (複合主キー)。

@Entity
@IdClass(DeptId.class)
public class Department
{
    @Id
    @Column(name="number")
    private Integer number;

    @Id
    @Column(name="country")
    private String country;

    @Column(name="name")
    private String name;

    @OneToMany(mappedBy="dept")
    private Collection<Project> projects;

    ...
}

ここで PK クラス:

public class DeptId implements Serializable
{
    private Integer number;
    private String country;

    ...
}

プロジェクトと部門の間の関係は多対 1 です。つまり、部門は多くのプロジェクトを持つことができます。Project クラス自体は、Department の複合キーを参照する複合キーを使用しています。重要な注意: @EmbeddedId ではなく @IdClass を使用した実装のみです。

次に、(問題のある) JPA 1.0 @IdClass 実装は次のようになります (冗長な deptNum および deptCtry プロパティ): -> 部門内の一意の名前です。

@Entity 
@IdClass(ProjectId.class)
public class Project
{
    @Id
    @Column(name="dept_number")
    private Integer deptNumber;

    @Id
    @Column(name="dept_country")
    private String deptCountry;

    @Id
    @Column(name="name")
    private String name;

    @ManyToOne 
    @JoinColumns({
       @JoinColumn(name="dept_number", referencedColumnName="number"),
       @JoinColumn(name="dept_country", referencedColumnName="country")
    })    
    private Department dept;

    ...
}

ProjectId は次のとおりです。

public class ProjectId implements Serializable
{
    private String name;
    private DeptId dept;

    ...
}

これに関する問題は、Hibernate も EclipseLink も、Project の 2 つの冗長なプロパティ deptNum と deptCtry を DeptId の dept プロパティ (またはその中のプロパティ) にマップする方法を認識していないことです。-> MappingException など

私の質問は:

これは JPA 1.0 の制限であり、@IdClass 実装を持つ他の複合キーを参照する複合キーを持つテーブルは一般に機能しません。JPA 実装はこれらのフィールドをマップする方法を単に知らないためですか?

回避策として、これらのクラスに @EmbeddedId を使用するか、JPA 2.0 構文を使用して @XToX 関連付けに @Id のアノテーションを付ける必要があります。これに関する私の見解が正しいことを確認したいだけです。

ありがとう

4

3 に答える 3

4

はい、これは JPA 1.0 の制限であり、JPA 2.0 で修正されました。新しい JPA 2.0 では、ID アノテーションを dept リレーションシップに配置して、重複する deptCountry および deptNumber 属性を完全に回避できます。キー クラスはネストを使用します。JPA 1.0 では、ID の一部としてマークできるのは基本的なマッピングのみであり、冗長なマッピングと、永続化するときに値/関係が正しくキャッシュに入れられるようにするためのコードが必要です。冗長性のため、他の回答で述べたように、フィールドのマッピングの 1 つを挿入可能/更新可能 = false を介して読み取り専用としてマークする必要があります。ただし、そうすると、値がキャッシュにマージされないことを意味します。そのため、オブジェクトがデータベースから更新されない限り、変更 (オブジェクト ID が存在すると変更できないため、挿入時など) は反映されません。JoinColumns を読み取り専用としてマークした場合、プロジェクトを永続化する場合は、参照先の dept から値を取得し、対応する基本 ID 属性に手動で配置する必要があります。ただし、基本属性を読み取り専用としてマークすることもできます。いずれにせよ、Eclipselink には問題がなく、関連する dept エンティティを使用してフィールド値を正しく設定します (プロジェクトで persist が呼び出される前に設定されている限り)。ただし、別のコンテキストでプロジェクトを読み返すと、基本属性が入力される場合と入力されない場合があることに注意してください。これは、エンティティがデータベースから更新されるかどうかによって異なります。それらが読み取り専用である場合、それらは共有キャッシュにマージされません。これは、読み取り専用であるため変更されていないためです。したがって、それらは単に無視することができます。または、データを入力する必要がある場合は、

この同じモデルは、JPA2.0 @MapsId を使用して再利用できます。これにより、リレーションシップの値を使用して基本的なマッピングも維持されます。私が見る唯一の利点は、外部キー/IDフィールド値を取得するために関係にアクセスする必要がないことです(遅延関係で不要な結合またはデータベースアクセスを引き起こす可能性があります)。

ZipArea EclipseLink の例外については、ZipAreaId がフラット化される代わりに ZipId zip 属性を持つことが原因です。JPA 1.0 では、キー クラスがエンティティの各 @ID 属性に対して同じタイプと名前の属性を持つ必要があります。

于 2010-11-03T15:12:08.630 に答える
2

投稿されたコードの問題は、JPA 1.0 が実際には複合主キー クラスのネストを許可していないことです。この ProjectId は無効です:

public class ProjectId implements Serializable
{
    private String name;
    private DeptId dept;

    ...
}

DeptId は、次のように平坦化する必要があります。

public class ProjectId implements Serializable
{
    private Integer deptNumber;
    private String deptCountry;
    private String name;

    ...
}

EclipseLink のバージョンを入手したばかりですが、Hibernate には問題があります。JPA 1.0が想定されていることをHibernateにどのように伝えるのだろうか。

于 2010-11-03T09:57:45.010 に答える
2

これに関する問題は、Hibernate も EclipseLink も、Project の 2 つの冗長なプロパティ deptNum と deptCtry を DeptId の dept プロパティにマップする方法を知らないことです。

これが、この種のマッピングでManyToOne外部キーを読み取り専用として定義する必要がある理由です。これは、JoinColumn属性insertableupdatableを に設定することによって行われますfalse

したがって、次のことを試してください。

@Entity 
@IdClass(ProjectId.class)
public class Project
{
    @Id
    @Column(name="dept_number")
    private Integer deptNumber;

    @Id
    @Column(name="dept_country")
    private String deptCountry;

    @Id
    @Column(name="name")
    private String name;

    @ManyToOne 
    @JoinColumns({
       @JoinColumn(name="dept_number", referencedColumnName="number", insertable=false, updatable=false),
       @JoinColumn(name="dept_country", referencedColumnName="country", insertable=false, updatable=false)
    })    
    private Department dept;
    ...
}
于 2010-10-31T11:26:45.907 に答える