I am using JPA with Hibernate underneath, and I am having trouble getting merge to work, but before I describe the problems I am encountering with JPA, let me lay out what I am trying to accomplish, just in case my problems stems from my approach.
I have data from on system that I need to place into another system. To do this I am reading the data then constructing new ORM objects based on that data, I then want to persist the ORM objects into the database. Now when the database is empty, the program works without issue, with a simple em.persist(object) call. However I want to be able to run this process when the database has data in it, adding new data and updating old data as necessary, this is where I am having issues.
I do a simple check to see if the item already exists in the database, if nothing is found, I persist, if the record exists, I attempt a merge. Which fails with a duplicate record error;
ERROR JDBCExceptionReporter - Violation of UNIQUE KEY constraint 'UK-SubStuff-StuffId-SubStuffNumber'. Cannot insert duplicate key in object 'SubStuff'.
It strikes me as strange that the em.merge() call is attempting a insert instead of a update (I confirmed this through SQL logging).
Hibernate: insert into SubStuff (SubStuffNumber, StuffId, Name, TypeId) values (?, ?, ?, ?)
I should note that I am using objects that cascade into sub-objects. The failure is occurring on a sub-object, which makes sense to me as I would expect it to try and merge the sub-objects first.
Below is my code, please excuse the rough state, I've tried to document a few workaround approaches here.
private void storeData(Collection<Stuff> Stuffs) {
for (Stuff stuff : Stuffs) {
//I think this first block can be safely ignored, as I am having no issues with it
// Left it in just in case someone more experianced then I sees the root of the issue here.
Collection<SubStuff> subStuffs = stuff.getSubStuffCollection();
for (SubStuff s : subStuffs) {
//Persist SubStuff Type, which DOES NOT cascade,
// due to it not having an internal SubStuff collection
Query q = em.createNamedQuery("SubStuffType.findByType");
q.setParameter("type", f.getTypeId().getType());
try {
SubStuffType sst = (SubStuffType) q.getSingleResult();
s.setTypeId(sst);
} catch (NoResultException ex) {
if (logger.isDebugEnabled()) logger.debug("SubStuff Type not found, persisting");
em.persist(s.getTypeId());
}
}
if (em.find(Stuff.class, stuff.getId()) == null) {
//Persist on Stuffs will cascade to SubStuffs
em.persist(stuff);
} else {
// Failing to merge SubStuff, tries to insert duplicate
// Merge SubStuff first
// The block below is my attempt to merge the SubStuff Collection before merging Stuff,
// it creates the same isuse as a straight merge of Stuff.
Collection<SubStuff> mergedSubStuffs = new ArrayList<SubStuff>(SubStuffs.size());
for (SubStuff s : SubStuffs) {
Query q = em.createNamedQuery("SubStuff.findBySubStuffNumberStuffId");
q.setParameter("SubStuffNumber", s.getSubStuffNumber());
q.setParameter("StuffId", stuff.getId());
try {
SubStuff subStuff = (SubStuff) q.getSingleResult();
// -----> Merge fails, with an duplicate insert error
SubStuff mergedSubStuff = em.merge(s);
mergedSubStuffs.add(mergedSubStuff);
} catch (NoResultException ex) {
throw ex;
}
}
stuff.setSubStuffCollection(mergedSubStuffs);
// -----> This will fails with same error as above, if I remove the attempt
// to merge the sub objects
em.merge(stuff);
}
}
}
If anyone with JPA experience can help me out I would really appreciate it. The differances between Hibernate's saveOrUpdate() and JPA's merge() are obviously tripping me up, but despite reading several articles on EnityManger's merge, I still can't wrap my head around what is going on here.
Thanks for your time.