org.hibernate.NonUniqueObjectException:具有相同标识符值的不同对象已与会话关联
Posted
技术标签:
【中文标题】org.hibernate.NonUniqueObjectException:具有相同标识符值的不同对象已与会话关联【英文标题】:org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session 【发布时间】:2014-04-16 19:56:32 【问题描述】:我有一个看起来像这样的实体设置:
User <- (M:N) -> Project
Project <- (1:n) -> WorkPackage
WorkPackage <- (N:1) -> Category
使用:休眠 4.3.4
现在我想创建一个新用户并使用以下设置为他分配一个项目:
用户分配了一个包含两个工作包的项目。这些工作包引用相同的类别。项目、工作包和类别存在于数据库中。
项目、工作包和类别从数据库加载并使用 org.modelmapper.ModelMapper 传输到 DTO。
当我尝试保存用户时,我收到以下错误:4355 [错误] UserServiceImpl::[de.java.appserver。 persistence.model.Category#1] null
在尝试找到解决方案 2 天后,我几乎要放弃了。
用户实体:(实现了Getter和Setter!)
@Entity
@Table
public class User extends AbstractModel
private static final long serialVersionUID = 5668294997295174851L;
@Id
@GenericGenerator(name = "generator", strategy = "increment")
@GeneratedValue(generator = "generator")
@Column(unique = true, nullable = false)
private int userId;
@Column(nullable = false)
private String firstName;
@Column(nullable = false)
private String lastName;
@Column(nullable = false)
private String password;
@Column(unique = true, nullable = false)
private String email;
@ManyToMany
@Cascade( CascadeType.ALL )
private Set<Role> roles;
@ManyToMany
@Cascade( CascadeType.ALL, CascadeType.MERGE )
private Set<Project> projects;
@Column
private String theme;
@OneToOne
@Cascade( CascadeType.ALL )
private Contract contract;
@OneToMany(mappedBy = "calendarEntryUser")
@Cascade( CascadeType.ALL )
private Set<CalendarEntry> calendarEntries;
/**
* Default Constructor. Creates an empty object.
*/
public User()
// nothing to do here!
/**
* Convenience Constructor. Use this constructor to create a new
* @link User object,
*
* @param firstName
* The first name of the user. May not be null.
* @param lastName
* The last name of the user. May not be null.
* @param password
* The password of the user. May not be null.
* @param email
* The email of the user. May not be null.
* @param roles
* The roles assigned to the user
*/
public User(String firstName, String lastName, String password,
String email, Set<Role> roles)
this.firstName = firstName;
this.lastName = lastName;
this.password = password;
this.email = email;
this.roles = roles;
this.theme = "default";
/**
* Uses Guava to assist in providing hash code of this user instance.
*
* @return the hash code.
*/
@Override
public int hashCode()
return com.google.common.base.Objects.hashCode(this.lastName,
this.firstName, this.email, this.password,
this.theme);
项目实体:(实现了Getter和Setter!)
@Entity
@Table
public class Project extends AbstractModel
private static final long serialVersionUID = -8619177706660662830L;
@Id
@GenericGenerator(name = "generator", strategy = "increment")
@GeneratedValue(generator = "generator")
@Column(unique = true, nullable = false)
private int projectId;
@Column(unique = true, nullable = false)
private String name;
@ManyToMany(mappedBy = "projects")
@Cascade(CascadeType.ALL)
private Set<User> projectUsers;
@OneToMany
@Cascade( CascadeType.ALL )
@JoinColumn(name = "project")
private Set<WorkPackage> workPackages;
/**
* Default Constructor. Creates an empty object.
*/
public Project()
// nothing to do here!
/**
* Convenience Constructor. Use this constructor to create a new
* @link Project object.
*
* @param name
* The name of the project. May not be null.
* @param workPackageSet
* Set of @link WorkPackage
* @param userSet
* Set of @link User
*/
public Project(String name, Set<WorkPackage> workPackageSet, Set<User> userSet)
this.name = name;
this.workPackages = workPackageSet;
this.projectUsers = userSet;
/**
* Uses Guava to assist in providing hash code of this user instance.
*
* @return the hash code.
*/
@Override
public int hashCode()
return com.google.common.base.Objects.hashCode(this.name);
WorkPackage 实体:(已实现 Getter 和 Setter!)
@Entity
@Table
public class WorkPackage extends AbstractModel
private static final long serialVersionUID = 6953170627587422231L;
@Id
@GenericGenerator(name = "generator", strategy = "increment")
@GeneratedValue(generator = "generator")
@Column(unique = true, nullable = false)
private int workPackageId;
@Column(nullable = false)
private String name;
@ManyToOne
@Cascade( CascadeType.ALL )
private Project project;
@ManyToOne
@Cascade( CascadeType.SAVE_UPDATE )
@JoinColumn(name = "cat_id")
private Category category;
@OneToMany
@Cascade( CascadeType.ALL )
private Set<CalendarEntry> calendarEntries;
/**
* Default Constructor. Creates an empty object.1
*/
public WorkPackage()
// nothing to do here!
/**
* Convenience Constructor. Use this constructor to create a new
* @link WorkPackage object.
*
* @param name
* The name of the work package. May not be null.
* @param project
* The project assigned to this @link WorkPackage
* @param category
* The category assigned to this @link WorkPackage
*/
public WorkPackage(String name, Project project, Category category)
this.name = name;
this.project = project;
this.category = category;
/**
* Uses Guava to assist in providing hash code of this user instance.
*
* @return the hash code.
*/
@Override
public int hashCode()
return com.google.common.base.Objects.hashCode(this.name, this.workPackageId);
类别实体(Getter 和 Setter 已实现!)
@Entity
@Table
public class Category extends AbstractModel
private static final long serialVersionUID = 7469802197491523844L;
@Id
@GenericGenerator(name = "generator", strategy = "increment")
@GeneratedValue(generator = "generator")
@Column(unique = true, nullable = false)
private int categoryId;
@Column(unique = true, nullable = false)
private String name;
@OneToMany(mappedBy = "category")
@Cascade(CascadeType.SAVE_UPDATE)
private Set<WorkPackage> workPackages;
/**
* Default Constructor. Creates an empty object.
*/
public Category()
// nothing to do here!
/**
* Convenience Constructor. Use this constructor to create a new
* @link Category object.
*
* @param name
* The name of the category. May not be null.
*/
public Category(String name)
this.name = name;
/**
* Uses Guava to assist in providing hash code of this user instance.
*
* @return the hash code.
*/
@Override
public int hashCode()
return com.google.common.base.Objects.hashCode(this.name);
抽象模型:
@Override
public boolean equals(Object object)
boolean isEqual = false;
if (null != object && object.getClass() == this.getClass())
isEqual = object.hashCode() == this.hashCode();
return isEqual;
用户服务:
@Override
public boolean saveUser(UserDto user)
Transaction tx = null;
boolean success = false;
try
tx = HibernateUtil.getSession().beginTransaction();
userDao.saveUser(DtoFactory.INSTANCE.createUser(user));
tx.commit();
success = true;
catch (HibernateException ex)
LOGGER.error(ex + " " + ex.getCause());
tx.rollback();
return success;
UserDao 扩展了 GenericDao:
@Override
public void saveUser(User user)
if (user.getUserId() == 0)
saveOrUpdate(user);
else
merge(user);
通用道:
@Override
public void saveOrUpdate(E element)
HibernateUtil.getSession().saveOrUpdate(element);
DtoFactory createUser():
/**
* Creates a @link User from a @link UserDto.
*
* @param userDto
* The @link UserDto that should be converted
* @return Created @link User
*/
public User createUser(UserDto userDto)
User user = new User();
if (userDto == null)
user = null;
else
// mapper.map(userDto, user);
user = mapper.map(userDto, User.class);
return user;
来自 JUnit 测试的堆栈跟踪:
org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session : [de.java.appserver.persistence.model.Role#1]
at org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:617)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:301)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:244)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:109)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90)
at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:684)
at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:676)
at org.hibernate.engine.spi.CascadingActions$5.cascade(CascadingActions.java:235)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:350)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:293)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161)
at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:379)
at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:319)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:296)
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:137)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:209)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:194)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:114)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90)
at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:684)
at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:676)
at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:671)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.hibernate.context.internal.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:356)
at com.sun.proxy.$Proxy28.saveOrUpdate(Unknown Source)
at de.java.appserver.persistence.dao.impl.GenericDaoImpl.saveOrUpdate(GenericDaoImpl.java:46)
at de.java.appserver.persistence.dao.impl.UserDaoImpl.saveOrUpdate(UserDaoImpl.java:1)
at de.java.appserver.persistence.dao.impl.UserDaoImpl.saveUser(UserDaoImpl.java:29)
at de.java.appserver.service.hibernate.impl.UserServiceImpl.saveUser(UserServiceImpl.java:47)
at de.java.appserver.service.hibernate.UserServiceTest.createNewUser(UserServiceTest.java:202)
at de.java.appserver.service.hibernate.UserServiceTest.testCreateNewUserWithRoleAndProject(UserServiceTest.java:178)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
任何人都可以看到代码中的错误或有适当的解决方案吗?
提前致谢!
【问题讨论】:
你在creatUser(user)
做什么?
我将 DtoFactory 中的 createUser(user) 添加到帖子中。
你能提供一个堆栈跟踪吗?
查看堆栈跟踪的帖子
Hibernate Error: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session的可能重复
【参考方案1】:
基于 hashCode() 的 equals() 实现绝对是问题的根源。首先,您应该有适当的 equals 方法。
【讨论】:
正确的 equals 方法的最佳方法是什么?【参考方案2】:NonUniqueObjectException 表示persistentContext 中具有相同标识符的两个不同对象(有两个对象指向上下文中加载的同一个注册表)并且您正在尝试修改其中一个。
要解决这个问题,您需要找到重复的对象并将其从上下文中分离出来。您可以使用以下方法:
session.evict(duplicatedObject);
【讨论】:
以上是关于org.hibernate.NonUniqueObjectException:具有相同标识符值的不同对象已与会话关联的主要内容,如果未能解决你的问题,请参考以下文章