Key Entity:
@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;
}
}
Value Entity:
@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;
}
}
Container Entity:
@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());
If I add several key-value pairs to a ContainerEntity
and then reload said container, only one key-value pair will be present. If you look at the output of running the above main function, first '2' is printed, followed by '1'.
I can see that this is because of KeyEntity.hashCode
- when inserting into the HashMap
KeyEntity.handle
is null
, so all pairs will have the same hash code. KeyEntity.id
is populated at this point - if I base the hash code off of this field everything works out. Also if I change the key to be a String
it is loaded in time for the calls to hashCode
.
How can I change my mappings in ContainerEntity
so that KeyEntity.handle
is loaded when it is placed inside the map, so hashCode
can use it?
See here and here:
... As Mike points out, @MapKey was only intended by the spec for the case Map<Basic, Entity>, not for Map<Basic, Embeddable>. The correct annotation for embeddable values would be.
I.E, it is intended that the key should be a simple basic type (which means not an entity or embeddable) and the value an entity. If the value is a basic type, @ElementCollection
would do.
However, what you need is that the key is an entity type, and in this case, you are out of luck.
As you already said, if you change the the key to String
, everything is fine, so I will suggest you to do this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With