I've here a database with a PERSON
- ADDRESS
- ADDRESS_TYPE
relationship maintained by a triple join table PERSON_ADDRESS
. The PERSON
-ADDRESS
relationship is effectively one-to-many.
PERSON
ID FIRSTNAME LASTNAME -- --------- -------- 1 John Doe 2 Jane Doe
ADDRESS
ID STREET CITY -- -------------------- ------------- 1 Home Street 1 Hometown 2 Office Street 1 Officetown 3 Main Street 1 Maintown 4 Business Building 1 Businesstown
ADDRESS_TYPE
ID NAME -- --------------- 1 Home Address 2 Office Address
PERSON_ADDRESS
PERSON_ID ADDRESS_TYPE_ID ADDRESS_ID --------- --------------- ---------- 1 1 1 1 2 2 2 1 3 2 2 4
For practical reasons, I'd like to have my Person
entity to end up like:
public class Person {
private Address homeAddress; // Insertable/updateable by ADDRESS_TYPE_ID=1
private Address officeAddress; // Insertable/updateable by ADDRESS_TYPE_ID=2
}
Is this ever possible with JPA 2.0 annotations?
I've read the Map Key Columns chapter of the JPA wikibook and it seems that I have to use a @MapKeyJoinColumn
, but it's not entirely clear to me how to successfully use it in this situation. I expected to see a @JoinColumn
example along it, but it is absent in the code snippets in the wikibook.
If that's not possible with @MapKeyJoinColumn
, then an alternative approach with help of maybe @MapKeyClass
on a Map<AddressType, Address>
is also welcome, as long as I can end up with a getHomeAddress()
and getOfficeAddress()
in the Person
entity.
Assuming that you have two predefined AddressType
s when other can be added, the following approach with @MapKeyJoinColumn
works:
public class AddressType {
public static final AddressType HOME = new AddressType(1L, "Home");
public static final AddressType OFFICE = new AddressType(2L, "Office");
...
... hashCode, equals based on id ...
}
public class Person {
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinTable(name = "PERSON_ADDRESS",
joinColumns = @JoinColumn(name = "PERSON_ID"),
inverseJoinColumns = @JoinColumn(name = "ADDRESS_ID"))
@MapKeyJoinColumn(name = "ADDRESS_TYPE_ID")
private Map<AddressType, Address> addresses = new HashMap<AddressType, Address>();
...
public Address getHomeAddress() {
return getAddress(AddressType.HOME);
}
public void setHomeAddress(Address a) {
setAddress(AddressType.HOME, a);
}
...
public void setAddress(AddressType type, Address a) {
if (a == null) {
addresses.remove(type);
} else {
addresses.put(type, a);
}
}
public Address getAddress(AddressType type) {
return addresses.get(type);
}
}
So, you have predefined methods for predefined address types, when other types can be used via direct access to the map. orphanRemoval
is used to implement setHomeAddress(null)
behaviour. @ElementCollection
wouldn't work here because it doesn't support join table.
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