Possible Duplicate of Update Vs Merge
Whats happening here is:
Edit Mode :
List<UserRole> roles = entity.getRoles(); //Gets Existing Roles from DB
for(UserRole r : roles) {
Em.get().remove(r); //Removes Roles to existing user
}
roles.clear(); // Clean up local memory
for(RoleEnum r : selectedRoles) { // User Input Roles
UserRole role = new UserRole(entity, r); // New Entity with existing user
Em.get().persist(role); // Role Entity Referenced to existing user object, saved
}
Em.get().merge(entity); // ?? No Need in edit unless roles are stored in user table
Em.get().flush();
New User Mode :
List<UserRole> roles = entity.getRoles(); // New Detached User Entity Roles
for(UserRole r : roles) { // Probably Empty Roles Array
Em.get().remove(r); // Removed roles
}
roles.clear(); // Clean up Memory
for(RoleEnum r : selectedRoles) { // Copy from App Roles
UserRole role = new UserRole(entity, r); //Create new role
Em.get().persist(role); //Save Role to DB
}
Em.get().merge(entity); // Trying to merge non existing Entity <-- This is where error appears
Em.get().flush();
The persist method works because it has decides when to use insert or update command. Since new user entity has no ID set to it, it has no idea what to do with it, while it may have worked in past, actual behavior of mergig is very well explain in this thread merging a detached or new entity with an existing entity in hibernate/jpa best practice question
See for yourself :
If your entity is a detached entity the only thing u really need to do
is to invoke entityManager.merge(user). You dont need to exec any
finder method. If your entity is not detached but rather new (it does
not have id specified) you should find appropriate entity in the
database prior performing any modification operations on that entity
and merge it afterwards.
Another detailed reference is given here : persist() and merge() in JPA and Hibernate
Here is the reference from docs :
Serializable
save(Object object) throws HibernateException
Persist the given transient instance, first assigning a generated identifier. (Or using the current value of the identifier property if the assigned generator is used.) This operation cascades to associated instances if the association is mapped with cascade="save-update".
Parameters:
object - a transient instance of a persistent class
Returns:
the generated identifier
Throws:
HibernateException
persist
void persist(String entityName,
Object object)
throws HibernateException
Make a transient instance persistent. This operation cascades to associated instances if the association is mapped with cascade="persist".
The semantics of this method are defined by JSR-220.
Parameters:
object - a transient instance to be made persistent
Throws:
HibernateException
merge
Object merge(String entityName,
Object object)
throws HibernateException
Copy the state of the given object onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance. If the given instance is unsaved, save a copy of and return it as a newly persistent instance. The given instance does not become associated with the session. This operation cascades to associated instances if the association is mapped with cascade="merge".
The semantics of this method are defined by JSR-220.
Parameters:
object - a detached instance with state to be copied
Returns:
an updated persistent instance
Throws:
HibernateException
save() and persist() result in an SQL INSERT, delete() in an SQL
DELETE and update() or merge() in an SQL UPDATE. Changes to persistent
instances are detected at flush time and also result in an SQL UPDATE.
saveOrUpdate() and replicate() result in either an INSERT or an
UPDATE.
Conclusion: Functions are behaving as they are intended.