Using Hibernate 3.3, JPA 1.x and Hsqldb 1.8
I'm having an issue with cascading updates to objects that contain collections of other objects. Here's a contrived (slightly) example to illustrate (getters/setters omitted). Sorry its lots of code, I can't find another way to express the issue and its all fairly straightforward here.
@Entity
public class Salesman
{
@Id
@GeneratedValue
@Column(name="salesman_id")
private long id;
@OneToMany(mappedBy=salesman, targetEntity=ContactSet.class, fetch=FetchType.EAGER, cascade=CascadeType.All)
private Set<ContactSet> contacts;
}
@Entity
public class Company
{
@Id
@GeneratedValue
@Column(name="company_id")
private long id;
@Column(name="company_name")
private String name;
}
@Entity
public class ContactSet
{
@Id
@GeneratedValue
@Column(name="contact_set_id")
private long id;
@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name="salesman_id",referencedColumnName="salesman_id", updatable=false, nullable=false)
private Salesman salesman
@ManyToOne
@JoinColumn(name="company_id", referencedColumnName="company_id", updatable=false, nullable=false)
private Company company;
@OneToMany(mappedBy="contactSet", targetEntity=Contact.class, fetch=FetchType.EAGER, cascade=CascadeType.ALL)
private Set<Contact> contacts;
}
@Entity
public class Contact
{
@Id
@GeneratedValue
@Column(name="contact_id")
private long id;
@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name="contact_set_id",referencedColumnName="contact_set_id", updatable=true, nullable=false)
private ContactSet contactSet;
@Column(name="contact_name", updatable=false, nullable=false)
private String name;
}
So a salesman has a set of contacts for each company, and there may be many salesman that service a company, each with different contacts. I can add a new company (and associated contacts) to a salesman, everything is fine.
The issue comes when adding a new company (and contacts) to a salesman that already has some:
session.beginTransaction();
session.lock(salesman, LockMode.NONE)
ContactSet newSet = new ContactSet();
/*...Add contacts to ContactSet...*/
salesman.addContactSet(newSet);
session.saveOrUpdate(salesman);
session.commit();
When running this with hibernate.show_sql=true, I can see this has the expected effect of running an Insert for each Contact, and insert for the new ContactSet. I also see an update for the Salesman itself, as well as an update for every other ContactSet and Contact that the salesman in question has. Since there are thousands of contacts for each salesman, this transaction takes a long time.
My question is, how do I avoid all the updates for ContactSets and Contacts that I know haven't changed? I have tried adding the @Version tags, changing CascadeType (anything but ALL fails in my application) and loads of more specific code tinkering, but no success!
Thanks in advance for any pointers,
**Edit: Have found that adding session.lock(salesman, LockMode.NONE) to the transaction means the other objects don't get updated, but the code is still slow. I suspect because its still adding thousands of objects to the session to lock them. Updated example code to reflect this. The LockMode.NONE simply reassociates the detached objects with the session.
Phil
I was unable to find a good solution to this, so I ended up storing the ContactSet in the Company as well as the Salesman, and removing all cascade from Salesman->ContactSet. Now I only have to save a new Company, and all new ContactSets are saved. There are still situations where all a Salesman's ContactSets must be updated, and this can only be solved (as far as I can see) by saving all of a salesman's Companies.
If anyone finds a better solution please add!