Hibernate triggering constraint violations using orphanRemoval
Some remarks:
- Since you have a bi-directional association, you need to add a
mappedBy
attribute to declare the owning side of the association. - Also don't forget that you need to manage both sides of the link when working with bi-directional associations and I suggest to use defensive methods for this (shown below).
- And you must implement
equals
andhashCode
onContact
.
So, in Account
, modify the mapping like this:
@Entity
public class Account {
@Id @GeneratedValue
public Long id;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "account", orphanRemoval = true)
public List<Contact> contacts = new ArrayList<Contact>();
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
}
In Contact
, the important part is that the @ManyToOne
field should have the optional
flag set to false
:
@Entity
public class Contact {
@Id @GeneratedValue
public Long id;
@ManyToOne(optional = false)
public Account account;
// getters, setters, equals, hashCode
}
With these modifications, the following just works:
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());
And the orphaned Contact
gets deleted, as expected. Tested with Hibernate 3.5.3-Final.
ptomli
Updated on June 03, 2022Comments
-
ptomli almost 2 years
I'm having trouble with a JPA/Hibernate (3.5.3) setup, where I have an entity, an "Account" class, which has a list of child entities, "Contact" instances. I'm trying to be able to add/remove instances of Contact into a List<Contact> property of Account.
Adding a new instance into the set and calling saveOrUpdate(account) persists everything lovely. If I then choose to remove the contact from the list and again call saveOrUpdate, the SQL Hibernate seems to produce involves setting the account_id column to null, which violates a database constraint.
What am I doing wrong?
The code below is clearly a simplified abstract but I think it covers the problem as I'm seeing the same results in different code, which really is about this simple.
SQL:
CREATE TABLE account ( INT account_id ); CREATE TABLE contact ( INT contact_id, INT account_id REFERENCES account (account_id) );
Java:
@Entity class Account { @Id @Column public Long id; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "account_id") public List<Contact> 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);
Result:
UPDATE contact SET account_id = null WHERE contact_id = ?
Edit #1:
It might be that this is actually a bug http://opensource.atlassian.com/projects/hibernate/browse/HHH-5091
Edit #2:
I've got a solution that seems to work, but involves using the 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<Contact> contacts = new HashSet<Contact>(); } class Contact { @ManyToOne(optional = false) @JoinColumn(name = "account_id", nullable = false) private Account account; }
Since Hibernate CascadeType.DELETE_ORPHAN is deprecated, I'm having to assume that it has been superseded by the JPA2 version, but the implementation is lacking something.
-
ptomli almost 14 yearsIt seems that somewhere in the changes I made to get Hibernate specific API working, I solved whatever it was that was breaking the JPA2 version. Somewhat embarrassingly I suspect it was the equals/hashCode...
-
Shantaram Tupe about 6 yearshello sir, I'm not using
EntityManager
, I'm usingSession#saveOrUpdate
hibernate specific, In DB I've 3 children, and from view I've 2 children to be updated and want to remove 3rd or last one, How to deal with that situation? Should I useSession#get()
orSession#load()
, to know which element to remove?