2009-12-08 9 views
5

Desidero una unidirezionale relazione uno-a-uno tra oggetti di 3 classi java: Person to Heart e Person to Liver. Voglio che gli oggetti condividano lo stesso PK, cioè ogni persona ha un cuore e un fegato corrispondenti, dove person.person_id = heart.heart_id = liver.liver_id. Non voglio unire le 3 tabelle in 1 perché ognuna ha un sacco di campi. Ecco il mio codice (per lo più in base alla risposta accettata per this question):Hibernate associazione di entità one-to-one con PK condiviso tra 3 classi

@Entity 
public class Person { 
    public long personId; 
    private String name; 
    public Heart heart; 
    public Liver liver; 
    // other fields 

    @Id 
    @GeneratedValue 
    public long getPersonId() {return personId;} 

    @OneToOne(cascade = CascadeType.ALL) 
    @PrimaryKeyJoinColumn 
    public Heart getHeart() {return heart;} 

    @OneToOne(cascade = CascadeType.ALL) 
    @PrimaryKeyJoinColumn 
    public Liver getLiver() {return liver;} 

    // other getters and setters and constructors 
} 


@Entity 
public class Heart { 
    private long heartId; 
    private int bpm; 
    private Person person; 
    // other fields 

    @Id 
    @GenericGenerator(
     name = "generator", 
     strategy = "foreign", 
     parameters = @Parameter(name = "property", value = "person") 
    ) 
    @GeneratedValue(generator = "generator") 
    public long getHeartId() {return heardId;} 

    @OneToOne(mappedBy="heart") 
    @PrimaryKeyJoinColumn 
    public Person getPerson() {return person;} 

    // other getters and setters and constructors 
} 


@Entity 
public class Liver { 
    private long liverId; 
    private boolean healthy; 
    private Person person; 
    // other fields 

    // the rest uses the same hibernate annotation as Heart 
} 

I ha installato la sessione e procedere come segue:

Person jack = new Person(); 
jack.setName("jack"); 
Heart heart = new Heart(); 
heart.setBpm(80); 
Liver liver = new Liver(); 
liver.setHealthy(true); 

Poi se collego l'oggetto persona con i suoi organi, e salvarlo, ottengo un errore (NOTA: ho avuto lo stesso comportamento quando ho appena usato 2 classi, ad esempio persona e cuore):

jack.setHeart(heart); 
jack.setLiver(liver); 
session.save(jack); 

org. hibernate.id.IdentifierGenerationException: ha tentato di assegnare id da nulla proprietà uno-a-uno: persona

Tuttavia funziona se ho impostato il rapporto in entrambi i modi:

jack.setHeart(heart); 
heart.setPerson(jack); 
jack.setLiver(liver); 
liver.setPerson(jack); 
session.save(jack); 

Ma sicuramente questo non dovrebbe essere necessario per le relazioni unidirezionali?
Cheers

ps. Stranamente, noto che funziona (salva entrambi gli oggetti nel DB) quando uso solo 2 classi, ad es. Persona e cuore, e ho appena impostato il link il contrario:

heart.setPerson(jack); 
session.save(heart); 

Non ho idea del perché questo funziona (sembra logico per me quella persona è l'oggetto padre, come si auto-genera il proprio PK, e gli altri lo usano, quindi è tutto quello che dovresti impostare), ma comunque non riesco a capire come applicare questo metodo di lavoro alla mia situazione a 3 classi ...

+0

ho appena trovato questa domanda, che credo sia per quanto riguarda la stessa situazione, ma utilizza un approccio completamente diverso (che utilizza @Embeddable e @Inheritance): http://stackoverflow.com/questions/ 904.634. L'unico problema è che non c'è abbastanza codice pertinente nella soluzione accettata per comprenderlo/implementarlo ... qualcuno può aiutarti? – jackocnr

risposta

6

Odio dirti questo, ma hai una relazione bidirezionale. La Persona ha un riferimento al Cuore e al Fegato e ognuno di questi ha un riferimento alla Persona. Le annotazioni che hai impostato sulle proprietà Id di Heart e Liver stanno specificamente dicendo che ottengono il valore della loro proprietà Id delegando alla loro proprietà Person.Negli esempi che hai dimostrato che non funzionano, non hai ancora impostato la proprietà Person su quei ragazzi e quindi, ovviamente, non possono ottenere il loro valore Id.

È possibile impostare questa relazione come una vera OnetoOne unidirezionale, che è documentata nella documentazione di annotazioni di Hibernate:

@Entity 
public class Body { 
    @Id 
    public Long getId() { return id; } 

    @OneToOne(cascade = CascadeType.ALL) 
    @PrimaryKeyJoinColumn 
    public Heart getHeart() { 
     return heart; 
    } 
    ... 
} 


@Entity 
public class Heart { 
    @Id 
    public Long getId() { ...} 
} 

o si può cambiare la nostra entità oggetti con dimensioni leggermente per snellire collegare entrambi i lati della relazione come ad esempio:

@Entity 
public class Person { 
    public long personId; 
    private String name; 
    public Heart heart; 
    public Liver liver; 
    // other fields 

    @Id 
    @GeneratedValue 
    public long getPersonId() {return personId;} 

    @OneToOne(cascade = CascadeType.ALL) 
    @PrimaryKeyJoinColumn 
    public Heart getHeart() {return heart;} 

    public void setHeart(Heart heart){ 
     this.heart = heart; 
     this.heart.setPerson(this); 
    } 

    @OneToOne(cascade = CascadeType.ALL) 
    @PrimaryKeyJoinColumn 
    public Liver getLiver() {return liver;} 

    public void setLiver(Liver liver){ 
     this.liver = liver; 
     this.liver.setPerson(this); 
    } 
    // other getters and setters and constructors 
} 
+0

Ho provato l'esempio dalla documentazione, ma non capisco come possa funzionare: non c'è nulla per mantenere gli ID coerenti tra le 3 tabelle. L'unica ragione per cui ho un campo di persone nelle classi di cuore e di fegato è per l'estraneo @GenericGenerator, in modo che tutti gli oggetti Cuore e Fegato abbiano lo stesso ID del loro oggetto Persona corrispondente. NOTA: Ottengo lo stesso errore citato nella domanda se rimuovo @OneToOne e @PrimaryKeyJoinColumn dal getPerson() nelle classi Heart e Liver. – jackocnr

1

Non l'ho provato, ma direi che ....

Una differenza tra entrambi i lati è che lo 0 La classenon ha mappedBy nella sua mappatura.

Quindi, per ogni One-To-One, Person ha il valore di riferimento, quello che Hibernate considera come ufficiale. Al contrario, sugli altri oggetti, lo mappedBy indica a Hibernate di non utilizzare il valore, ma passa a quell'oggetto e considera la proprietà mappata su quell'oggetto.


Per controllare se ho ragione, è possibile impostare solo i valori che non hanno mappedBy, e salvare l'oggetto Person (perché è quello che ha le cascate), e vedere se il risultato è corretto .. ;-)

+0

Posso semplicemente creare un nuovo oggetto Person, impostare il suo nome e salvarlo senza errori. Ma se creo un nuovo oggetto Heart, impostalo come campo bpm e lo salvo, ottengo di nuovo IdentifierGenerationException. È ciò che intendi? Conosci una soluzione alternativa? – jackocnr

Problemi correlati