최대 절전 모드 오류 : org.hibernate.NonUniqueObjectException : 동일한 식별자 값을 가진 다른 개체가 이미 세션과 연결되었습니다.


114

두 개의 사용자 개체가 있고 사용하여 개체를 저장하려고하는 동안

session.save(userObj);

다음과 같은 오류가 발생합니다.

Caused by: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session:
[com.pojo.rtrequests.User#com.pojo.rtrequests.User@d079b40b]

나는 사용하여 세션을 만들고 있습니다

BaseHibernateDAO dao = new BaseHibernateDAO();          

rtsession = dao.getSession(userData.getRegion(),
                           BaseHibernateDAO.RTREQUESTS_DATABASE_NAME);

rttrans = rtsession.beginTransaction();
rttrans.begin();

rtsession.save(userObj1);
rtsession.save(userObj2);

rtsession.flush();
rttrans.commit();

rtsession.close(); // in finally block

나는 또한 session.clear()저장하기 전에 시도했지만 여전히 운이 없습니다.

이것은 사용자 요청이 올 때 처음으로 세션 개체를 얻는 것이므로 개체가 세션에 존재한다고 말하는 이유를 얻고 있습니다.

어떤 제안?


다음은 내 문제를 해결하는 데 도움이 된 또 다른 멋진 스레드입니다. getj2ee.over-blog.com/…
Reddymails

답변:


173

이 오류가 여러 번 발생했으며 추적하기가 매우 어려울 수 있습니다.

기본적으로 hibernate가 말하는 것은 동일한 식별자 (동일한 기본 키)를 가진 두 개의 개체가 있지만 동일한 개체가 아니라는 것입니다.

나는 당신이 당신의 코드를 쪼개는 것을 제안 할 것이다. 즉, 오류가 사라질 때까지 비트를 주석으로 처리 한 다음 코드가 돌아올 때까지 코드를 다시 넣으면 오류를 찾을 수있다.

대부분의 경우 개체 A와 B 사이에 연속 저장이 있지만 개체 B가 이미 세션과 연결되어 있지만 A에있는 것과 동일한 B 인스턴스에 있지 않은 경우 연속 저장을 통해 발생합니다.

어떤 기본 키 생성기를 사용하고 있습니까?

내가 묻는 이유는이 오류가 객체의 지속 상태 (즉, 객체가 지속되는지 여부)를 확인하기 위해 최대 절전 모드에 지시하는 방법과 관련이 있기 때문입니다. 최대 절전 모드가 이미 영구적 인 개체를 유지하려고하기 때문에 오류가 발생할 수 있습니다. 실제로 save hibernate를 사용하면 해당 객체를 시도하고 유지하며 세션과 관련된 동일한 기본 키를 가진 객체가 이미있을 수 있습니다.

기본 키 조합 (열 1 및 열 2)을 기반으로 행이 10 개인 테이블에 대해 최대 절전 모드 클래스 개체가 있다고 가정합니다. 이제 특정 시점에 테이블에서 5 개의 행을 제거했습니다. 이제 동일한 10 개의 행을 다시 추가하려고 시도하는 동안 hibernate가 데이터베이스에서 개체를 유지하려고 시도하면 이미 제거 된 5 개의 행이 오류없이 추가됩니다. 이제 이미 존재하는 나머지 5 개 행은이 예외를 발생시킵니다.

따라서 쉬운 접근 방식은 무언가의 일부인 테이블에서 값을 업데이트 / 제거했는지 확인하고 나중에 동일한 객체를 다시 삽입하려고 시도하는지 확인하는 것입니다.


4
좋은 답변 ². 기본 키는 내 문제였으며, Postgresql에 대한 시퀀스를 설정하는 GeneratedValue로 해결되었습니다.
Rodrigo Ferrari

2
나는 같은 문제가 있었다. 제 경우에는 코드에서 개체를 검색했고 첫 번째 개체가 아직 최대 절전 모드에있는 동안 다른 코드에서 동일한 ID를 가진 새 개체를 만들려고했습니다.
dellasavia

18

이것은 최대 절전 모드가 해결하는 것보다 더 많은 문제를 일으키는 유일한 지점입니다. 제 경우에는 동일한 식별자 0을 가진 많은 객체가 있습니다. 왜냐하면 새롭고 하나도 없기 때문입니다. db가 생성합니다. 어딘가에 0 신호 Id가 설정되지 않았다는 것을 읽었습니다. 그것들을 지속시키는 직관적 인 방법은 그것들을 반복하고 객체를 저장하기 위해 최대 절전 모드라고 말하는 것입니다. 하지만 그렇게 할 수는 없습니다. "물론 최대 절전 모드가 이런 식으로 작동한다는 것을 알아야합니다. 따라서해야합니다."그래서 이제 Ids를 long 대신 Long으로 변경하여 작동하는지 살펴볼 수 있습니다. 결국에는 최대 절전 모드가 추가 불투명 부담 일 뿐이므로 간단한 매퍼로 직접 수행하는 것이 더 쉽습니다. 또 다른 예 : 한 데이터베이스에서 매개 변수를 읽고 다른 데이터베이스에 유지하려고하면 거의 모든 작업을 수동으로해야합니다.


13
저도 최대 절전 모드가 싫습니다 ... 데이터베이스 ... 문제를 일으킨 모든 문제가 발생한 후에도 텍스트 파일을 사용하는 것이 더 쉬울 것 같습니다 (농담이지만 ​​여전히 ...).
Igor Popov

제 경우에는 동일한 식별자 0을 가진 많은 객체가 있습니다. 왜냐하면 새롭고 하나도 없기 때문입니다. db가 생성합니다. 어딘가에 0 신호 Id가 설정되지 않았다는 것을 읽었습니다. 그것들을 지속시키는 직관적 인 방법은 그것들을 반복하고 객체를 저장하기 위해 최대 절전 모드라고 말하는 것 입니다. 나는 이것을 정확히해야한다. 이 작업을 수행하는 "최대 절전 모드"가 무엇인지 말씀해 주시겠습니까?
Ramses

제 경우에는이 개체의 두 인스턴스가 있기 때문에 session.merge (myobject)를 사용해야했습니다. 일반적으로 엔터티가 세션에서 유지되고 분리 될 때 발생합니다. 이 엔티티의 다른 인스턴스는 최대 절전 모드로 요청됩니다. 이 두 번째 인스턴스는 세션에 계속 연결되어 있습니다. 첫 번째 인스턴스가 수정됩니다. 더에서 getj2ee.over-blog.com/...
Reddymails

최대 절전 모드로 인해 문제가 발생하지는 않지만 작동 방식에 대한 이해 부족
ACV

17

USe method session.evict(object);의 기능은 evict()세션 캐시에서 인스턴스를 제거하는 데 사용됩니다. 따라서 처음으로 객체를 저장 session.save(object)하려면 캐시에서 객체를 제거하기 전에 메서드를 호출 하여 객체를 저장하십시오. 같은 방법으로 evict ()를 호출 session.saveOrUpdate(object)하거나 session.update(object)호출하기 전에 객체를 업데이트합니다 .


11

이것은 읽기 및 쓰기에 동일한 세션 개체를 사용했을 때 발생할 수 있습니다. 어떻게? 하나의 세션을 생성했다고 가정합니다. 기본 키 Emp_id = 101로 직원 테이블에서 레코드를 읽었습니다. 이제 Java에서 레코드를 수정했습니다. 그리고 Employee 레코드를 데이터베이스에 저장합니다. 우리는 여기서 세션을 닫지 않았습니다. 읽은 개체도 세션에 유지됩니다. 우리가 작성하고자하는 객체와 충돌합니다. 따라서이 오류가 발생합니다.


9

누군가가 이미 위에서 지적했듯이 나는 관계 cascade=all의 양쪽 끝에 있을 때이 문제에 부딪 혔 one-to-many으므로 A-> B (A에서 일대 다, B에서 다 대일)를 가정하고 A에서 B를 호출 한 다음 saveOrUpdate (A)를 호출하면 순환 저장 요청이 발생했습니다. 즉, A의 저장은 A의 저장을 트리거하는 B의 저장을 트리거하고 세 번째 인스턴스에서는 엔티티 (A의)가 시도되었습니다. sessionPersistenceContext에 duplicateObject 예외가 발생했습니다.
  한쪽 끝에서 캐스케이드를 제거하여 해결할 수 있습니다.



5

session.merge(obj)동일한 식별자 영구 객체로 다른 세션으로 저장하는 경우를 사용할 수 있습니다 .
효과가 있었고 이전에도 같은 문제가있었습니다.


4

나는 또한이 문제에 부딪 쳤고 오류를 찾기가 어려웠습니다.

내가 가진 문제는 다음과 같습니다.

다른 최대 절전 세션을 사용하여 Dao가 객체를 읽었습니다.

이 예외를 방지하려면 나중에이 개체를 저장 / 업데이트 할 dao로 개체를 다시 읽으십시오.

그래서:

class A{      

 readFoo(){
       someDaoA.read(myBadAssObject); //Different Session than in class B
    }

}

class B{



 saveFoo(){
       someDaoB.read(myBadAssObjectAgain); //Different Session than in class A
       [...]
       myBadAssObjectAgain.fooValue = 'bar';
       persist();
    }

}

어떤 사람들은 많은 시간을 절약 할 수 있기를 바랍니다!


4

나는이 문제를 다음과 같이 만났다.

  1. 개체 삭제 (HQL 사용)
  2. 동일한 ID로 새 개체를 즉시 저장

삭제 후 결과를 플러시하고 새 개체를 저장하기 전에 캐시를 지워서 해결했습니다.

String delQuery = "DELETE FROM OasisNode";
session.createQuery( delQuery ).executeUpdate();
session.flush();
session.clear();

4

이 문제는 데이터베이스에서 객체를 가져 오는 데 사용했던 세션의 동일한 객체를 업데이트 할 때 발생합니다.

업데이트 방법 대신 최대 절전 모드의 병합 방법을 사용할 수 있습니다.

예를 들어 먼저 session.get ()을 사용한 다음 session.merge (객체)를 사용할 수 있습니다. 이 방법은 문제를 일으키지 않습니다. merge () 메서드를 사용하여 데이터베이스의 객체를 업데이트 할 수도 있습니다.


3

세션 내에서 개체를 가져옵니다. 여기에 예가 있습니다.

MyObject ob = null;
ob = (MyObject) session.get(MyObject.class, id);

3
좋은 시도이지만 개체 "ob"은 업데이트 된 데이터없이 반환됩니다 (데이터베이스에서 검색하는 개체와 저장하는 동안 런타임 동안 응용 프로그램을 통해 업데이트 된 것으로 간주).
Alex

2

ID 매핑이 정확합니까? 데이터베이스가 식별자를 통해 ID를 생성하는 경우 사용자 개체를 해당 ..


2

객체를 삭제하는 데이 문제가 발생했지만 evict도 clear도 도움이되지 않았습니다.

/**
 * Deletes the given entity, even if hibernate has an old reference to it.
 * If the entity has already disappeared due to a db cascade then noop.
 */
public void delete(final Object entity) {
  Object merged = null;
  try {
    merged = getSession().merge(entity);
  }
  catch (ObjectNotFoundException e) {
    // disappeared already due to cascade
    return;
  }
  getSession().delete(merged);
}

2

반복되는 객체가 시작되기 전에 세션을 닫은 다음 새 세션을 시작해야합니다.

session.close();      
session = HibernateUtil.getSessionFactory().openSession();

따라서 이러한 방식으로 한 세션에는 동일한 식별자를 가진 엔터티가 하나만 있습니다.


2

파티에 늦었지만 향후 사용자에게 도움이 될 수 있습니다.

내가 때 나는이 문제를 가지고 선택 사용 기록을 getsession() 다시 갱신 같은 또 다른 기록 식별자를 사용하여 동일한 세션이 문제가 발생합니다. 아래에 코드가 추가되었습니다.

Customer existingCustomer=getSession().get(Customer.class,1);
Customer customerFromUi;// This customer details comiong from UI with identifer 1

getSession().update(customerFromUi);// Here the issue comes

이것은 절대해서는 안됩니다. 해결책은 업데이트 전에 세션을 제거하거나 비즈니스 로직을 변경하는 것입니다.


1

@Id 열에 @GenerateValue를 넣는 것을 잊었는지 확인하십시오. 나는 영화와 장르의 다 대다 관계에서 같은 문제가 있었다. 프로그램에서 최대 절전 모드 오류가 발생했습니다. org.hibernate.NonUniqueObjectException : 동일한 식별자 값을 가진 다른 개체가 이미 세션 오류와 연결되었습니다. 나중에 GenreId get 메서드에 @GenerateValue가 있는지 확인해야한다는 사실을 나중에 알게되었습니다.


내 질문에 솔루션을 어떻게 적용 할 수 있습니까? 태스크 클래스에서 ID 열을 작성합니까? stackoverflow.com/questions/55732955/…
sbattou apr

1

null인지 0인지 여부를 확인하십시오.

if(offersubformtwo.getId()!=null && offersubformtwo.getId()!=0)

콘텐츠가 폼에서 Pojo로 설정되는 추가 또는 업데이트


1

나는 NHibernate를 처음 접했고, 내 문제는 내가 그것을 저장하기 위해했던 것보다 내 객체를 쿼리하기 위해 다른 세션을 사용했다는 것입니다. 그래서 저장 세션은 객체에 대해 알지 못했습니다.

분명해 보이지만 이전 답변을 읽음으로써 2 세션이 아닌 2 개의 개체를 모든 곳에서 찾고있었습니다.


1

@GeneratedValue (strategy = GenerationType.IDENTITY),이 어노테이션을 엔티티 Bean의 기본 키 특성에 추가하면이 문제가 해결됩니다.


1

이 문제를 해결했습니다.
실제로 이것은 빈 클래스에서 PK 속성의 Generator Type 구현을 잊었 기 때문에 발생합니다. 따라서 다음과 같은 유형으로 만드십시오.

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;

Bean의 객체를 지속 할 때 모든 객체가 동일한 ID를 얻었으므로 첫 번째 객체가 저장되고 다른 객체가 지속될 Exception: org.hibernate.NonUniqueObjectException:때 동일한 식별자 값을 가진 이 유형의 다른 객체를 통해 HIB FW 가 이미 세션과 연관되어 있습니다.


1

동일한 최대 절전 모드 세션에서 동일한 식별자로 두 개의 개체를 저장하려고하기 때문에 문제가 발생합니다.

  1. 이는 아래와 같이 id 필드에 mapping.xml 파일을 올바르게 구성하지 않았기 때문에 발생합니다.

    <id name="id">
      <column name="id" sql-type="bigint" not-null="true"/>
      <generator class="hibernateGeneratorClass"</generator>
    </id>
  2. getsession 메서드를 오버로드하여 isSessionClear와 같은 Parameter를 수락하고 아래와 같이 현재 세션을 반환하기 전에 세션을 지 웁니다.

    public static Session getSession(boolean isSessionClear) {
        if (session.isOpen() && isSessionClear) {
            session.clear();
            return session;
        } else if (session.isOpen()) {
            return session;
        } else {
            return sessionFactory.openSession();
        }
    }

이로 인해 기존 세션 객체가 지워지고 최대 절전 모드가 고유 식별자를 생성하지 않더라도 Auto_Increment와 같은 것을 사용하여 기본 키에 대해 데이터베이스를 올바르게 구성했다고 가정하면 작동합니다.


1

비슷한 문제가있었습니다. 내 경우에는 내가 설정 잊고 있었던 increment_by에 의해 사용되는 것과 같은 동일하게 데이터베이스에 값을 cache_size하고 allocationSize. (화살표는 언급 된 속성을 가리 킵니다.)

SQL :

CREATED         26.07.16
LAST_DDL_TIME   26.07.16
SEQUENCE_OWNER  MY
SEQUENCE_NAME   MY_ID_SEQ
MIN_VALUE       1
MAX_VALUE       9999999999999999999999999999
INCREMENT_BY    20 <-
CYCLE_FLAG      N
ORDER_FLAG      N
CACHE_SIZE      20 <-
LAST_NUMBER     180

자바:

@SequenceGenerator(name = "mySG", schema = "my", 
sequenceName = "my_id_seq", allocationSize = 20 <-)

1

wbdarby가 말한 것 외에는 객체의 식별자를 HQL에 제공하여 객체를 가져올 때도 발생할 수 있습니다. 동일한 세션 에서 오브젝트 필드를 수정하고 DB에 다시 저장 (수정은 삽입, 삭제 또는 업데이트 가능)을 시도하는 경우이 오류가 나타납니다. 수정 된 개체를 저장하거나 완전히 새로운 세션을 생성하기 전에 최대 절전 세션을 지우십시오.

내가 도왔 으면 좋겠다 ;-)


1

내 세트를 Jackson에서 얻은 새 세트로 교체하는 것과 동일한 오류가 있습니다.

이를 해결하기 위해 기존 세트를 유지하고 이전 세트에서 알 수없는 요소를 retainAll. 그런 다음 addAll.

    this.oldSet.retainAll(newSet);
    this.oldSet.addAll(newSet);

Session을 가지고 조작 할 필요가 없습니다.


1

이 시도. 아래는 나를 위해 일했습니다!

에서 hbm.xml파일

  1. dynamic-update클래스 태그 의 속성 을 다음과 같이 설정해야 합니다 true.

    <class dynamic-update="true">
  2. 고유 열 아래 생성기 태그의 클래스 속성을 다음과 같이 설정하십시오 identity.

    <generator class="identity">

참고 : 고유 열을 identity대신로 설정하십시오 assigned.


0

나를 위해 일한 또 다른 것은 long 대신 인스턴스 변수 Long을


내 기본 키 변수 긴 ID가 있습니다. Long id로 변경; 일했다

모두 제일 좋다


0

항상 세션 플러시를 수행 할 수 있습니다. Flush는 세션에있는 모든 개체의 상태를 동기화하고 (내가 틀렸다면 누군가 나를 고쳐주세요) 경우에 따라 문제를 해결할 수도 있습니다.

자신의 같음과 해시 코드를 구현하는 것도 도움이 될 수 있습니다.


0

캐스케이드 설정을 확인할 수 있습니다. 모델의 캐스케이드 설정으로 인해이 문제가 발생할 수 있습니다. 캐스케이드 설정을 제거했고 (본질적으로 캐스케이드 삽입 / 업데이트를 허용하지 않음) 이로 인해 문제가 해결되었습니다.


0

이 오류도 발견했습니다. 나를 위해 일한 것은 기본 키 (자동 생성됨)가 PDT (예 : long, int 등)가 아니라 객체 (예 : Long, Integer 등)인지 확인하는 것입니다.

객체를 생성하여 저장할 때 0이 아닌 null을 전달해야합니다.


0

도움이 되나요?

User userObj1 = new User();
User userObj2 = userObj1;
.
.
.
rtsession.save(userObj1);
rtsession.save(userObj2); 

0

비슷한 문제를 해결했습니다.

plan = (FcsRequestPlan) session.load(plan.getClass(), plan.getUUID());
while (plan instanceof HibernateProxy)
    plan = (FcsRequestPlan) ((HibernateProxy) plan).getHibernateLazyInitializer().getImplementation();
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.