1

我有三个实体:凭据、用户和管理员。User 和 Admin 实体都有一个字段 credentials,它与使用 OneToOne 注释的 Credentials 实体相关。

通过 entityManager.merge 更新现有的用户或管理员条目时,我在具有唯一约束的 Credentials.login 列上获得了重复的键。

@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
    **********************************************/
}

我希望在调用 entityManager.merge(user) 后在各自的数据库表中更新 user 和 user.credentials,但我收到错误“重复条目 'loginname' for key 'login_UNIQUE。管理实体也会发生同样的情况。

预先感谢您的任何帮助。

4

1 回答 1

1

您可能会收到此错误的原因是由于将merge分离实体的内容复制到托管实体中。因此,如果您将作为参数传递给包含持久存在于数据库中的实体的分离副本的mergeorUser实体Admin(从现在起称为) ;那么你肯定会遇到这个问题。这样做的原因是:PERSONCredentials

  1. merge会将实体参数的整个状态复制PERSON到相应的上下文管理 PERSON实体中。
  2. 此副本将包括参数Credentials内的实体PERSON
  3. 由于Credentials实体已分离,持久性管理器将假定该实体对应于尚未持久化的实体。
  4. 持久化上下文在刷新会话时,将使用 Credentials一个INSERT(持久化新的Credentials)而不是一个UPDATE.
  5. 将触发您正在获取INSERT的字段上的重复约束违规,因为原始记录存在于.loginCredentialsloginINSERT

编辑:(如何合并)

如果您没有更新Credentialsin PERSON,那么在合并时PERSON_DAO,您可以:

  1. 在合并之前暂时CredentialsPERSON( ) 中删除。null
  2. 合并PERSON
  3. 将原始添加Credentials回您新合并的 PERSON

由于我无法访问您的DAO代码,因此这是之前的伪代码:

    public PERSON mergeSafely(PERSON person) {
        Credentials originalCredentials = person.getCredentials();
        person.setCredentials(null);
        person = em.merge(person);
        person.setCredentials(originalCredentials);
        return person;
    }

如果您还想合并Credentials,那么您应该(为了清洁起见)使用service图层上方的dao图层。在该层中完成此操作的实用程序方法的伪代码如下所示:

    public PERSON mergeCompletely(PERSON person) {
        Credentials mergedCredentials = credentialsDAO.merge(person.getCredentials());
        person.setCredentials(mergedCredentials);
        person = personDAO.merge(person);
        return person;
    }

希望这可以帮助。

于 2019-05-16T21:41:33.213 回答