2013-05-04 11 views
6

Ich habe die nächsten zwei Entitäten mit einer OneToOne Beziehung zwischen ihnen:Persist OneToOne Beziehung mit SpringData JPA

@Entity 
@Table(name = "tasks") 
public class Task { 
    @OneToOne(mappedBy = "task", cascade = CascadeType.PERSIST) 
    private Tracker tracker; 

    /* More code */ 
} 

@Entity 
@Table(name = "trackers") 
public class Tracker { 
    @OneToOne 
    @JoinColumn(name = "trk_task", unique = true) 
    private Task task; 

    /* More code */ 
} 

Ich versuche, diesen Code auszuführen:

Task task = taskService.findDispatchableTask(); 
if (task != null) { 
    Tracker tracker = trackerService.findIdleTracker(); 
    if (tracker != null) { 
     task.setTracker(tracker); 
     task.setStatus(TaskStatus.DISPATCHED); 
     taskService.save(task); 
    } 
} 

Aber ich habe diesen Fehler :

ERROR org.hibernate.AssertionFailure - an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session) 
org.hibernate.AssertionFailure: non-transient entity has a null id 

I "lösen" kann es meinen Code zu ändern:

Task task = taskService.findDispatchableTask(); 
if (task != null) { 
    Tracker tracker = trackerService.findIdleTracker(); 
    if (tracker != null) { 
     tracker.setTask(task); 
     trackerService.save(tracker); 
     task.setTracker(tracker); 
     task.setStatus(TaskStatus.DISPATCHED); 
     taskService.save(task); 
    } 
} 

Meine Frage ist, Was ist der richtige Weg, um eine OneToOne-Beziehung zu bestehen? Warum muss ich in meinem Code beide Teile der Beziehung speichern, damit sie funktioniert?

Antwort

15

Hier gehen wir wieder.

Jede bidirektionale Verknüpfung hat zwei Seiten: die Eigentümerseite und die umgekehrte Seite. Die umgekehrte Seite ist diejenige, die das mappedBy Attribut hat. Die Eigentümerseite ist die andere. JPA/Hibernate kümmert sich nur um die Eigentümerseite. Wenn Sie also nur die inverse Seite initialisieren, wird die Assoziation nicht beibehalten.

Es ist generell eine gute Übung, beide Seiten der Assoziation zu initialisieren. Erstens, weil es sicherstellt, dass die Eigentümerseite initialisiert wird, und zweitens, weil es den Graph von Entitäten kohärent macht, zu Ihrem Besten.

Beachten Sie auch, dass alle Entitäten, die von Ihren Abfragen zurückgegeben werden, angefügte Entitäten sind, wenn Sie innerhalb einer Transaktion arbeiten (und das sollten Sie). Die Änderungen, die auf die Entitäten angewendet werden, werden automatisch als permanent ausgeführt, wenn die Transaktion festgeschrieben wird (oder vorher). Es besteht keine Notwendigkeit, die Entitäten explizit zu speichern, so wie Sie es tun.

+0

Vielen Dank für Ihre großartige Antwort. Ich verstehe es jetzt, aber ich verstehe nicht, warum ich meine Entitäten nicht explizit speichern muss. Was save() tut, ist das Repository save() -Methode aufzurufen, das transaktional ist. Was soll ich machen? –

+2

Der Punkt einer Transaktion besteht darin, mehrere Entitäten mehrerer Arten atomar ändern zu können. Entweder ist alles gerettet, oder nichts ist. Das garantiert die Kohärenz der Daten. Aus diesem Grund sollten die Services transaktional sein und nicht nur die Repositories. Sobald Sie Entitäten in einer Transaktion laden und sie in derselben Transaktion ändern, behält Hibernate die Änderungen automatisch bei, ohne sie explizit speichern zu müssen. Kurz gesagt, der gesamte Code in Ihrer Frage sollte in einer transaktionalen Service-Methode sein, und Sie sollten 'save()' nicht aufrufen müssen. –