java - key 'string1-string2' 的重复条目 'PRIMARY'

标签 java spring hibernate jpa hibernate-mapping

在通过 MySQL 数据库使用 hibernate 和 jpa 的 Spring MVC 应用程序中,每当我尝试保存包含子实体的父实体时,我都会收到有关子实体的以下错误消息:

Duplicate entry 'string1-string2' for key 'PRIMARY'  

这里,string1string2指的是子实体的复合主键的两部分。 如何解决此错误?

这是在父 Address 实体中定义实体之间关系的方式:

@ManyToOne(cascade = { CascadeType.ALL }, fetch=FetchType.EAGER)
@JoinColumns({ @JoinColumn(name = "usecode", referencedColumnName = "code", insertable = false, updatable = false),
        @JoinColumn(name = "usecodesystem", referencedColumnName = "codesystem", insertable = false, updatable = false)
})
public HL7GeneralCode use;

这是在子 GeneralCode 实体中定义关系的方式:

@OneToMany(mappedBy = "use", cascade = {CascadeType.ALL})
private Set<HL7Address> addresses;

可以查看完整的堆栈跟踪by clicking on this link .
Address 实体的完整代码可以在 at this link 中找到。 .

GeneralCode实体的完整代码可以阅读at this link .

可以找到复合主键类的代码at this link .
并且可以找到由Address 扩展的BaseEntityat this link .

我已经阅读了很多关于此错误消息的帖子。其他帖子的答案没有解决我的错误消息,而且它们通常没有解决我的实体使用复合主键的事实。


编辑:

持久化地址的代码是:

@Override
public void savehl7Address(HL7Address addr) {
    if ((Integer)addr.getId() == null) {
        System.out.println("[[[[[[[[[[[[ about to persist address ]]]]]]]]]]]]]]]]]]]]");
        this.em.persist(addr);}
    else {
        System.out.println("]]]]]]]]]]]]]]]]]] about to merge address [[[[[[[[[[[[[[[[[[[[[");
        this.em.merge(addr);}
}

第二次编辑:

我尝试遵循@Ben75 的建议,但代码在 this.em.persist(addr.getUse()); 行崩溃。请注意,他的 if 子句不适合我的实际对象模型,因此我将下面的 if 子句更改为 if(addr.getUse() != null && addr.getId()==null)。这是我的代码。

@Override
public void savehl7Address(HL7Address addr) {
    if(addr.getUse() != null && addr.getId()==null){
        //this next line prints in the stack trace right before the app crashes
        System.out.println("about to this.em.persist(addr.getUse());");
        //HL7GeneralCode is not persistent yet
        this.em.persist(addr.getUse());
        //since there is a cascade ALL on the adresses relationship addr is now persistent
        return;
    }
    System.out.println("=========================== inside jpahl7patientrespository.savehl7Address(addr)");
    if ((Integer)addr.getId() == null) {
        System.out.println("[[[[[[[[[[[[ about to persist address ]]]]]]]]]]]]]]]]]]]]");
        this.em.persist(addr);}
    else {
        System.out.println("]]]]]]]]]]]]]]]]]] about to merge address [[[[[[[[[[[[[[[[[[[[[");
        this.em.merge(addr);}
}

HL7Address 的相关部分现在是:

@ManyToOne(fetch=FetchType.EAGER)
@JoinColumns({ @JoinColumn(name = "usecode", referencedColumnName = "code", insertable = false, updatable = false),
        @JoinColumn(name = "usecodesystem", referencedColumnName = "codesystem", insertable = false, updatable = false)
})
public HL7GeneralCode use;

HL7GeneralCode 的相关部分现在是:

@OneToMany(mappedBy = "use")
private Set<HL7Address> addresses;

可以读取新的堆栈跟踪 by clicking on this link .

我该如何解决这个错误?


第三次编辑:

我听从了 ben75 的建议,将以下代码添加到保存地址方法中:

if(addr.getUse() != null && !this.em.contains(addr.getUse())){
    System.out.println("about to this.em.persist(addr.getUse());");
    this.em.persist(addr.getUse());return;
}

不幸的是,尽管堆栈跟踪 SYSO 表明上述代码恰好在应用程序崩溃之前正在运行,但我仍然遇到相同的错误。

您可以阅读生成的堆栈跟踪 by clicking on this link .

最佳答案

首先要清除一些东西:

  1. HL7GeneralCode(父级)和 HL7Address(子级)之间存在双向关联。如果 HL7GeneralCode.addresses 是“反面”(mappedBy),那么为什么拥有方 HL7Address.use 具有可插入/可更新的错误?拥有方应控制此关联,因此您应该删除 insertable/updatable=false 标志。

  2. 从父级级联到子级总是有意义的,而不是相反。但是在您的用例中,您尝试保留 Child 并自动保留 Parent 。这就是为什么 CASCADE.ALL 在多对一端没有意义。

  3. 使用双向关联时,双方都必须设置:

    HL7Address addr = new HL7Address();
    HL7GeneralCode code = new HL7GeneralCode();
    ...
    code.getAddresses().add(addr);
    addr.setUse(code); 
    
  4. 持久化操作旨在插入临时实体,永远不会合并它们或重新附加实体。这意味着当您调用服务方法时,HL7Address 和 HL7GeneralCode 都是新实体。如果您已经保存了具有相同 ID 的 HL7GeneralCode,您将获得违反主键约束的异常。

  5. 如果 HL7GeneralCode 可能存在,那么您应该从数据库中获取它。

    HL7GeneralCode code = em.find(HL7GeneralCode, pk);
    HL7Address addr = new HL7Address();
    if(code != null) {
       code = new HL7GeneralCode();
       em.persist(code);    
    }
    code.getAddresses().add(addr);
    addr.setUse(code);            
    em.persist(addr);
    

更新

  1. HL7Address 地址不会覆盖 equals/hashCode,因此应用默认对象相同引用检查规则。这将确保我们可以从 code.addresses 列表中添加/删除地址。如果您稍后改变主意,请确保您 implement equals and hashCode properly .

  2. 虽然与您的问题无关,但您可能希望使用 getter/setter 而不是公开您的字段。这提供了更好的封装,您将避免将 setter 与公共(public)字段访问混合使用。

savehl7Address 方法:

@Override
public void savehl7Address(HL7Address addr) {
    HL7GeneralCode code = addr.use();
    if(code != null && code.getId()==null){
    //HL7GeneralCode is not persistent. We don't support that
        throw new IllegalStateException("Cannot persist an adress using a non persistent HL7GeneralCode");
       //In case you'd want to support it
       //code = em.find(HL7GeneralCode, code.getId());
    }
    //Merge the code without any address info        
    //This will ensure we only reattach the code without triggering the address 
    //transitive persistence by reachability
    addr.setUse(null);
    code.getAddresses().remove(addr);
    code = em.merge(code); 

    //Now set the code to the address and vice-versa  
    addr.setUse(code);
    code.getAddresses().add(addr);

    if ((Integer)addr.getId() == null) {
        System.out.println("[[[[[[[[[[[[ about to persist address ]]]]]]]]]]]]]]]]]]]]");
        em.persist(addr);
    }
    else {
        System.out.println("]]]]]]]]]]]]]]]]]] about to merge address [[[[[[[[[[[[[[[[[[[[[");
        addr = em.merge(addr);
    }       
}

关于java - key 'string1-string2' 的重复条目 'PRIMARY',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25316761/

相关文章:

java - 造成这种无限循环的原因是什么?

java - Spring 的 SimpleNamingContextBuilder 和 LDAP

java - 有没有办法根据 Activity 配置文件不启动 spring boot 应用程序?

mysql - 何时使用 Lucene/Hibernate Search

java - 列不能为空无法插入

java - 在 Spring Controller 中使用日期参数的最佳实践?

java - Apache HttpClient 持久连接使用

java - 我希望球知道它们位于 JPanel 中的矩形内

java - SessionManagementFilter 从不调用 SessionAuthenticationStrategy

java - 失败 : alter table add constraint: Table doesn't exist on create