Hibernate Error : 동일한 식별자 값을 가진 다른 개체가 이미 세션과 연결되었습니다.


94

기본적으로이 구성에 몇 가지 개체가 있습니다 (실제 데이터 모델은 조금 더 복잡합니다).

  • A는 B와 다 대다 관계를 가지고 있습니다. (B는 inverse="true")
  • B는 C와 다 대일 관계를 가지고 있습니다. (로 cascade설정했습니다 "save-update")
  • C는 유형 / 카테고리 테이블의 일종입니다.

또한 기본 키는 저장시 데이터베이스에 의해 생성된다는 점을 언급해야합니다.

내 데이터로 A에 다른 B 개체 집합이 있고 이러한 B 개체가 동일한 C 개체를 참조하는 문제가 가끔 발생합니다.

을 호출 session.saveOrUpdate(myAObject)하면 다음과 같은 최대 절전 모드 오류가 발생 "a different object with the same identifier value was already associated with the session: C"합니다. 최대 절전 모드가 동일한 세션에서 동일한 객체를 두 번 삽입 / 업데이트 / 삭제할 수 없다는 것을 알고 있지만이 문제를 해결할 수있는 방법이 있습니까? 이것은 드문 상황이 아닌 것 같습니다.

이 문제를 조사하는 동안 사람들이를 사용하는 것을 제안하는 것을 보았습니다 session.merge().하지만 그렇게하면 "충돌하는"개체가 모든 값이 null로 설정된 빈 개체로 데이터베이스에 삽입됩니다. 분명히 그것은 우리가 원하는 것이 아닙니다.

[편집] 내가 언급하는 것을 잊은 또 다른 점은 (내가 통제 할 수없는 구조적인 이유로) 각 읽기 또는 쓰기는 별도의 세션에서 수행되어야한다는 것입니다.


답변이 도움 이되는지 확인하십시오 .
joaonlima 2013-04-26

답변:


98

아마도 B 객체가 동일한 Java C 객체 인스턴스를 참조하지 않기 때문일 것입니다. 데이터베이스에서 동일한 행 (즉, 동일한 기본 키)을 참조하지만 다른 사본입니다.

그래서 일어나고있는 것은 엔티티를 관리하는 Hibernate 세션이 동일한 기본 키를 가진 행에 해당하는 Java 객체를 추적하는 것입니다.

한 가지 옵션은 동일한 행을 참조하는 개체 B의 엔터티가 실제로 C의 동일한 개체 인스턴스를 참조하는지 확인하는 것입니다. 또는 해당 멤버 변수에 대해 계단식 연결을 해제합니다. 이렇게하면 B가 지속될 때 C는 그렇지 않습니다. 하지만 C를 수동으로 별도로 저장해야합니다. C가 유형 / 범주 테이블 인 경우 그 방법이 이치에 맞을 것입니다.


3
감사합니다 jbx. 말했듯이 B 객체는 메모리의 여러 C 인스턴스를 참조하고 있습니다. 본질적으로 일어나는 일은 내 프로그램의 한 부분이 C로 읽고 B에 연결하는 것입니다. 또 다른 부분은 데이터베이스에서 동일한 C로 다른 B를로드하는 것입니다. 둘 다 A에 연결되어 저장시 오류가 발생합니다. B-> C 관계에 대해 <pre> 캐스케이드 </ pre>를 "<pre> none </ pre>"으로 설정했지만 여전히 동일한 오류가 발생합니다. 다 대일 또는 일대 다 관계에서 Hibernate에게 외래 키만 변경하고 나머지는 걱정하지 않도록 지시하는 방법이 있습니까?
John

1
C의 기본 키에 ID 생성 전략이 있습니까? 시퀀스 생성기 또는 이와 유사한 것?
jbx 2013

예, 각 데이터베이스에는 고유 한 시퀀스가 ​​있습니다. 언급했듯이 캐스 케이 딩이 문제로 밝혀졌습니다. 유형 테이블에 대해 캐스케이드를 해제하고 다른 테이블에 대해서는 "merge"캐스케이드를 사용하여 모든 null 행을 생성하지 않고 merge ()를 호출 할 수 있도록했습니다. 그에 따라 답변을 표시했습니다. 감사합니다!
John

14
saveOrUpdate () 및 BOOM 대신 merge ()를 사용했습니다! 그것은 : 작동
Lahiru Ruhunage에게


13

한 가지만하면됩니다. session_object.clear()새 개체를 실행 하고 저장합니다. 이렇게하면 세션 (적절한 이름)이 지워지고 세션에서 문제가되는 중복 개체가 제거됩니다.


10

@Hemant Kumar에 동의합니다. 대단히 감사합니다. 그의 해결책에 따라 나는 내 문제를 해결했습니다.

예 :

@Test
public void testSavePerson() {
    try (Session session = sessionFactory.openSession()) {
        Transaction tx = session.beginTransaction();
        Person person1 = new Person();
        Person person2 = new Person();
        person1.setName("222");
        person2.setName("111");
        session.save(person1);
        session.save(person2);
        tx.commit();
    }
}

Person.java

public class Person {
    private int id;
    private String name;

    @Id
    @Column(name = "id")
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Basic
    @Column(name = "name")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

이 코드는 항상 내 응용 프로그램에서 실수를합니다. A different object with the same identifier value was already associated with the session, 나중에 기본 키자동 으로 늘리는 것을 잊었 음을 알게되었습니다 !

내 해결책은 기본 키에이 코드를 추가하는 것입니다.

@GeneratedValue(strategy = GenerationType.AUTO)

6

즉, 동일한 개체에 대한 참조를 사용하여 테이블에 여러 행을 저장하려고합니다.

엔티티 클래스의 id 속성을 확인하십시오.

@Id
private Integer id;

...에

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(unique = true, nullable = false)
private Integer id;

1
질문이 아닌 경우 주어진 설명에 따라
Sudip 반 다리를

5

다음을 사용하여 Hibernate에서 데이터베이스로 개체 ID를 할당하는 작업을 전송합니다.

<generator class="native"/>

이것은 나를 위해 문제를 해결했습니다.



3

위의 문제를 해결하는 한 가지 방법은 hashcode().
또한 저장 전후에 최대 절전 모드 세션을 플러시하십시오.

getHibernateTemplate().flush();

분리 된 개체를로 명시 적으로 설정하는 null것도 도움이됩니다.


2

이 메시지를 보았지만 C # 코드입니다. 관련성이 있는지 확실하지 않습니다 (정확히 동일한 오류 메시지).

중단 점을 사용하여 코드를 디버깅하고 디버거가 중단 점에있는 동안 개인 멤버를 통해 일부 컬렉션을 확장했습니다. 구조를 파헤 치지 않고 코드를 다시 실행하면 오류 메시지가 사라졌습니다. 개인 지연로드 컬렉션을 조사하는 행위로 인해 NHibernate가 그 당시에로드되지 않았던 것들을로드하게 된 것 같습니다 (개인 멤버에 있었기 때문에).

코드 자체는 해당 트랜잭션 (가져 오기 프로세스)의 일부로 많은 레코드와 많은 종속성을 업데이트 할 수있는 매우 복잡한 트랜잭션으로 래핑됩니다.

문제를 접한 다른 사람에게 단서가되기를 바랍니다.


2

Hibernate에서 "Cascade"속성을 찾아 삭제하십시오. "Cascade"를 사용 가능하게 설정하면 관련 클래스와 관계가있는 다른 엔티티에 대해 다른 작업 (저장, 업데이트 및 삭제)을 호출합니다. 따라서 동일한 ID 값이 발생합니다. 저와 함께 일했습니다.


1

며칠 동안이 오류가 발생 했으며이 오류를 수정하는 데 너무 많은 시간을 보냈습니다.

 public boolean save(OrderHeader header) {
    Session session = sessionFactory.openSession();


    Transaction transaction = session.beginTransaction();

    try {
        session.save(header);

        for (OrderDetail detail : header.getDetails()) {
            session.save(detail);
        }

        transaction.commit();
        session.close();

        return true;
    } catch (HibernateException exception) {

        exception.printStackTrace();
        transaction.rollback();
        return false;
    }
}

이 오류가 발생하기 전에 OrderDetil 개체에 ID 생성 유형을 언급하지 않았습니다. Orderdetails의 id를 생성하지 않으면 모든 OrderDetail 개체에 대해 Id를 0으로 유지합니다. 이것은 #jbx가 설명한 것입니다. 네, 최선의 답변입니다. 이것이 어떻게 일어나는지 한 가지 예입니다.


1

전에 쿼리 코드를 넣으십시오. 그것은 내 문제를 해결합니다. 예를 들어 이것을 변경하십시오.

query1 
query2 - get the error 
update

이에:

query2
query1
update

0

업데이트 쿼리를 호출하기 전에 개체의 식별자를 설정하지 않을 수 있습니다.


3
그가 아니었다면 그는이 문제가 없을 것입니다. 문제는 동일한 식별자를 가진 두 개의 개체가 있다는 것입니다.
aalku

0

다음과 같은 행을 삽입 할 때 기본 키 생성이 잘못되어 문제가 발생했습니다.

public void addTerminal(String typeOfDevice,Map<Byte,Integer> map) {
        // TODO Auto-generated method stub
        try {
            Set<Byte> keySet = map.keySet();
            for (Byte byte1 : keySet) {
                Device device=new Device();
                device.setNumDevice(DeviceCount.map.get(byte1));
                device.setTimestamp(System.currentTimeMillis());
                device.setTypeDevice(byte1);
                this.getHibernateTemplate().save(device);
            }
            System.out.println("hah");
        }catch (Exception e) {
            // TODO: handle exception
            logger.warn("wrong");
            logger.warn(e.getStackTrace()+e.getMessage());
        }
}

ID 생성기 클래스를 ID로 변경합니다.

<id name="id" type="int">
    <column name="id" />
    <generator class="identity"  />
 </id>

0

제 경우에는 flush () 만 작동하지 않았습니다. flush () 후에 clear ()를 사용해야했습니다.

public Object merge(final Object detachedInstance)
    {
        this.getHibernateTemplate().flush();
        this.getHibernateTemplate().clear();
        try
        {
            this.getHibernateTemplate().evict(detachedInstance);
        }
}


0

내 IDE에서 표현식 탭을 열어두면이 예외를 일으키는 개체에 대한 최대 절전 모드 호출을 가져옵니다. 이 같은 개체를 삭제하려고했습니다. 또한이 오류가 발생하는 데 필요한 것으로 보이는 삭제 호출에 중단 점이 있습니다. 다른 표현식 탭을 전면 탭으로 만들거나 ide가 중단 점에서 멈추지 않도록 설정을 변경하여이 문제를 해결했습니다.


0

엔터티가 매핑 된 모든 엔터티와 동일한 세대 유형을 가지고 있는지 확인하십시오.

예 : UserRole

public class UserRole extends AbstractDomain {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String longName;

private String shortName;

@Enumerated(EnumType.STRING)
private CommonStatus status;

private String roleCode;

private Long level;

@Column(columnDefinition = "integer default 0")
private Integer subRoleCount;

private String modification;

@ManyToOne(fetch = FetchType.LAZY)
private TypeOfUsers licenseType;

}

모듈 :

public class Modules implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String longName;

private String shortName;

}

매핑을 사용하는 주요 엔터티

public class RoleModules implements Serializable{

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private UserRole role;

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private Modules modules;

@Type(type = "yes_no")
private boolean isPrimaryModule;

public boolean getIsPrimaryModule() {
    return isPrimaryModule;
}

}


0

이전의 모든 답변 외에도 클래스에 Value Object를 사용하여 VO Transformer 클래스에서 id 속성을 설정하지 않은 경우 대규모 프로젝트에서이 문제를 해결할 수 있습니다.


0

현재 트랜잭션을 커밋하십시오.

currentSession.getTransaction().commit();

이제 다른 트랜잭션을 시작하고 엔티티에 대해 무엇이든 할 수 있습니다.


0

동일한 오류 메시지가 생성 될 수있는 또 다른 경우, custom allocationSize:

@Id
@Column(name = "idpar")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "paramsSequence")
@SequenceGenerator(name = "paramsSequence", sequenceName = "par_idpar_seq", allocationSize = 20)
private Long id;

일치하지 않고

alter sequence par_idpar_seq increment 20;

삽입하는 동안 (이해하기 쉬운) 제약 조건 유효성 검사 또는 "동일한 식별자 값을 가진 다른 개체가 이미 세션과 연결되었습니다"-이 경우는 덜 명확합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.