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.