org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions
Upon deleting an entity from the database I get the following exception:
org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions
at org.hibernate.collection.AbstractPersistentCollection.setCurrentSession(AbstractPersistentCollection.java:410)
at org.hibernate.event.def.OnUpdateVisitor.processCollection(OnUpdateVisitor.java:43)
at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:101)
at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:61)
at org.hibernate.event.def.AbstractVisitor.processEntityPropertyValues(AbstractVisitor.java:55)
at org.hibernate.event.def.AbstractVisitor.process(AbstractVisitor.java:123)
at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:101)
at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:52)
at org.hibernate.impl.SessionImpl.fireDelete(SessionImpl.java:767)
at org.hibernate.impl.SessionImpl.delete(SessionImpl.java:745)
at org.springframework.orm.hibernate3.HibernateTemplate$25.doInHibernate(HibernateTemplate.java:790)
at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:372)
at org.springframework.orm.hibernate3.HibernateTemplate.delete(HibernateTemplate.java:784)
at org.springframework.orm.hibernate3.HibernateTemplate.delete(HibernateTemplate.java:780)
at pl.edu.agh.adam.core.projects.dao.TagDAO.delete(TagDAO.java:98)
at pl.edu.agh.adam.core.projects.ProjectService.deleteTag(ProjectService.java:109)
at pl.edu.agh.adam.core.projects.web.TagPresenter.deleteTag(TagPresenter.java:97)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.sun.el.parser.AstValue.invoke(AstValue.java:234)
at com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:297)
at org.apache.myfaces.view.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:83)
at javax.faces.component._MethodExpressionToMethodBinding.invoke(_MethodExpressionToMethodBinding.java:88)
at org.apache.myfaces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:100)
at javax.faces.component.UICommand.broadcast(UICommand.java:120)
at javax.faces.component.UIData.broadcast(UIData.java:708)
at javax.faces.component.UIViewRoot._broadcastAll(UIViewRoot.java:890)
at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:234)
at javax.faces.component.UIViewRoot._process(UIViewRoot.java:1202)
at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:623)
at org.apache.myfaces.lifecycle.InvokeApplicationExecutor.execute(InvokeApplicationExecutor.java:35)
at org.apache.myfaces.lifecycle.LifecycleImpl.executePhase(LifecycleImpl.java:143)
at org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:93)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:189)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:79)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:198)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:852)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Thread.java:619)
I've dug deep and tried numerous solutions from the hibernate forum, but still I don't know what's going on and where the sessions are opened. The conditions under which this problem occurs:
First: OpenSessionInViewFilter
- I've asked about it here. Everything seemed to work fine, but the deleting stopped all of a sudden the following day and all I've done is - I've added a non-connected class to a non-connected package.
<!-- Hibernate OpenSession Filter -->
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
<init-param>
<param-name>singleSession</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>sessionFactoryBeanName</param-name>
<param-value>sessionFactory</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>
Second: three-tier architecture. Here are the classes and the JSF page:
@Entity
@Table(name = "tag")
public class Tag implements Serializable {
private static final long serialVersionUID = 1L;
@ManyToMany(mappedBy = "tags", targetEntity = Project.class)
List<Project> projects = new ArrayList<Project>();
@Transient
public static final String PROP_ID = "id";
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "tag_id")
private Long id;
@Transient
public static final String PROP_NAME = "name";
@Column(name = "name", length = 25, unique = true)
private String name;
}
public class TagDAO extends HibernateDaoSupport implements ITagDAO {
@Override
public void create(Tag tag) {
getHibernateTemplate().save(tag);
}
@Override
public Tag getTag(String name){
Tag group = null;
DetachedCriteria criteria = DetachedCriteria.forClass(Tag.class);
criteria.add(Restrictions.eq("name", name));
List<Tag> tags = getHibernateTemplate().findByCriteria(criteria);
if ((tags != null) && (tags.size() > 0)) {
group = (Tag)tags.get(0);
}
return group;
}
@Override
public Tag getTag(Long id){
Tag group = null;
List<Tag> groups = getHibernateTemplate().find(
"from Tag where id = ?", id);
if ((groups != null) && (groups.size() > 0)) {
group = (Tag)groups.get(0);
}
return group;
}
@Override
public List<Tag> getTags(){
List<Tag> ret = getHibernateTemplate().find("from Tag");
System.out.println("Dao got "+ret.size()+" tags");
return ret;
}
@Override
public Integer getTagCount() {
DetachedCriteria criteria = DetachedCriteria.forClass(Tag.class);
criteria.setProjection(Projections.rowCount());
return (Integer)(getHibernateTemplate().findByCriteria(criteria).get(0));
}
@Override
public void delete(Tag group) {
getHibernateTemplate().delete(group);
}
@Override
public void update(Tag group) {
getHibernateTemplate().update(group);
}
@Override
public List<Tag> getTags(Integer first, Integer resultsPerPage,
String order, Boolean asc) {
DetachedCriteria criteria = DetachedCriteria.forClass(Tag.class);
if (asc){
criteria.addOrder(Order.asc(order));
}else{
criteria.addOrder(Order.desc(order));
}
return (List<Tag>)getHibernateTemplate().findByCriteria(criteria, first, resultsPerPage);
}
}
public class ProjectService implements IProjectService {
// Beans used by this service.
private IProjectDAO projectDao;
private ITagDAO tagDao;
@Override
public void createProject(Project project) throws AlreadyExistsException {
if (projectDao.getProject(project.getName()) != null) {
throw new AlreadyExistsException();
}
projectDao.addProject(project);
}
@Override
public List<Project> getProjects(Integer first, Integer howMany, String order,
boolean asc) {
return projectDao.getProjects(first, howMany, order, asc);
}
@Override
public Integer getProjectCount(){
return projectDao.getProjectCount();
}
@Override
public List<Project> getProjects() {
return projectDao.getAllProjects();
}
@Override
public void deleteProject(Long id) {
projectDao.removeProject(id);
}
@Override
public List<Tag> getTags() {
return tagDao.getTags();
}
@Override
public Tag getTag(String name){
return tagDao.getTag(name);
}
@Override
public void createTag(Tag tag) throws AlreadyExistsException {
if (tagDao.getTag(tag.getName()) != nul开发者_如何学运维l) {
throw new AlreadyExistsException();
}
tagDao.create(tag);
}
@Override
public void deleteTag(Long id) {
tagDao.delete(tagDao.getTag(id));
}
@Override
public void updateTag(Tag tag) {
tagDao.update(tag);
}
}
@ManagedBean(name = "tagPresenter")
@RequestScoped
public class TagPresenter {
private List<Tag> tags;
private IProjectService projectService;
private Tag tag;
public void setTag(Tag tag) {
this.tag= tag;
}
public Tag getTag() {
return tag;
}
public TagPresenter() {
projectService = (IProjectService)ServiceFinder.getInstance()
.findBean("projectService");
tags = projectService.getTags();
}
private void refresh() {
tags = projectService.getTags();
}
public List<Tag> getTags() {
refresh();
return tags;
}
public void deleteTag() {
projectService.deleteTag(tag.getId());
}
}
finally the webpage:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:p="http://primefaces.prime.com.tr/ui">
<ui:composition template="/templates/template.xhtml">
<ui:define name="head">
<title>Tags</title>
<link rel="stylesheet" type="text/css" href="#{facesContext.externalContext.requestContextPath}/styles/style.css"/>
</ui:define>
<ui:define name="content">
<h:form name="commandForm">
<p:dataTable var="tag" name="tagsList" value="${tagPresenter.tags}" paginator="true" rows="10" >
<p:column sortBy="#{tag.id}">
<f:facet name="header">
<h:outputText value="Id" />
</f:facet>
<h:outputText value="#{tag.id}" />
</p:column>
<p:column sortBy="#{tag.name}">
<f:facet name="header">
<h:outputText value="Name" />
</f:facet>
<h:outputText value="#{tag.name}" />
</p:column>
<p:column>
<h:commandLink action="#{tagDisplayer.showTag}" value="Modify">
<f:setPropertyActionListener target="#{tagDisplayer.tag}" value="#{tag}"/>
</h:commandLink>
<h:commandLink action="#{tagPresenter.deleteTag}" value="Delete">
<f:setPropertyActionListener target="#{tagPresenter.tag}" value="#{tag}"/>
</h:commandLink>
</p:column>
</p:dataTable>
</h:form>
<p:messages id="deletingError" showDetail="true"/>
</ui:define>
</ui:composition>
</html>
How is this problem caused and how can I solve it?
Fixes for this vary widely; it can be caused by problems like
- bad session handling in your framework configuration
- bad cascade settings, incorrect persistence settings
- forgetting initialization of an Collection stored in a @ManyToOne database relation.
Make sure to check thoroughly for these cases in your mapping/hibernate configurations - before wasting hours. :D
Take a look at the HibernateTemplate chapter in the Spring documentation. Take a look at implementing a callback approach to access the session.
public void delete(final Tag group) throws Exception {
HibernateCallback callback = new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
Object groupObj = session.load(Tag.class, group.getId());
session.delete(groupObj);
return null;
}
};
getHibernateTemplate().execute(callback);
}
If the above is not what you are looking for you can still do a traditional approach as mentioned further down in the Spring documentation. In this approach don't use the HibernateTemplate to delete the object but use the Session from the HibernateDaoSupport to handle the delete.
public void delete(Tag group) throws Exception {
Session session = getSession(false);
Object groupObj = session.load(Tag.class, group.getId());
session.delete(groupObj);
}
This can be caused by calling session.disconnect()
instead of session.close()
.
精彩评论