Карта JPA с ключом сущности и значением сущности - PullRequest
6 голосов
/ 05 января 2012

Ключевой объект:

@Entity public class KeyEntity
{
    @Id @GeneratedValue(strategy = GenerationType.TABLE)
    public Long id;

    public String handle;

    public boolean equals(Object o) {
        KeyEntity oke = (KeyEntity) o;
        return handle != null ? handle.equals(oke.handle) : oke.handle == null;
    }

    public int hashCode() {
        return handle != null ? handle.hashCode() : 0;
    }
}

Значение сущности:

@Entity public class ValueEntity
{
    @Id @GeneratedValue(strategy = GenerationType.TABLE)
    public Long id;

    @ManyToOne
    public KeyEntity key;

    public String value;

    public boolean equals(Object o) {
        ValueEntity ove = (ValueEntity) o;
        return key != null ? key.equals(ove.key) : ove.key == null;
    }

    public int hashCode() {
        return key != null ? key.hashCode() : 0;
    }
}

Контейнерный объект:

@Entity public class ContainerEntity
{
    @Id @GeneratedValue(strategy = GenerationType.TABLE)
    public Long id;

    @OneToMany @MapKey(name = "key")
    public Map<KeyEntity, ValueEntity> values = new HashMap<KeyEntity, ValueEntity>();
}

Main:

KeyEntity k1 = new KeyEntity();
k1.handle = "k1";
em.persist(k1);

KeyEntity k2 = new KeyEntity();
k2.handle = "k2";
em.persist(k2);

ValueEntity v1 = new ValueEntity();
v1.key = k1;
v1.value = "v1";
em.persist(v1);

ValueEntity v2 = new ValueEntity();
v2.key = k2;
v2.value = "v2";
em.persist(v2);

ContainerEntity ce = new ContainerEntity();
ce.values.put(k1, v1);
ce.values.put(k2, v2);
em.persist(ce);

// display number of values
System.out.println(ce.values.size());

// create new transaction
em.getTransaction().commit();
em.close();
em = emf.createEntityManager();
em.getTransaction().begin();

// find our container and inspect the number of values
ce = em.find(ContainerEntity.class, ce.id);
System.out.println(ce.values.size());

Если я добавлю несколько пар ключ-значение в ContainerEntity и затем перезагрузлю указанный контейнер, будет присутствовать только одна пара ключ-значение. Если вы посмотрите на результат выполнения вышеуказанной основной функции, сначала печатается «2», а затем «1».

Я вижу, что это из-за KeyEntity.hashCode - при вставке в HashMap KeyEntity.handle это null, поэтому все пары будут иметь одинаковый хэш-код. KeyEntity.id заполняется на этом этапе - если я основываю хэш-код из этого поля, все работает. Также, если я изменю ключ на String, он будет загружен вовремя для вызовов на hashCode.

Как я могу изменить свои отображения в ContainerEntity, чтобы KeyEntity.handle загружался, когда он помещался внутри карты, чтобы hashCode мог использовать его?

1 Ответ

3 голосов
/ 05 января 2012

См. здесь и здесь :

... Как отмечает Майк, @MapKey был предназначен только для спецификации для случая Mapне для карты,Правильная аннотация для встраиваемых значений будет.

IE, предполагается, что ключ должен быть простым базовым типом (что означает не сущность или встраиваемый объект), а значение - сущностью.Если значение является базовым типом, подойдет @ElementCollection.

Однако вам нужно, чтобы ключ был типом сущности, и в этом случае вам не повезло.

Как вы уже сказали, если вы измените ключ на String, все в порядке, поэтому я предлагаю вам сделать это.

...