I wrestled with JPA again today, and came away with some hard earned knowledge, so I want to put this one up on the interwebs.
I am working with a Database that was created by, effectively, another product. It has a relationship of Contact -<> ContactItem where contact item has a PK of [ContactID, TypeID]. What I really wanted was for Contact to have a map on it of ContactItems index by the Integer typeID.
First off, Composite keys are a PITA in JPA anyway, and doing this with a Map collection type is even worse. So lets go over the important parts:
First, ContactItem. Here you need to use an @EmbeddedId with your two key fields. To get the select to work properly, though, you still need to @ManyToOne field. What you need to do is create the dup fields and make them read only on the parent object:
class ContactItem {
@ManyToOne
@JoinColumn(name = "contact", insertable=false, updatable=false) //NOTE THIS!
private Contact contact;
@EmbeddedId
PK pk = new PK();
private String value;
@Column(name="type",insertable=false, updatable=false) //NB!
private int type;
Next the nested inner type:
@Embeddable
public static class PK implements IsSerializable, Serializable
{
private int type;
private long contact;
//...
}
}
Next, you need to make sure the relationships are maintained:
public void setContact(Contact contact)
{
this.contact = contact;
this.pk.contact = (contact == null || contact.getId() == null )? -1L : contact.getId();
}
public void setType(int type)
{
int type = pk.type;
this.pk.type = type;
this.type = type;
}
Finally, make sure your parent class is using method level annotations (I didn't know you couldn't mix them until just today) and be sure to fix the relationships there -- this makes sure that after the phase 1 (insert of the parent object) runs, the FK part of the PK is updated:
@OneToMany(mappedBy = "contact", // this is the field on the ContactItem object
fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@MapKey(name = "type") // field on ContactItem
public Map getContactItemMap()
{
for (Entry entry : this.contactItemMap.entrySet())
{
entry.getValue().setContact(this);
entry.getValue().setContactItemTypeId(entry.getKey());
}
return this.contactItemMap;
}
Chatter
1 sec ago
3 hours 35 min ago
4 hours 19 min ago
1 week 3 hours ago
1 week 2 days ago
1 week 4 days ago
1 week 6 days ago
3 weeks 21 hours ago
3 weeks 4 days ago
3 weeks 4 days ago