2011-07-26 13 views
46

Estoy trabajando con un esquema existente que prefiero no cambiar. El esquema tiene una relación uno-a-uno entre las tablas Person y VitalStats, donde Person tiene una clave principal y VitalStats usa el mismo campo que su clave principal y su clave foránea para Person, lo que significa que su valor es el valor de la PK correspondiente. de Persona.JPA @OneToOne con identificación compartida: ¿puedo hacerlo mejor?

Estos registros se crean mediante procesos externos, y mi código JPA nunca necesita actualizar VitalStats. Por mi modelo de objetos Me gustaría que mi clase Persona para contener un miembro VitalStats, PERO:

Cuando intento

@Entity 
public class Person{ 
    private long id; 
    @Id 
    public long getId(){ return id; } 

    private VitalStats vs; 
    @OneToOne(mappedBy = “person”) 
    public VitalStats getVs() { return vs; } 
} 

@Entity 
    public class VitalStats{ 
    private Person person; 
    @OneToOne 
    public Person getPerson() { return person; } 
} 

tengo el problema de que carece de un VitalStats @Id, que no funciona para un @Entity. \

Si intento

@Id @OneToOne 
public Person getPerson() { return person; } 

que resuelve el problema @Id pero requiere que la persona sea serializable. Volveremos sobre eso.

Podría hacer VitalStats @Embeddable y conectarlo a Person a través de @ElementCollection, pero luego tendría que accederse como una colección, aunque sé que solo hay un elemento. Doable, pero a la vez un poco molesto y un poco confuso.

¿Qué me impide decir que la persona implementa Serializable? Nada, en realidad, excepto que me gusta que todo en mi código esté allí por una razón, y no puedo ver ninguna lógica en esto, lo que hace que mi código sea menos legible.

Mientras tanto, simplemente reemplacé el campo Persona en VitalStats con una ID de persona larga e hice esa Id. De VitalStats, por lo que ahora funciona @OneToOne.

Todas estas soluciones a lo que parece (a mí) como un problema directo son un poco torpes, entonces me pregunto si me falta algo, o si alguien puede explicarme por qué Person tiene que ser Serializable

TIA

Respuesta

67

Para asignar asociación uno-a-uno utilizando claves primarias compartidos usan @PrimaryKeyJoinColumn y @MapsId anotación.

secciones pertinentes de la documentación de Hibernate referencia:

PrimaryKeyJoinColumn

La anotación PrimaryKeyJoinColumn sí dice que la clave primaria de la entidad se utiliza como valor de clave externa a la entidad asociada.

MapsId

La anotación MapsId pido Hibernate para copiar el identificador de otra entidad asociada. En la jerga de hibernación, se conoce como un generador extranjera pero el mapeo JPA lee mejor y se anima

persona.java

@Entity 
public class Person { 

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column(name = "person_id") 
    private Long id; 

    @OneToOne(cascade = CascadeType.ALL) 
    @PrimaryKeyJoinColumn 
    private VitalStats vitalStats;  
} 

VitalStats.java

@Entity 
public class VitalStats 
{ 
    @Id @Column(name="vitalstats_id") Long id; 

    @MapsId 
    @OneToOne(mappedBy = "vitalStats") 
    @JoinColumn(name = "vitalstats_id") //same name as id @Column 
    private Person person; 

    private String stats; 
} 

persona Tabla de base de datos

CREATE TABLE person (
    person_id bigint(20) NOT NULL auto_increment, 
    name  varchar(255) default NULL, 
    PRIMARY KEY (`person_id`) 
) 

Tabla VitalStats Base de datos

CREATE TABLE vitalstats 
(
    vitalstats_id bigint(20) NOT NULL, 
    stats   varchar(255) default NULL, 
    PRIMARY KEY (`vitalstats_id`) 
) 
+0

Gracias, pero me temo que esto no realmente ayuda Has añadido un campo de identificación en VitalStats además del campo Persona, mientras que no puedo agregar un nuevo campo al esquema y tengo que usar el FK que subyace a 'persona' como PK de VitalStats. Ese fue todo el punto. – Michael

+2

No es necesario agregar otra columna debido a la anotación MapById en el atributo persona. El ejemplo que publiqué no era muy claro, modifico el ejemplo para agregar más detalles a VitalStats (vea #JoinColumn en el atributo de persona). Agrego la tabla de base de datos VitalStats y Person y puede ver que la tabla VitalStats contiene solo un PK que es el FK por persona. Creo que es lo mismo que lo que quieres? Si no es lo que quieres, ¿puedes agregar más detalles sobre el diseño de la mesa? @MapId tiene la ventaja de que la entidad persona no necesita ser serializable. Actualicé el ejemplo. –

+0

¡Eso es excelente, gracias! Lo intenté y funcionó. – Michael

8

En mi caso esto hizo el truco:

clase de Padres:

public class User implements Serializable { 
    private static final long serialVersionUID = 1L; 

    /** auto generated id (primary key) */ 
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column(unique = true, nullable = false) 
    private Long id; 

    /** user settings */ 
    @OneToOne(cascade = CascadeType.ALL, mappedBy = "user") 
    private Setting setting; 
} 

de Child-Pugh:

public class Setting implements Serializable { 
    private static final long serialVersionUID = 1L; 

    /** setting id = user id */ 
    @Id 
    @Column(unique = true, nullable = false) 
    private Long id; 

    /** user with this associated settings */ 
    @MapsId 
    @OneToOne 
    @JoinColumn(name = "id") 
    private User user; 
} 
+0

Esta es una respuesta correcta para mí. Crea una clave externa en la base de datos, y este es un problema con otras respuestas. Desafortunadamente, no pude forzar a Hibernate a hacer solo 1 consulta para obtener entidades como User & Setting. Siempre haciendo 2 consultas, incluso si se crea la clave foránea y fetch = FetchType.EAGER ... – dorsz

+0

me funciona, vote por esta respuesta. –

Cuestiones relacionadas