2013-05-04 2 views
6

У меня есть следующие две сущности с OneToOne отношения между ними:Упорство OneToOne отношения с 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 */ 
} 

Я пытаюсь запустить этот код:

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

Но я получаю эту ошибку :

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 

я могу "решить" это изменение моего кода:

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

Мой вопрос: Каков правильный способ сохранения отношения OneToOne? В моем коде, Почему я сохраняю обе части отношения, чтобы заставить его работать?

ответ

15

Здесь мы снова идем.

Каждая двунаправленная ассоциация имеет две стороны: сторону владельца и обратную сторону. Обратная сторона - та, которая имеет атрибут mappedBy. Сторона владельца - другая. JPA/Hibernate только заботится о владельце. Поэтому, если вы просто инициализируете обратную сторону, ассоциация не будет сохранена.

Это хорошая практика, как правило, для инициализации обеих сторон ассоциации. Во-первых, потому что он удостоверяет, что сторона владельца инициализирована, а во-вторых, потому что он делает граф объектов связными, для вашего же блага.

Также обратите внимание, что если вы работаете внутри транзакции (и вы должны), все объекты, возвращаемые вашими запросами, являются прикрепленными объектами. Изменения, применяемые к объектам, автоматически становятся постоянными при совершении транзакции (или раньше). Нет необходимости сохранять объекты явно так, как вы делаете.

+0

Спасибо за отличный ответ. Я понимаю это сейчас, но я не понимаю, почему мне не нужно сохранять свои объекты явно. Что save() делает для вызова метода сохранения репозитория(), который является транзакционным. Что мне делать? –

+2

Пункт транзакции заключается в том, чтобы иметь возможность изменять несколько объектов, из нескольких видов, атомарно. Либо все спасено, либо ничего. Именно это гарантирует согласованность данных. И поэтому сервисы должны быть транзакционными, а не только репозиториями. Как только вы загружаете объекты в транзакцию и изменяете их в одной транзакции, Hibernate автоматически сохраняет изменения, не требуя явно их сохранения. Короче говоря, весь код в вашем вопросе должен быть в методе транзакционной службы, и вам не нужно будет вызывать 'save()'. –