Java 如何在Hibernate中将更新级联到子实体

Java 如何在Hibernate中将更新级联到子实体,java,hibernate,jpa,design-patterns,persistence,Java,Hibernate,Jpa,Design Patterns,Persistence,我有三个实体:凭证、用户和管理员。用户和管理员实体都有一个字段credentials,该字段与使用OneToOne注释的credentials实体相关 当通过entityManager.merge更新现有用户或管理员条目时,我在Credentials.login列上获得了重复的密钥,该列具有唯一约束 @实体 @桌子 公共类凭据{ @身份证 @列(name=“id”,unique=true,nullable=false) @GeneratedValue(策略=GenerationType.IDEN

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

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

@实体
@桌子
公共类凭据{
@身份证
@列(name=“id”,unique=true,nullable=false)
@GeneratedValue(策略=GenerationType.IDENTITY)
私人长id;
@列(unique=true,nullable=false,length=50)
私有字符串登录;
@列(nullable=false,长度=50)
私有字符串密码;
/*********************************************
*这里有接球手和接球手
**********************************************/
}
@实体
@桌子
公共类用户{
@身份证
@列(name=“id”,unique=true,nullable=false)
@GeneratedValue(策略=GenerationType.IDENTITY)
私人长id;
/*********************************************
*此处的特定用户列
**********************************************/
@OneToOne(cascade={CascadeType.ALL})
@JoinColumn(nullable=false,name=“idCredentials”)
私人证书;
/*********************************************
*这里有接球手和接球手
**********************************************/
}
@实体
@桌子
公共类管理员{
@身份证
@列(name=“id”,unique=true,nullable=false)
@GeneratedValue(策略=GenerationType.IDENTITY)
私人长id;
/*********************************************
*这里有特定的管理员栏
**********************************************/
@OneToOne(cascade={CascadeType.ALL})
@JoinColumn(nullable=false,name=“idCredentials”)
私人证书;
/*********************************************
*这里有接球手和接球手
**********************************************/
}
我希望在调用entityManager.merge(用户)后,在相应的数据库表中更新user和user.credentials,但我得到的错误是“login\u UNIQUE”键的“replicate entry'loginname”。管理实体也是如此


提前感谢您的帮助。

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

  • merge
    PERSON
    实体参数的整个状态复制到相应的上下文管理的
    PERSON
    实体中
  • 此副本将在
    PERSON
    参数中包含
    凭证
    实体
  • 由于
    凭证
    实体已分离,因此持久性管理器将假定此实体对应于尚未持久化的实体
  • 刷新会话时,持久化上下文将使用
    插入
    (持久化新的
    凭据
    )而不是
    更新来保存合并的
    凭据
  • INSERT
    将触发您正在获取的
    login
    字段上的重复约束冲突,因为原始
    凭证
    记录与
    INSERT
    中使用的
    login
    值一起存在
  • 编辑:(如何合并)

    如果您没有更新
    个人
    中的
    凭证
    ,那么在您的
    个人(道
    )中合并时,您可以:

  • 在合并之前,暂时删除
    个人的
    凭证
    null
  • 合并
    个人
  • 将原始的
    凭证
    添加回您的新合并的
    人员
  • 由于我无法访问您的
    DAO
    代码,因此这里是前一个的伪代码:

        public PERSON mergeSafely(PERSON person) {
            Credentials originalCredentials = person.getCredentials();
            person.setCredentials(null);
            person = em.merge(person);
            person.setCredentials(originalCredentials);
            return person;
        }
    
    如果您还想合并
    凭证
    ,那么您应该(为了清洁起见)使用
    dao
    层上方的
    服务
    层。在该层中完成此操作的实用程序方法的伪代码如下所示:

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

    希望这能有所帮助。

    感谢您的澄清@Marco R。您的确是对的。我添加了调用
    user.setCredentials(entityManager.merge(user.getCredentials())
    以在调用
    entityManager.merge(user)之前管理user.credentials
    它解决了这个问题。但是我不喜欢这个解决方案,因为它在UserDAO中混合了对CredentialsDAO的调用。有没有更优雅的方法来解决这个问题?我的意思是,有没有一种方法可以一次性保存用户及其凭据?我只是在您的评论中添加了一个问题的答案,作为我原始答案中的编辑。谢谢为了再次回复@Marco R.My person.credentials还需要更新数据,因此我相信
    person.setCredentials(null);
    会导致凭据不会随person一起更新。我已经编辑了答案,以包含最新的考虑因素。