14

JPA(プロバイダー:Hibernate)と連携するために一方向の1対1の関係を取得しようとすると非常に苦労しています。私の意見では、これはそれほど面倒なことではないはずですが、明らかにJPA/Hibernateはそれに同意しません;-)

問題は、変更できないレガシースキーマをマップする必要があることと、このスキーマが2つのエンティティ間で共有される主キーを使用することです。これは同時に1つのエンティティの外部キーです。

簡単なTestCaseを作成しました。

DBは次のようになります。

CREATE TABLE PARENT (PARENT_ID Number primary key, Message varchar2(50));

CREATE TABLE CHILD (CHILD_ID Number primary key, Message varchar2(50),
CONSTRAINT FK_PARENT_ID FOREIGN KEY (CHILD_ID )REFERENCES PARENT (PARENT_ID));

CREATE SEQUENCE SEQ_PK_PARENT START WITH 1 INCREMENT BY 1 ORDER;

親(= 1対1の所有側)は次のようになります。

@Entity
@Table(name = "PARENT")
public class Parent implements java.io.Serializable {       
    private Long parentId;
    private String message;
    private Child child;

    @Id
    @Column(name = "PARENT_ID", unique = true, nullable = false, precision = 22, scale = 0)
    @SequenceGenerator(name="pk_sequence", sequenceName="SEQ_PK_PARENT")
    @GeneratedValue(generator="pk_sequence", strategy=GenerationType.SEQUENCE)
    public Long getParentId() {
        return this.parentId;
    }

    public void setParentId(Long parentId) {
        this.parentId = parentId;
    }

    @Column(name = "MESSAGE", length = 50)
    public String getMessage() {
        return this.message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    @OneToOne (cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn(name="PARENT_ID", referencedColumnName="CHILD_ID")
    public Child getTestOneToOneChild() {
        return this.child;
    }

    public void setTestOneToOneChild(Child child) {
        this.child = child;
    }
}

子供:

@Entity
@Table(name = "TEST_ONE_TO_ONE_CHILD", schema = "EXTUSER")
public class Child implements java.io.Serializable {    
    private static final long serialVersionUID = 1L;
    private Long childId;       

    private String message;

    public Child() {
    }

    public Child(String message) {
        this.message = message;
    }

    @Id
    @Column(name = "CHILD_ID")    
    public Long getChildId() {
        return this.childId;
    }

    public void setChildId(Long childId) {
        this.childId = childId;
    }

    @Column(name = "MESSAGE", length = 50)
    public String getMessage() {
        return this.message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

JPAが子のIDを割り当てる方法を知らないという問題を完全に理解しています。ただし、Hibernatesの「外部」キージェネレーターを使用しようとしましたが、これも成功しませんでした。これは、子から親への逆参照が必要であり、望ましくないためです。この問題は私にはそれほど珍しいことではないようですが、ここで何が欠けていますか?解決策はありますか?純粋なJPAで解決策が提供されない場合は、Hibernate拡張機能を使用することもできます。

正しい振る舞いに対する私の期待は次のとおりです。子供を付けたまま親を維持しようとすると、次のようになります。

  1. シーケンスからIDを取得し、親に設定します
  2. 親を永続化する
  3. 子に親のIDを設定する
  4. 子を持続させる

「スタンドアロン」の子(entityManager.persist(aChild)など)を永続化しようとすると、RuntimeExceptionが発生します。

どんな助けでも大歓迎です!

4

3 に答える 3

5

説明した db スキーマの場合、次のように、依存クラス (子クラス) で@MapsIdアノテーションを使用して、親へのマッピングを実現できます。

@Entity
class Parent {
  @Id
  @Column(name = "parent_id")
  @GeneratedValue 
  Long parent_id;
}

@Entity
class Child {
  @Id
  @Column(name = "child_id")
  Long child_id;

  @MapsId 
  @OneToOne
  @JoinColumn(name = "child_id")
  Parent parent;
}

親から子へのマッピングを追加すると、リストしたように @PrimaryKeyJoinColumn アノテーションを使用して、完全な双方向の 1 対 1 マッピングを次のようにします。

@Entity
class Parent {
  @Id
  @Column(name = "parent_id")
  @GeneratedValue 
  Long parent_id;

  @OneToOne
  @PrimaryKeyJoinColumn(name="parent_id", referencedColumnName="child_id")
  public Child;
}

@Entity
class Child {
  @Id
  @Column(name = "child_id")
  Long child_id;

  @MapsId 
  @OneToOne
  @JoinColumn(name = "child_id")
  Parent parent;
}

メソッド アクセスではなくフィールドを使用しましたが (関係に関係のないものはすべて削除しました)、ゲッターに適用されるのと同じ注釈になります。

@MapsIdの別の例については、セクション 2.2.3.1 の最後の部分も参照してください。

于 2012-09-07T00:27:12.367 に答える
3

望ましくない子から親への後方参照が必要なため

親が存在する場合にのみ子が存在できる場合、それらの間に関係があります。オブジェクト指向で表現したくないだけかもしれませんが、リレーショナル モデルには存在します。

とはいえ、これに対する自然な解決策は、子の中に親を持つことだと思います。

しかし、本当にそうしたくない場合は、ID を PK クラスとしてマッピングし、@EmbeddedId を使用してそれらを両方のクラスで共有することをお勧めします。1つの例外を除いて、それがあなたの問題を解決すると確信しています:

「スタンドアロン」の子 (entityManager.persist(aChild) など) を永続化しようとすると、RuntimeException が発生することが予想されます。

PK クラスで @EmbeddedId アプローチを使用する場合は、上記のケースを「ビジネス ルール」として処理する必要があると思います。

于 2011-01-21T13:45:37.427 に答える