我的实体设置如下:
User <- (M:N) -> Project
Project <- (1:n) -> WorkPackage
WorkPackage <- (N:1) -> Category
使用:Hibernate 4.3.4
现在我想创建一个新用户并使用以下设置为他分配项目:
用户已分配了一个包含两个WorkPackage的项目。这些WorkPackages指的是同一类别。 Project,WorkPackages和Categorys存在于数据库中。
Project,WorkPackages和Categorys从数据库加载并使用org.modelmapper.ModelMapper转移到DTO。
当我尝试保存用户时,我收到以下错误:4355 [ERROR] UserServiceImpl:org.hibernate.NonUniqueObjectException:具有相同标识符值的另一个对象已与会话关联:[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);
}
}
AbstractModel:
@Override
public boolean equals(Object object) {
boolean isEqual = false;
if (null != object && object.getClass() == this.getClass()) {
isEqual = object.hashCode() == this.hashCode();
}
return isEqual;
}
UserService:
@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);
}
}
GenericDao:
@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 Test的Stacktrace:
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)
任何人都可以在代码中看到错误或有适当的解决方案吗?
提前致谢!
答案 0 :(得分:3)
基于hashCode()的equals()实现肯定是问题的根源。首先你应该有适当的equals方法。
答案 1 :(得分:0)
NonUniqueObjectException意味着持久化上下文中的两个不同对象具有相同的标识符(有两个对象指向上下文中加载的同一注册表),并且您正在尝试修改其中一个。
要解决此问题,您需要找到重复的对象并将其与上下文分离。您可以使用以下方法进行操作:
session.evict(duplicatedObject);