web-dev-qa-db-ja.com

@OneToManyと複合主キー?

私はHibernateを注釈付きで使用しています(春)、順序付けられた多対1の関係を持つオブジェクトがあり、そのオブジェクトは複合主キーを持つ子オブジェクトであり、その1つのコンポーネントは外部キーです親オブジェクトのID。

構造は次のようになります。

+=============+                 +================+
| ParentObj   |                 | ObjectChild    |
+-------------+ 1          0..* +----------------+
| id (pk)     |-----------------| parentId       |
| ...         |                 | name           |
+=============+                 | pos            |
                                | ...            |
                                +================+

アノテーションのさまざまな組み合わせを試しましたが、どれもうまくいかないようです。これは私が思いついた最も近いものです:

@Entity
public class ParentObject {
    @Column(nullable=false, updatable=false)
    @Id @GeneratedValue(generator="...")
    private String id;

    @OneToMany(mappedBy="parent", fetch=FetchType.EAGER, cascade={CascadeType.ALL})
    @IndexColumn(name = "pos", base=0)
    private List<ObjectChild> attrs;

    ...
}

@Entity
public class ChildObject {
    @Embeddable
    public static class Pk implements Serializable {
        @Column(nullable=false, updatable=false)
        private String parentId;

        @Column(nullable=false, updatable=false)
        private String name;

        @Column(nullable=false, updatable=false)
        private int pos;

        @Override
        public String toString() {
            return new Formatter().format("%s.%s[%d]", parentId, name, pos).toString();
        }

        ...
    }

    @EmbeddedId
    private Pk pk;

    @ManyToOne
    @JoinColumn(name="parentId")
    private ParentObject parent;

    ...
}

私がこれに到達したのは、他のほとんどの試みで、さまざまな理由で休止状態を読み込むことさえできなかったエンティティが得られた長い実験の結果です。

更新:コメントをありがとう。私はいくつかの進歩を遂げました。私はいくつかの調整を行いましたが、近いと思います(上記のコードを更新しました)。ただし、現在は挿入に関する問題があります。親オブジェクトはうまく保存されているように見えますが、子オブジェクトは保存されていません。私が判断できたのは、休止状態が子オブジェクトの(複合)主キーのparentId部分を埋めていないということです。 m一意でないエラーを取得:

org.hibernate.NonUniqueObjectException:
   a different object with the same identifier value was already associated 
   with the session: [org.kpruden.ObjectChild#null.attr1[0]]

私は自分のコードにnameおよびpos属性を設定していますが、もちろん、まだ保存されていないため、親IDがわかりません。これを埋めるように冬眠を説得する方法についてのアイデアはありますか?

ありがとう!

29
Kris Pruden

多くの実験とフラストレーションの後、私は最終的に自分が望むことを正確にできないと判断しました。

最終的には、子オブジェクトに独自の合成キーを与え、Hibernateに管理させました。キーは他のデータとほぼ同じ大きさなので、理想的ではありませんが、機能します。

2
Kris Pruden

Manning book Java Persistence with Hibernate には、セクション7.2でこれを行う方法の概要を示す例があります。幸い、本を所有していない場合でも、JPAバージョンの Caveat Emptor サンプルプロジェクト(直接リンク here をダウンロードすると、このソースコードの例を見ることができます。 )およびauction.modelパッケージのクラスCategoryおよびCategorizedItemを調べます。

また、以下の主要な注釈も要約します。まだ行っていないかどうか教えてください。

ParentObject:

@Entity
public class ParentObject {
   @Id @GeneratedValue
   @Column(name = "parentId", nullable=false, updatable=false)
   private Long id;

   @OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
   @IndexColumn(name = "pos", base=0)
   private List<ChildObject> attrs;

   public Long getId () { return id; }
   public List<ChildObject> getAttrs () { return attrs; }
}

ChildObject:

@Entity
public class ChildObject {
   @Embeddable
   public static class Pk implements Serializable {
       @Column(name = "parentId", nullable=false, updatable=false)
       private Long objectId;

       @Column(nullable=false, updatable=false)
       private String name;

       @Column(nullable=false, updatable=false)
       private int pos;
       ...
   }

   @EmbeddedId
   private Pk id;

   @ManyToOne
   @JoinColumn(name="parentId", insertable = false, updatable = false)
   @org.hibernate.annotations.ForeignKey(name = "FK_CHILD_OBJECT_PARENTID")
   private ParentObject parent;

   public Pk getId () { return id; }
   public ParentObject getParent () { return parent; }
}
14
RTBarnard

親とparentIdを別々にマップするのではなく、ParentObject参照を_ChildObject.Pk_に組み込む必要があります。

(ゲッター、セッター、問題に関連しないHibernate属性、およびメンバーアクセスキーワードは省略)

_class ChildObject { 
    @Embeddable
    static class Pk {
        @ManyToOne...
        @JoinColumn(name="parentId")
        ParentObject parent;

        @Column...
        String name...
        ...
    }

    @EmbeddedId
    Pk id;
}
_

ParentObject@OneToMany(mappedBy="id.parent")を置くだけで機能します。

6

まず、ParentObjectで、"parent"に設定する必要があるmappedBy属性を「修正」します。また(ただし、これはタイプミスかもしれません)@Idアノテーションを追加します。

@Entity
public class ParentObject {
    @Id
    @GeneratedValue
    private String id;

    @OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
    @IndexColumn(name = "pos", base=0)
    private List<ObjectChild> attrs;

    // getters/setters
}

次に、ObjectChildで、name属性を複合キーのobjectIdに追加します。

@Entity
public class ObjectChild {
    @Embeddable
    public static class Pk implements Serializable {
        @Column(name = "parentId", nullable = false, updatable = false)
        private String objectId;

        @Column(nullable = false, updatable = false)
        private String name;

        @Column(nullable = false, updatable = false)
        private int pos;
    }

    @EmbeddedId
    private Pk pk;

    @ManyToOne
    @JoinColumn(name = "parentId", insertable = false, updatable = false)
    private ParentObject parent;

    // getters/setters

}

[〜#〜] and [〜#〜]また、このエンティティのマッピングでparentId列を繰り返しているため、insertable = false, updatable = false@JoinColumnを追加します。

これらの変更により、エンティティの永続化と読み取りは正常に機能しています(Derbyでテスト済み)。

3
Pascal Thivent

これに3日間を費やした後、私は解決策を見つけたと思いますが、正直なところ、私はそれが好きではなく、間違いなく改善することができます。しかし、それは機能し、問題を解決します。

これがエンティティコンストラクターですが、setterメソッドでも実行できます。また、Collectionオブジェクトを使用しましたが、Listと同じまたは類似している必要があります。

...
public ParentObject(Collection<ObjectChild> children) {
    Collection<ObjectChild> occ = new ArrayList<ObjectChild>();
    for(ObjectChild obj:children){
        obj.setParent(this);
        occ.add(obj);
    }
    this.attrs = occ;
}
...

基本的に、他の誰かが示唆したように、親を保存する前に、すべての子の親IDを手動で設定する必要があります(すべての子とともに)

0
Lorenzo

あなたはかなり近づいたようで、私は現在のシステムで同じことをしようとしています。代理キーから始めましたが、親のPKとリスト内のインデックスで構成される複合主キーを優先して、代理キーを削除したいと思います。

「外部」ジェネレーターを使用して、マスターテーブルからPKを共有する1対1の関係を取得できました。

@Entity
@GenericGenerator(
    name = "Parent",
    strategy = "foreign",
    parameters = { @Parameter(name = "property", value = "parent") }
)
public class ChildObject implements Serializable {
    @Id
    @GeneratedValue(generator = "Parent")
    @Column(name = "parent_id")
    private int parentId;

    @OneToOne(mappedBy = "childObject")
    private ParentObject parentObject;
    ...
}

@GenericGeneratorと@GeneratedValueを追加して、挿入時に親が新しく取得したPKを割り当てないHibernateの問題を解決できるかどうか疑問に思います。

0
David Harkness

親オブジェクトを保存した後、子オブジェクトの挿入が機能するように、子オブジェクトに明示的にparentIdを設定する必要があります。

0
gabor