After migrating to spring 3.1.0 from spring 3.0.5 hibernate save method is encountering problem with detached object - when trying to save new object with saveOrUpdate(Object obj) method where obj has no id.
Here are the code samples and unimportant parts are omitted
Domains:
and the object I'm really trying to save is task_comment:Code:@Entity @Table(name = "task") public class Task implements java.io.Serializable { private Long id; private Set<TaskComment> taskComments = new HashSet<TaskComment>(0); @OneToMany(fetch = FetchType.LAZY, mappedBy = "task", cascade = CascadeType.REMOVE) @OrderBy("date") public Set<TaskComment> getTaskComments() { return this.taskComments; } }
Controller:Code:@Entity @Table(name = "task_comment") public class TaskComment implements java.io.Serializable { private Long id; private Task task; private String content; public TaskComment() { } @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "task_id") public Task getTask() { return this.task; } }
HibernateFilter extends which extends from OpenSessionInViewFilter:Code:@RequestMapping(value="/{id}", method=RequestMethod.GET) public String getDretail(@PathVariable("id") long id, Model model){ model.addAttribute("task", taskDao.find(id)); model.addAttribute("taskComment", new TaskComment()); return "task-detail"; } @RequestMapping(value="/{id}", method=RequestMethod.POST) public String postComment(@PathVariable("id") long id, @ModelAttribute("taskComment") TaskComment taskComment, Model model){ Task task = taskDao.find(id); taskComment.setUsers(securityService.getCurrentUser()); taskComment.setTask(task); taskComment.setDate(new Date()); taskCommentDao.saveOrUpdate(taskComment); return "redirect:/dashboard/task/" + id; }
jsp exerpt:Code:public class HibernateFilter extends OpenSessionInViewFilter { @Override protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException { Session session = SessionFactoryUtils.getSession(sessionFactory, true); //set the FlushMode to auto in order to save objects. session.setFlushMode(FlushMode.AUTO); return session; } @Override protected void closeSession(Session session, SessionFactory sessionFactory) { try{ if (session != null && session.isOpen() && session.isConnected()) { try { session.flush(); } catch (HibernateException e) { throw new CleanupFailureDataAccessException("Failed to flush session before close: " + e.getMessage(), e); } catch(Exception e){ } } } finally{ super.closeSession(session, sessionFactory); } } }
Problem: overriding all the previous comments. In other words, Task domain contains only one taskComment after post which was the last entered. However, when I tried unit testing it had no problem, so I think it's the problem with @ModelAttribute binding.Code:<form:form modelAttribute="taskComment" method="post" cssClass="formFrm" style="padding:0;"> <form:textarea path="content" cssStyle="width:99%;"/> <input id="submit-btn" type="submit" value="Add comment" class="buttons" style="float:right;margin:0;"> </form:form>
Work around:
So I tried in multiple ways to get this working. I wrapped it in a transaction, played with the entity annotations... Only initializing new TaskComment then copying properties from bound taskComment then saving works fine.Code:public String postComment(@PathVariable("id") long id, @ModelAttribute("taskComment") TaskComment taskComment, Model model){ TaskComment taskComment2 = new TaskComment(); taskComment2.setContent(taskComment.getContent()); Task task = taskDao.find(id); taskComment2.setUsers(securityService.getCurrentUser()); taskComment2.setTask(task); taskComment2.setDate(new Date()); taskCommentDao.saveOrUpdate(taskComment2); return "redirect:/dashboard/task/" + id; }
I could do this to all my objects but would really painful and ugly refactoring. Any ideas? Please, help.
p.s I'm using hibernate 3.5.6-Final and spring security 3.1.0 and included pom file if it helps.


Reply With Quote
