2012-06-09 6 views
14

Ho due classi Foo e Bar. Le tabelle del database simile a questa:Chiave primaria composta, chiave esterna. Riferimento a oggetto o chiave?

|Foo| 
|id : INT (PK) | bar_id : INT (PK, FK) | 

|Bar| 
|id : INT (PK) | 

Normalmente avrei mappa in questo modo:

@Entity 
public class Bar 
{ 
    @Id 
    @Column(name = "id") 
    private int id; 

    @OneToMany 
    private Set<Foo> foo; 
} 

@Entity 
public class Foo 
{ 
    @EmbeddedId 
    private FooPK key; 

    @MapsId("barId") 
    @ManyToOne 
    @JoinColumn(name = "bar_id", referencedColumnName = "id") 
    private Bar bar; 
} 

@Embeddable 
public class FooPK 
{ 
    @Column(name = "id") 
    private int id; 
    @Column(name = "bar_id") 
    private int barId; 
} 

Tuttavia i id in FooPK sono vagamente mappati e devono essere collegati manualmente. Preferirei una soluzione che mappasse usando Oggetti al posto di ID sciolti. Ho provato quanto segue ma (ovviamente) non ha funzionato, ma penso che dà un'idea di quello che vorrei realizzare:

@Entity 
public class Bar 
{ 
    @Id 
    @Column(name = "id") 
    private int id; 

    @OneToMany 
    private Set<Foo> foo; 
} 

@Entity 
public class Foo 
{ 
    @EmbeddedId 
    private FooPK key; 

    @MapsId("barId") 
    @ManyToOne 
    @JoinColumn(name = "bar_id", referencedColumnName = "id") 
    @Access(AccessType.FIELD) 
    private Bar getBar() 
    { 
     return key.getBar(); 
    } 
} 

@Embeddable 
public class FooPK 
{ 
    @Column(name = "id") 
    private int id; 

    @Transient 
    private Bar bar; 

    //.... 

    @Column(name = "bar_id") 
    @Access(AccessType.PROPERTY) 
    private int getBarId 
    { 
     return bar.getId(); 
    } 
} 

Un altro problema con quest'ultima soluzione è che il metodo getBarId() in FooPK deve avere un metodo setBarId(Int). L'impostazione dell'oggetto usando l'ID può essere effettuata accedendo al livello di accesso ai dati, tuttavia questo (a mio avviso) viola la separazione dei livelli business/dominio/dati.

Quindi cosa fare? Vai con la prima soluzione e mantieni gli ID in sincronia manualmente o c'è un'altra (migliore) pratica?

risposta

23

Riferendosi alla JPA 2: composite primary key classes discussione, apportare le seguenti modifiche alle classi Foo e FooPK:

@Entity 
public class Foo { 

    @EmbeddedId 
    private FooPK key; 

    @MapsId("barId") //references EmbeddedId's property 
    @JoinColumn(name = "bar_id", referencedColumnName = "id") 
    @ManyToOne 
    private Bar bar; 
} 

@Embeddable 
public class FooPK { 

    @Column(name = "id") 
    private int id; 
    @Column(name = "bar_id") 
    private int barId; 
} 

Suggerisco prima farlo funzionare con l'accesso di campo e quindi applicare l'accesso alle proprietà.

Quindi cosa fare? Vai con la prima soluzione e mantieni gli ID in sincronia manualmente o c'è un'altra (migliore) pratica?

Salva te stesso dal dolore: generare automaticamente gli ID.

+0

Quindi questo è il primo esempio. Ho visto che questa è "la strada da percorrere", mi stavo chiedendo se il secondo esempio fosse un modo più pulito. Immagino che sia un no allora? – siebz0r

+0

@ siebz0r oh, scusa, non ho notato che il primo esempio era lo stesso; o. Penso che sia più semplice guardare PK quando ci sono ID, non oggetti mappati. Se dovessi cercare in FooPK, avresti bisogno solo di interi e non di oggetti con tutte le proprietà non necessarie. – JMelnik