Нарушение спящего режима с помощью orphanRemoval
У меня проблемы с установкой JPA / Hibernate (3.5.3), где у меня есть объект, class «Учетная запись», в котором есть список дочерних объектов, «Контакт». Я пытаюсь добавить / удалить экземпляры контакта в список свойства учетной записи.
Добавление нового экземпляра в набор и вызов saveOrUpdate (account) сохраняют все прекрасное. Если я затем выберу контакт из списка и снова вызову saveOrUpdate, похоже, создается hibernate SQL, связанный с установкой для столбца account_id значения null, что нарушает ограничение базы данных.
Что я делаю не так?
- В чем разница между однонаправленными и двунаправленными ассоциациями JPA и Hibernate?
- Hibernate: MyInterceptor # onFlushDirty никогда не вызывается
- объект ссылается на несохраненный экземпляр переходного процесса - сохраняет временный экземпляр перед промывкой
- Что используется session.flush () в Hibernate
- JPA: как иметь отношение «один ко многим» одного и того же типа Entity
Кодекс ниже явно упрощенная реферат, но я думаю, что она охватывает проблему, поскольку я вижу те же результаты в другом коде, который действительно об этом прост.
SQL:
CREATE TABLE account ( INT account_id ); CREATE TABLE contact ( INT contact_id, INT account_id REFERENCES account (account_id) );
Ява:
@Entity class Account { @Id @Column public Long id; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "account_id") public List contacts; } @Entity class Contact { @Id @Column public Long id; @ManyToOne(optional = false) @JoinColumn(name = "account_id", nullable = false) public Account account; } Account account = new Account(); Contact contact = new Contact(); account.contacts.add(contact); saveOrUpdate(account); // some time later, like another servlet request.... account.contacts.remove(contact); saveOrUpdate(account);
Результат:
UPDATE contact SET account_id = null WHERE contact_id = ?
Редактировать # 1:
Возможно, это на самом деле ошибка http://opensource.atlassian.com/projects/hibernate/browse/HHH-5091
Редактировать # 2:
У меня есть решение, которое, похоже, работает, но предполагает использование Hibernate API
class Account { @SuppressWarnings("deprecation") @OneToMany(cascade = CascadeType.ALL, mappedBy = "account") @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN) @JoinColumn(name = "account_id", nullable = false) private Set contacts = new HashSet(); } class Contact { @ManyToOne(optional = false) @JoinColumn(name = "account_id", nullable = false) private Account account; }
Поскольку Hibernate CascadeType.DELETE_ORPHAN устарел, я должен предположить, что он был заменен версией JPA2, но в реализации чего-то не хватает.
- Автоматическое резервирование зарезервированного слова для таблиц и столбцов Hibernate
- Hibernate против JPA против JDO - плюсы и минусы каждого?
- .net ORM Сравнение
- Почему Hibernate не требует конструктора аргументов?
- session.connection () не рекомендуется в Hibernate?
- Bypass GeneratedValue в спящем режиме (данные объединить не в db?)
- Есть ли способ вызвать хранимую процедуру с помощью Dapper?
- Каково решение проблемы N + 1 в JPA и Hibernate?
Некоторые замечания:
- Поскольку у вас есть двунаправленная связь, вам нужно добавить атрибут
mappedBy
чтобы объявить свою сторону ассоциации. - Также не забывайте, что вам нужно управлять обеими сторонами ссылки при работе с двунаправленными ассоциациями, и я предлагаю использовать для этого защитные методы (показано ниже).
- И вы должны реализовать
equals
иhashCode
onContact
.
Итак, в Account
измените отображение следующим образом:
@Entity public class Account { @Id @GeneratedValue public Long id; @OneToMany(cascade = CascadeType.ALL, mappedBy = "account", orphanRemoval = true) public List contacts = new ArrayList (); public void addToContacts(Contact contact) { this.contacts.add(contact); contact.setAccount(this); } public void removeFromContacts(Contact contact) { this.contacts.remove(contact); contact.setAccount(null); } // getters, setters }
В Contact
важная часть состоит в том, что поле @ManyToOne
должно иметь optional
флаг, установленный в false
:
@Entity public class Contact { @Id @GeneratedValue public Long id; @ManyToOne(optional = false) public Account account; // getters, setters, equals, hashCode }
С этими изменениями выполняются только следующие работы:
Account account = new Account(); Contact contact = new Contact(); account.addToContact(contact); em.persist(account); em.flush(); assertNotNull(account.getId()); assertNotNull(account.getContacts().get(0).getId()); assertEquals(1, account.getContacts().size()); account.removeFromContact(contact); em.merge(account); em.flush(); assertEquals(0, account.getContacts().size());
И потерянный Contact
удаляется, как и ожидалось. Протестировано с Hibernate 3.5.3-Final.