beku8
Apr 30th, 2012, 05:18 AM
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:
@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;
}
}
and the object I'm really trying to save is task_comment:
@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;
}
}
Controller:
@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.getCurrentUse r());
taskComment.setTask(task);
taskComment.setDate(new Date());
taskCommentDao.saveOrUpdate(taskComment);
return "redirect:/dashboard/task/" + id;
}
HibernateFilter extends which extends from OpenSessionInViewFilter:
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);
}
}
}
jsp exerpt:
<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>
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.
Work around:
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.getCurrentUs er());
taskComment2.setTask(task);
taskComment2.setDate(new Date());
taskCommentDao.saveOrUpdate(taskComment2);
return "redirect:/dashboard/task/" + id;
}
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.
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.
Here are the code samples and unimportant parts are omitted
Domains:
@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;
}
}
and the object I'm really trying to save is task_comment:
@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;
}
}
Controller:
@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.getCurrentUse r());
taskComment.setTask(task);
taskComment.setDate(new Date());
taskCommentDao.saveOrUpdate(taskComment);
return "redirect:/dashboard/task/" + id;
}
HibernateFilter extends which extends from OpenSessionInViewFilter:
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);
}
}
}
jsp exerpt:
<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>
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.
Work around:
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.getCurrentUs er());
taskComment2.setTask(task);
taskComment2.setDate(new Date());
taskCommentDao.saveOrUpdate(taskComment2);
return "redirect:/dashboard/task/" + id;
}
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.
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.