I've an entity setup that looks like that:
User <- (M:N) -> Project
Project <- (1:n) -> WorkPackage
WorkPackage <- (N:1) -> Category
Using: Hibernate 4.3.4
Now I want to create a new User and assign a project to him with the following settings:
The User has assigned a Project which contains two WorkPackages. Theses WorkPackages refer to the same Category. Project, WorkPackages and Categorys exist in the database.
Project, WorkPackages and Categorys are loaded from the database and transfered to a DTO using org.modelmapper.ModelMapper.
When I try to save the user i get the following error: 4355 [ERROR] UserServiceImpl: org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session : [de.java.appserver.persistence.model.Category#1] null
After 2 days of trying to find a solution I'm almost giving up.
User Entity: (Getter and Setter are implemented!)
@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);
}
}
Project Entity: (Getter and Setter are implemented!)
@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 Entity: (Getter and Setter are implemented!)
@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);
}
}
Category Entity (Getter and Setter are implemented!)
@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 extends 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;
}
Stacktrace from JUnit Test:
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)
Can anyone see a mistake in the code or has a proper solution?
Thanks in advance!