I have three entities: Credentials, User and Admin. Both User and Admin entities have a field credentials, which is related to Credentials entity using OneToOne annotation.
When updating an existing User or Admin entry, through entityManager.merge, I am getting duplicated key over Credentials.login column, which has unique constraint.
@Entity
@Table
public class Credentials {
@Id
@Column(name="id", unique=true, nullable=false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique=true, nullable=false, length=50)
private String login;
@Column(nullable=false, length=50)
private String password;
/*********************************************
* getters and setters here
**********************************************/
}
@Entity
@Table
public class User{
@Id
@Column(name="id", unique=true, nullable=false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/*********************************************
* Specific user columns here
**********************************************/
@OneToOne(cascade = {CascadeType.ALL})
@JoinColumn(nullable=false, name = "idCredentials")
private Credentials credentials;
/*********************************************
* getters and setters here
**********************************************/
}
@Entity
@Table
public class Admin{
@Id
@Column(name="id", unique=true, nullable=false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/*********************************************
* Specific admin columns here
**********************************************/
@OneToOne(cascade = {CascadeType.ALL})
@JoinColumn(nullable=false, name = "idCredentials")
private Credentials credentials;
/*********************************************
* getters and setters here
**********************************************/
}
I expect to have user and user.credentials updated in respective database tables after calling entityManager.merge(user), but I am getting the error "Duplicate entry 'loginname' for key 'login_UNIQUE. The same happens with Admin entity.
Thank you in advance for any help.
The reason you may be getting this error is due to the fact that merge
copies the contents of a detached entity into a managed entity. So, if you are passing as an argument to merge
a User
or Admin
entity (from now on referred as PERSON
) that contains a detached copy of a Credentials
entity persisted in the DB; then you will definitely be getting this problem. The reason for this is:
merge
will copy the entire state of thePERSON
entity argument into the corresponding context managedPERSON
entity.- This copy will include the
Credentials
entity inside thePERSON
argument. - Since the
Credentials
entity was detached, the persistence manager will assume this entity corresponds to an entity not persisted yet. - The persistence context, when flushing the session, will save the merged
Credentials
using anINSERT
(persist the newCredentials
) rather than anUPDATE
. - The
INSERT
will trigger the duplicate constrain violation on thelogin
field you are getting, because the originalCredentials
record exist with thelogin
value being used in theINSERT
.
EDIT: (How to merge)
If you are not updating the Credentials
in PERSON
, then when merging in your PERSON_DAO
, you could:
- Temporarily remove
Credentials
fromPERSON
(null
) before merging. - Merge
PERSON
. - Add the original
Credentials
back to your newly mergedPERSON
Since I have no access to your DAO
code, here is a pseudo code of the previous:
public PERSON mergeSafely(PERSON person) {
Credentials originalCredentials = person.getCredentials();
person.setCredentials(null);
person = em.merge(person);
person.setCredentials(originalCredentials);
return person;
}
In case you want to merge also the Credentials
, then you should (for cleanness sake) use the service
layer above the dao
layer. A pseudo code of a utility method to accomplish this in that layer would look like:
public PERSON mergeCompletely(PERSON person) {
Credentials mergedCredentials = credentialsDAO.merge(person.getCredentials());
person.setCredentials(mergedCredentials);
person = personDAO.merge(person);
return person;
}
Hope this helps.