복붙노트

[SPRING] 키 'PRIMARY'에 대한 중복 항목 'string1-string2'

SPRING

키 'PRIMARY'에 대한 중복 항목 'string1-string2'

MySQL 데이터베이스를 통해 hibernate와 jpa를 사용하는 Spring MVC 애플리케이션에서 하위 엔티티를 포함하는 상위 엔티티를 저장할 때마다 하위 엔티티에 대한 다음 오류 메시지가 표시됩니다.

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

여기서 string1 및 string2는 하위 엔터티의 복합 기본 키의 두 부분을 참조합니다. 이 오류를 어떻게 해결합니까?

엔티티 간의 관계가 상위 주소 엔티티에 정의되는 방식은 다음과 같습니다.

@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;

이 링크를 클릭하면 전체 스택 추적을 볼 수 있습니다. 주소 엔티티에 대한 전체 코드는이 링크에서 찾을 수 있습니다.

GeneralCode 엔티티의 전체 코드는이 링크에서 읽을 수 있습니다.

복합 기본 키 클래스의 코드는이 링크에서 찾을 수 있습니다. Address로 확장 된 BaseEntity 클래스는이 링크에서 찾을 수 있습니다.

이 오류 메시지에 대한 많은 게시물을 읽었습니다. 다른 게시물에 대한 응답으로 내 오류 메시지가 해결되지 않으며 일반적으로 내 엔터티가 복합 기본 키를 사용한다는 사실을 설명하지 않습니다.

주소를 유지하는 코드는 다음과 같습니다.

@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;

이 링크를 클릭하면 새 스택 추적을 읽을 수 있습니다.

이 오류를 어떻게 해결할 수 있습니까?

저장 주소 메서드에 다음 코드를 추가하여 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가 위의 코드가 앱이 충돌하기 직전에 실행 중임을 나타내지 만 여전히 동일한 오류가 발생합니다.

이 링크를 클릭하면 결과 스택 추적을 읽을 수 있습니다.

해결법

  1. ==============================

    1.무엇보다도 먼저 해결해야 할 몇 가지 사항이 있습니다.

    무엇보다도 먼저 해결해야 할 몇 가지 사항이 있습니다.

    최신 정보

    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);
        }       
    }
    
  2. ==============================

    2.문제는 CascadeType.ALL 사용 관계에있는 것 같습니다.

    문제는 CascadeType.ALL 사용 관계에있는 것 같습니다.

    무슨 일이야 ?

    해결책 :

    사용 관계에 대한 계단식 규칙을 정의하지 않습니다.

    @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가 아직 데이터베이스에없는 유스 케이스가있는 경우 특히 손으로 관리해야합니다.

    지나치게 단순화 된 솔루션 (문제를 이해할 수 있도록) :

    @Override
    public void savehl7Address(HL7Address addr) {
        if(addr.use() != null && addr.use().getId()==null){
            //HL7GeneralCode is not persistent yet
            this.em.persist(addr.use());
            //since there is a cascade ALL on the adresses relationship addr is now persistent
            return;
        }
        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);}
    }
    

    보시다시피이 솔루션은 확실히 생산 준비가 아닌 최고의 것이 아닙니다. 실제 솔루션은 모든 유스 케이스를 연구하고 그에 따라 계단식 규칙 (사용 및 주소 관계 모두에 따라)을 적용하는 것입니다.

    제 의견으로는 할 수있는 가장 좋은 방법은 savehl7Address를 호출 할 때 HL7GeneralCode가 이미 영속적으로 유지되도록하는 것입니다. 이렇게하면 더 나은 해결책 일 수 있습니다.

    @Override
    public void savehl7Address(HL7Address addr) {
        if(addr.use() != null && addr.use().getId()==null){
            //HL7GeneralCode is not persistent. We don't support that
            throw new IllegalStateException("Cannot persist an adress using a non persistent HL7GeneralCode");
        }
        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);}
    }
    
  3. from https://stackoverflow.com/questions/25316761/duplicate-entry-string1-string2-for-key-primary by cc-by-sa and MIT license