现在的位置: 首页 > 综合 > 正文

org.hibernate.PersistentObjectException: detached entity passed to persist

2014年09月05日 ⁄ 综合 ⁄ 共 13658字 ⁄ 字号 评论关闭

有两个实体类:

通知和用户,需求是发送通知给各个用户,用户可以看到发来的所有通知,那么就是一个双向n-n的关系,看我下边的两个实体类:

/**  
 * @Project: middleware
 * @Title: Notify.java
 * @Package com.richsoft.middleware.domain
 * @Description: TODO
 * @author dsun <b>Email:dreamfly@126.com<b> 
 *              <b>Blog:http://blog.csdn.net/dsundsun/article/<b> 
 *              <b>QQ:349466315<b> 
 * @date 2014年2月26日 下午4:07:37
 * @Copyright: 2014 
 * @version V1.0  
 */
package com.richsoft.middleware.domain;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;

/**
 * @ClassName Notify
 * @Description TODO
 * @author dsun <b>Email:dreamfly@126.com<b>
 *         <b>Blog:http://blog.csdn.net/dsundsun/article/<b> <b>QQ:349466315<b>
 * @date 2014年2月26日 下午4:07:37
 */
@Entity(name = "tb_notify")
public class Notify implements Serializable {

	/**
	 * @Fields serialVersionUID : 序列化
	 */
	private static final long serialVersionUID = -1274705480010201155L;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;// 标示符

	@Column(length = 255)
	public String content;// 通知内容

	@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
	@JoinTable(name = "notify_user", 
		joinColumns = { @JoinColumn(name = "uid", referencedColumnName = "id") }, 
		inverseJoinColumns = { @JoinColumn(name = "nid", referencedColumnName = "id") })
	private Set<User> users = new HashSet<User>();

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}

	public Set<User> getUsers() {
		return users;
	}

	public void setUsers(Set<User> users) {
		this.users = users;
	}

	@Override
	public String toString() {
		return "Notify [id=" + id + ", content=" + content + ", users=" + users
				+ "]";
	}
}

package com.richsoft.middleware.domain;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;


/**
 * @ClassName User
 * @Description 用户登陆注册操作的基本信息
 * @author dsun <b>Email:dreamfly@126.com<b> 
 *              <b>Blog:http://blog.csdn.net/dsundsun/article/<b> 
 *              <b>QQ:349466315<b> 
 * @date 2014年2月7日 上午8:41:34
 */
@Entity(name="tb_user")
public class User implements Serializable {
	
	/**
	 * @Fields serialVersionUID : 序列化
	 */
	private static final long serialVersionUID = -7275682073812567790L;
	
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Long id ; //标示符
	
	@Column(nullable=false,length=255)
	private String email; //用户名(邮箱)
	
	@Column(nullable=false,length=255)
	private String password ;//密码
	
	@Column(length=255)
	private String address;//住址 
	
	@Column(length=255)
	private String realname;//用户真实姓名
	
	@Column(length=255)
	private String skills;//熟悉的技能
	
	@Column(length=255)
	private String icon;//用户头像
	
	@Column(length=50)
	private String theme = "blue";//网站主题模板样式
	
	@Column(length=255)
	private String shortname;//名字简写
	
	@OneToMany(fetch=FetchType.LAZY,mappedBy="user")
	private Set<Dbconfig> dbconfigs = new HashSet<Dbconfig>();
	
	@ManyToMany(mappedBy="users", fetch=FetchType.LAZY)  
	private Set<Notify> notifys = new HashSet<Notify>();
	
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public String getRealname() {
		return realname;
	}
	public void setRealname(String realname) {
		this.realname = realname;
	}
	public String getSkills() {
		return skills;
	}
	public void setSkills(String skills) {
		this.skills = skills;
	}
	public Set<Dbconfig> getDbconfigs() {
		return dbconfigs;
	}
	public void setDbconfigs(Set<Dbconfig> dbconfigs) {
		this.dbconfigs = dbconfigs;
	}
	public String getIcon() {
		return icon;
	}
	public void setIcon(String icon) {
		this.icon = icon;
	}
	public String getTheme() {
		return theme;
	}
	public void setTheme(String theme) {
		this.theme = theme;
	}
	public String getShortname() {
		return shortname;
	}
	public void setShortname(String shortname) {
		this.shortname = shortname;
	}
	public Set<Notify> getNotifys() {
		return notifys;
	}
	public void setNotifys(Set<Notify> notifys) {
		this.notifys = notifys;
	}
	@Override
	public String toString() {
		return "User [id=" + id + ", email=" + email + ", password=" + password
				+ ", address=" + address + ", realname=" + realname
				+ ", skills=" + skills + ", icon=" + icon + ", theme=" + theme
				+ ", shortname=" + shortname + ", dbconfigs=" + dbconfigs
				+ ", notifys=" + notifys + "]";
	}
}

这两个配置好多对多以后,在用户表中的notifys使用mappedBy,将控制方交给通知实体控制,这时候,进行业务逻辑操作,如下:

@Override
	public void addNotify(String notifyContent,String[] uid) {
		Set<User> userSet = new HashSet<User>();
		User user = null;
		for (String string : uid) {
			try{
				user = userDao.findOne(Long.valueOf(string));
			}catch(Exception e){
				continue;
			}
			userSet.add(user);
		}
		Notify notify = new Notify();
		notify.setContent(notifyContent);
		notify.setUsers(userSet);
		
		notifyDao.save(notify);
	}

此时出现报错信息:

严重: Servlet.service() for servlet springmvc threw exception
org.hibernate.PersistentObjectException: detached entity passed to persist: com.richsoft.middleware.domain.User
	at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:139)
	at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:797)
	at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:790)
	at org.hibernate.jpa.event.internal.core.JpaPersistEventListener$1.cascade(JpaPersistEventListener.java:97)
	at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:352)
	at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:295)
	at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161)
	at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:381)
	at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:321)
	at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:298)
	at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161)
	at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:118)
	at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:460)
	at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:294)
	at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:194)
	at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:125)
	at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:84)
	at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:206)
	at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:149)
	at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:75)
	at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:807)
	at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:780)
	at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:785)
	at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1181)
	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 org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:257)
	at $Proxy26.persist(Unknown Source)
	at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:358)
	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 org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:358)
	at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:343)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
	at $Proxy27.save(Unknown Source)
	at com.richsoft.middleware.core.service.impl.NotifyServiceImpl.addNotify(NotifyServiceImpl.java:66)
	at com.richsoft.middleware.web.controller.NotifyController.sendNotify(NotifyController.java:58)
	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 org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:175)
	at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:446)
	at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:434)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:945)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:931)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:833)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:807)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
	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:103)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:861)
	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:606)
	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
	at java.lang.Thread.run(Thread.java:619)

不难看出来detached entity passed to persist,这句话的意思,就是说需要游离状态的实体需要被持久化,这样,我把通知实体的级联关系改成了:DETACH,问题便解决了,如下:

@ManyToMany(cascade = CascadeType.DETACH, fetch = FetchType.EAGER)
	@JoinTable(name = "notify_user", 
		joinColumns = { @JoinColumn(name = "uid", referencedColumnName = "id") }, 
		inverseJoinColumns = { @JoinColumn(name = "nid", referencedColumnName = "id") })
	private Set<User> users = new HashSet<User>();

虽然问题解决了,但是我们仍然需要了解一下各个级联的意义:

官方文档:

ALL

public static final CascadeType ALL
Cascade all operations
PERSIST

public static final CascadeType PERSIST
Cascade persist operation
MERGE

public static final CascadeType MERGE
Cascade merge operation
REMOVE

public static final CascadeType REMOVE
Cascade remove operation
REFRESH

public static final CascadeType REFRESH
Cascade refresh operation
DETACH

public static final CascadeType DETACH
Cascade detach operation
Since:
Java Persistence 2.0

实际的含义是:

DETACH	若父实体从持久上下中游离(detached)出来,则关联实体也会游离。 

MERGE	若父实体合并(merge)到持久化上下文,那关联实体也会被合并。 

PERSIST	若父实体被持久化(persist)到持久化上下文,那关联实体也会被持久化。 

REFRESH	若当前持久化上下文中父实体刷新(refresh)了,则关联实体也会刷新。 

REMOVE	若复试题从当前持久化上下文中移除(remove),则关联实体也会移除。 

抱歉!评论已关闭.