【问题标题】:How to cascade update to child entity in Hibernate如何在 Hibernate 中级联更新到子实体
【发布时间】:2019-05-16 19:37:19
【问题描述】:

我有三个实体:凭据、用户和管理员。 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.credentials,但我收到错误“重复条目 'loginname' 键'login_UNIQUE。管理实体也会发生同样的情况。

提前感谢您的帮助。

【问题讨论】:

    标签: java hibernate jpa design-patterns persistence


    【解决方案1】:

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

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

    编辑:(如何合并)

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

    1. 在合并之前暂时从PERSON (null) 中删除Credentials
    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,那么您应该(为了简洁起见)在dao 层上方使用service 层。在该层中完成此操作的实用方法的伪代码如下所示:

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

    希望这会有所帮助。

    【讨论】:

    • 感谢您的澄清@Marco R。您确实是对的。我在调用entityManager.merge(user) 之前添加了调用user.setCredentials(entityManager.merge(user.getCredentials())) 来管理user.credentials,它解决了问题。但是我不喜欢这个解决方案,因为它在 UserDAO 中混合了对 CredentialsDAO 的调用。有没有更优雅的方法来解决问题?我的意思是,有没有办法一次性保存用户及其凭据?
    • 我刚刚在您的评论中添加了对问题的答案,作为对我原始答案的修改。
    • 感谢您再次回复@Marco R。我的 person.credentials 也有数据要更新,所以我相信 person.setCredentials(null); 会导致凭据不与 person 一起更新。
    • 我已经编辑了答案以包含最新的考虑因素。
    猜你喜欢
    • 1970-01-01
    • 2017-09-25
    • 1970-01-01
    • 1970-01-01
    • 2014-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多