0

I am stuck at a point where i am trying to update a @OneToMany mapping.

Problem: I have 2 entities: Criteria and Tasks. A criteria can contain multiple tasks.

class Criteria {
    @OneToMany(mappedBy = "criteria", cascade = { CascadeType.PERSIST,
    CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.LAZY)
    @Cascade({ org.hibernate.annotations.CascadeType.SAVE_UPDATE,
               org.hibernate.annotations.CascadeType.ALL,
               org.hibernate.annotations.CascadeType.MERGE,
               org.hibernate.annotations.CascadeType.PERSIST })
     private Set<Task> tasks;
}

class Task {
    @ManyToOne
    @JoinColumn(name = "CRITERIA_ID", nullable=false)
    private Criteria criteria;
}

I am trying to update an existing Criteria with new set of tasks. This requires removing all existing tasks and adding new tasks. Here is how i am doing:

criteriaDao.persist(criteria);
Set<Task> existingTasks=criteria.getTasks();
if(existingTasks != null) {
    for (Task task : existingTasks) {
        task.setCriteria(null);
    }
    criteria.setTasks(tasks);
    //more code which sets criteria for each task - this works if i try to save new criteria with new tasks
}

Hibernate is throwing an exception:

Caused by: java.sql.BatchUpdateException: ORA-00001: unique constraint (CCA_DEV_5.TASK__UN) violated

This exception is thrown because it didn't remove the existing tasks.

If i only delete all the tasks, it works perfectly and removed all the tasks for the given criteria with below code:

criteriaDao.persist(criteria);
Set<Task> existingTasks=criteria.getTasks();
if(existingTasks != null) {
    for (Task task : existingTasks) {
    task.setCriteria(null);
}
}

I tried all options of using merge after adding new tasks and more stuff but nothing seems to work for me. I am badly stuck here and definately missing something very basic in Hibernate.

Thanks in advance.!

Rakesh Sinha
  • 19
  • 1
  • 6

1 Answers1

0

In your Task class you should have no reference to Criteria at all. Just remove it along with its annotations.

Then, instead of setting the Criteria referenced by the Task, as this reference no longer exists, just use criteria.setTasks(newTasks) and save criteria.


Let me explain a bit. The relation between Criteria and Task is obvoiusly Criteria 1 --- n Tasks - 1 to n. Now, you can call one-to-many relation and you can call it many-to-one, depends on your point of view. Looking from Criteria oint of view, it's one-to-many, while looking from Task point of view - it's many-to-one. Generally, it's advisable and more intuitive to choose one of these point of views and stick to it. Choosing a point of view, defines the entity that "controls" the relation thorugh which any modification should be applied.

In your case, if you regard a Criteria as containing or owning a set of Tasks - you'd treat the relation as one-to-many. After deciding that, no need for Criteria reference in the java class of Task. You control the relation solely with Criteria API (the table created for Task in the DB, however, does have a column that refers its containing Criteria).

yair
  • 8,945
  • 4
  • 31
  • 50
  • Hmm...many-to-one in my opinion should work along with one-to-many, i haven't seen any issues with it. Also JPA supports inverse mapping. I got this fixed by adding following lines before setting the collection with new set collection- for (Task task : existingTasks) { task.setCriteria(null); } => session.flush(); criteria.setTasks(tasks); This makes sure that the delete statements are executed before the inserts. – Rakesh Sinha May 08 '12 at 06:16
  • @RakeshSinha it's great that you solved the problem. However, I'd recommend you read [this short parent-child hibernate docs example](http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html_single/#example-parentchild-bidir) which explains how to use `inverse` in a `one-to-many` relation. – yair May 08 '12 at 06:41
  • Thank you, i have seen this document in past and using the same. Do you think there is another way of achieving what i am doing.? Calling flush() explicitly works but that would cause entity manager to flush all the queries to Db. Is there a better way of updating a collection.? – Rakesh Sinha May 08 '12 at 17:35