2012-06-09 38 views
14

Tengo dos clases Foo y Bar. En las tablas de la base de datos se ven así:Clave primaria compuesta, clave externa. ¿Referencia a objeto o clave?

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

|Bar| 
|id : INT (PK) | 

Normalmente habría mapa de esta manera:

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

Sin embargo, los identificadores de FooPK se asignan libremente y necesitan ser conectados manualmente. Preferiría una solución que haga mapas utilizando objetos en lugar de identificadores sueltos. He intentado lo siguiente, pero (por supuesto) no funcionó, pero creo que da una idea de lo que me gustaría lograr:

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

Otro problema con esta última solución es que el método de getBarId()FooPK necesita tener un método setBarId(Int). Establecer el Objeto usando la ID se puede hacer accediendo a la capa de acceso a los datos, sin embargo esto (en mi opinión) viola la separación de las capas de negocios/dominio/datos.

¿Qué hacer? Vaya con la primera solución y mantenga los identificadores sincronizados manualmente o ¿hay alguna otra (mejor) práctica?

Respuesta

23

En referencia a la discusión JPA 2: composite primary key classes, realizar los siguientes cambios en las clases de Foo y 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; 
} 

Sugiero primero lo que es trabajar con el acceso de campo y luego aplicar acceder a la propiedad.

¿Qué hacer? Vaya con la primera solución y mantenga los identificadores sincronizados manualmente o ¿hay alguna otra (mejor) práctica?

Protéjase del dolor: genere ids automáticamente.

+0

Así que ese es el primer ejemplo. He visto que este es 'el camino a seguir', me preguntaba si el segundo ejemplo era una manera más limpia. Supongo que es un no, ¿entonces? – siebz0r

+0

@ siebz0r oh, lo siento, no me di cuenta de que el primer ejemplo era el mismo; o. Creo que es más fácil ver PK cuando hay IDs, no objetos asignados. Si buscara por FooPK, necesitaría solo enteros y no Objetos con todas las propiedades innecesarias. – JMelnik

Cuestiones relacionadas