2010-10-03 30 views
12

J'ai ici une base de données avec une relation PERSON - ADDRESS - ADDRESS_TYPE entretenue par une table de jointure triple PERSON_ADDRESS. La relation PERSON - ADDRESS est effectivement un-à-plusieurs.Définition de plusieurs propriétés d'adresse dans Person per AddressType par une table de jointure triple

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 

Pour des raisons pratiques, je voudrais avoir mon finir comme entité Person:

public class Person { 
    private Address homeAddress; // Insertable/updateable by ADDRESS_TYPE_ID=1 
    private Address officeAddress; // Insertable/updateable by ADDRESS_TYPE_ID=2 
} 

Est-ce jamais possible avec annotations JPA 2.0? J'ai lu le Map Key Columns chapter of the JPA wikibook et il semble que je devrais utiliser un @MapKeyJoinColumn, mais il n'est pas tout à fait clair pour moi comment l'utiliser avec succès dans cette situation. Je m'attendais à voir un exemple @JoinColumn le long, mais il est absent dans les extraits de code dans le wikibook.

Si cela est impossible avec @MapKeyJoinColumn, puis une autre approche avec l'aide de peut-être @MapKeyClass sur un Map<AddressType, Address> est également la bienvenue, aussi longtemps que je peux retrouver avec un getHomeAddress() et getOfficeAddress() dans l'entité Person.

+0

Utilisez-vous une énumération pour ADDRESS_TYPE? Parce que cela aurait du sens, puisque vous voulez des types d'adresses fixes à la compilation. – Bozho

+0

@Bozho: Je ne l'ai pas encore implémenté complètement, mais si c'est possible, utiliser un @ Enumerated aurait certainement ma préférence, oui. – BalusC

+0

aha. Et quel est votre fournisseur de persistance? (Les annotations spécifiques au fournisseur sont autorisées en dernier recours, je suppose) – Bozho

Répondre

11

En supposant que vous avez deux AddressType prédéfinies s quand d'autres peuvent être ajoutés, l'approche suivante avec @MapKeyJoinColumn œuvres:

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); 
    } 
} 

Ainsi, vous avez des méthodes prédéfinies pour les types d'adresses prédéfinies, alors que d'autres types peuvent être utilisés via accès direct à la carte. orphanRemoval est utilisé pour mettre en œuvre le comportement setHomeAddress(null). @ElementCollection ne fonctionnerait pas ici car il ne prend pas en charge la table de jointure.

+0

Oh mon garçon, ça marche :) Merci beaucoup. Je jouais aussi du violon entre-temps, mais j'avais le '@ JoinTable' complètement faux. Cela fait aussi plus de sens maintenant. Je suis cependant curieux de savoir si c'est aussi possible avec un 'enum AddressType'. – BalusC

+0

très bien, +1 :) – Bozho

+0

@BalusC: Je pense que vous ne pouvez pas utiliser 'enum' à moins que vous ne changiez votre schéma de base de données. Vous pouvez utiliser 'enum' ou une entité mappée à la table' ADDRESS_TYPE'. – axtavt