2

マップをデータベースに保存することになっている次のコードがあります。問題は、エンティティとその関係を保存している間、マップのキー値をサイレントに破棄することです。エラーは発生せず、データベースは代わりにNULLを取得します。

コード:

package com.tester;

import java.util.HashMap;
import java.util.Map;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.MapKey;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import lombok.Data;
import lombok.ToString;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;

public class App {
    @Entity
    @Table (name = "test_items")
    @Data
    @ToString (exclude = "parent")
    public static class Item {
        @Id @GeneratedValue
        private Long id;

        int slot;

        @ManyToOne
        private Item parent;

        @OneToMany (cascade = CascadeType.ALL, mappedBy = "parent")
        @MapKey (name = "slot")
        Map<Integer, Item>  children;
    }

    private static SessionFactory buildSessionFactory() {
        try {
            return new AnnotationConfiguration().configure().buildSessionFactory();
        } catch (final Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static void main(final String[] args) {
        final SessionFactory sf = buildSessionFactory();
        final Session session = sf.openSession();
        session.beginTransaction();

        final Item item100 = new Item();
        final Item item110 = new Item();
        session.save(item100);
        session.save(item110);
        session.flush();
        item110.setParent(item100);
        final HashMap<Integer, Item> children = new HashMap<Integer, Item>();
        children.put(3, item110);
        item100.setChildren(children);
        session.saveOrUpdate(item100);
        session.saveOrUpdate(item110);

        session.getTransaction().commit();
        session.close();
    }
}

上記の後に予想されるデータベースの結果:

id  slot parent_id
1   0    NULL
2   3    1 [->]

実結果:

id  slot parent_id
1   0    NULL
2   0    1 [->]

ご覧のとおり、Hibernateがキーの列を作成した場合でも、キーにはNULLが入力され、からの情報「3」children.put(3, item110);はデータベースにまったく保持されません。それは完全に失われます。

アイデア?

注:私も試し@MapKeyClass (Integer.class)ましたが、成功しませんでした。

編集:問題の3は、他のものを参照することを意図したものではなく、他のフィールド/列への参照でもありません。これは任意の値であり、この特定のコンテキストでは、多数のスロットを持つコンテナ内のアイテムのインデックスを定義する必要があります。しかし、私が気にかけていることはすべて、それを「abcdefg」に置き換えることができます(もちろん、整数ではなく文字列を使用するようにマップを変更したと仮定します)。

4

1 に答える 1

3

JPAの(時には厳しい)ルールは、同じ関係を持つ同じエンティティを取得することを期待する場合、永続化の前にエンティティ間のすべての関係を適切に確立する必要があるということです。

ですから、自問してみてください。「3」はどこから来たのでしょうか。また、それは何を意味するのでしょうか。それはどこに持続しますか?

オプション:マップキーは永続化されたフィールドではありません

これは最も可能性が低いように思われますが、質問の更新はこれが事実であると述べています。エンティティを取得するときは、キーに何かが必要になります。それはただ何かを作るつもりはありません。

結論:この場合、マップを使用することはできません。List<Item>代わりに使用してください。まだいくつかのインデックスでアクセスできるはずです。

オプション:マップキーは永続化されたフィールドです

マップキーが永続化されたフィールドである場合は、次のように指定する必要があります。

@MapKey(name=<keyfieldname>)

idマップキーとして以外のフィールドを使用する:

あなたの状況に合わせて、新しいフィールドを導入します(現在の例ではオプションが不足しています!):

@Column
Integer slot;

マップ上で適切な@MapKeyを使用します。

@OneToMany (cascade = CascadeType.ALL, mappedBy = "parent")
@MapKey(name="slot")
Map<Long, Item> children = new HashMap<Integer, Item>();

そして最後にあなたのコードで:

..。

final Item parent = new Item();
final Item child = new Item();
child.setSlot(3);
child.setParent(parent);

final HashMap<Integer, Item> children = new HashMap<Integer, Item>();
children.put(child.getParentKey(), child);
parent.setChildren(children);

session.save(child);
session.save(parent);

idマップキーとしてを使用する:

IDを使用すると(タイプの権利を取得したら)、新しい問題がid発生します。親を作成する前に、子(自動的に生成される)を取得する方法です。JPAが与えられた場合EntityManager em

@OneToMany (cascade = CascadeType.ALL, mappedBy = "parent")
@MapKey(name="id")
Map<Long, Item> children = new HashMap<Integer, Item>();

...

final Item parent = new Item();

final Item child = new Item();
child.setParent(parent);
em.persist(child);
em.flush(); // Force child's 'id' to be generated & populated

final HashMap<Integer, Item> children = new HashMap<Integer, Item>();
children.put(child.getId(), child);
parent.setChildren(children);

em.persist(parent);

SessionこれはJPA/EclipseLinkで正常に機能しますが、Hibernateでメソッドsaveとを使用して機能すると想定することしかできませんflush

于 2013-03-09T19:17:17.050 に答える