JPA EntityManager : merge () 대신 persist ()를 사용하는 이유는 무엇입니까?


답변:


1615

어느 쪽이든 PersistenceContext에 엔터티를 추가 할 것입니다. 차이점은 나중에 엔터티로 수행하는 작업의 차이점입니다.

Persist는 엔티티 인스턴스를 가져 와서 컨텍스트에 추가하고 인스턴스를 관리합니다 (즉, 엔티티에 대한 향후 업데이트가 추적 됨).

병합은 상태가 병합 된 관리 형 인스턴스를 반환합니다. PersistenceContext에 존재하는 것을 리턴하거나 엔티티의 새 인스턴스를 작성합니다. 어쨌든 제공된 엔터티에서 상태를 복사하고 관리되는 복사본을 반환합니다. 전달한 인스턴스는 관리되지 않습니다 (변경 사항은 병합을 다시 호출하지 않는 한 트랜잭션의 일부가 아님). 반환 된 인스턴스 (관리 된 인스턴스)를 통해 사용할 수 있습니다.

아마도 코드 예제가 도움이 될 것입니다.

MyEntity e = new MyEntity();

// scenario 1
// tran starts
em.persist(e); 
e.setSomeField(someValue); 
// tran ends, and the row for someField is updated in the database

// scenario 2
// tran starts
e = new MyEntity();
em.merge(e);
e.setSomeField(anotherValue); 
// tran ends but the row for someField is not updated in the database
// (you made the changes *after* merging)

// scenario 3
// tran starts
e = new MyEntity();
MyEntity e2 = em.merge(e);
e2.setSomeField(anotherValue); 
// tran ends and the row for someField is updated
// (the changes were made to e2, not e)

시나리오 1과 3은 거의 동일하지만 시나리오 2를 사용하려는 상황이 있습니다.


3
@ dma_k : Hibernate를 사용하고있는 것 같습니다. JPA보다 Hibernate에 익숙하지 않지만 JPA에서는 EntityManager.persist ()를 호출하고 분리 된 엔티티를 전달하면 a) EntityExistsException을 즉시 얻거나 b) 플러시 / 커밋시 다른 PersistenceException을 얻습니다. 어쩌면 나는 여기서 질문을 오해했을 것입니까?
Mike

49
그것은 또한 존재가 합병 법인이 / 이미 지속 지속 컨텍스트에 존재하는 (또는 적어도 그것을 명확하게 (가) / 지속 이미 존재하지 않는 법인을 합병 때하는 동작을 설명하는 만든) 경우를 적용하는 경우이 답변을 향상시킬 수 있습니다
헨리

2
한 가지 방법이 더 성능이 좋습니까? 아마도 merge개체를 관리하기 전에 전체 복사본을 사용하면 성능이 저하 될 수 있습니까?
케빈 메러디스

2
ID는 어떻습니까? 가능한 경우 @GeneratedId시나리오 2에서 얻을 수 있습니까?
rascio

7
Mike : "병합하여 새 인스턴스를 만듭니다 ...": 항상 그런 것은 아닙니다. EntityManager가 컨텍스트에서 이미 관리되는 엔티티를 찾으면 필드를 업데이트 한 후이 인스턴스를 리턴합니다. 답을 수정하면 투표하겠습니다.
Heri

181

지속과 병합은 서로 다른 두 가지 목적을위한 것입니다 (그들은 대안이 아닙니다).

(차이 정보를 확장하도록 편집 됨)

지속 :

  • 데이터베이스에 새 레지스터 삽입
  • 개체 관리자에 개체를 연결하십시오.

병합 :

  • 동일한 ID를 가진 첨부 된 오브젝트를 찾아서 업데이트하십시오.
  • 존재하는 경우 이미 첨부 된 개체를 업데이트하고 반환합니다.
  • 존재하지 않는 경우 새 레지스터를 데이터베이스에 삽입하십시오.

persist () 효율 :

  • merge ()보다 데이터베이스에 새 레지스터를 삽입하는 것이 더 효율적일 수 있습니다.
  • 원본 객체를 복제하지 않습니다.

persist () 의미 :

  • 실수로 삽입하고 업데이트하지 않는지 확인하십시오.

예:

{
    AnyEntity newEntity;
    AnyEntity nonAttachedEntity;
    AnyEntity attachedEntity;

    // Create a new entity and persist it        
    newEntity = new AnyEntity();
    em.persist(newEntity);

    // Save 1 to the database at next flush
    newEntity.setValue(1);

    // Create a new entity with the same Id than the persisted one.
    AnyEntity nonAttachedEntity = new AnyEntity();
    nonAttachedEntity.setId(newEntity.getId());

    // Save 2 to the database at next flush instead of 1!!!
    nonAttachedEntity.setValue(2);
    attachedEntity = em.merge(nonAttachedEntity);

    // This condition returns true
    // merge has found the already attached object (newEntity) and returns it.
    if(attachedEntity==newEntity) {
            System.out.print("They are the same object!");
    }

    // Set 3 to value
    attachedEntity.setValue(3);
    // Really, now both are the same object. Prints 3
    System.out.println(newEntity.getValue());

    // Modify the un attached object has no effect to the entity manager
    // nor to the other objects
    nonAttachedEntity.setValue(42);
}

이 방법은 엔티티 관리자의 모든 레지스터에 대해 하나의 첨부 된 오브젝트 만 존재합니다.

ID가있는 엔티티의 merge ()는 다음과 같습니다.

AnyEntity myMerge(AnyEntity entityToSave) {
    AnyEntity attached = em.find(AnyEntity.class, entityToSave.getId());
    if(attached==null) {
            attached = new AnyEntity();
            em.persist(attached);
    }
    BeanUtils.copyProperties(attached, entityToSave);

    return attached;
}

ON DUPLICATE KEY UPDATE 옵션을 사용하여 INSERT 호출을 사용하여 MySQL merge ()에 persist ()만큼 효율적일 수 있지만 JPA는 매우 높은 수준의 프로그래밍이므로 이것이 어디에서나 일어날 것이라고 가정 할 수는 없습니다.


당신은 대체 유효하지의 경우 이름을 수 em.persist(x)와를 x = em.merge(x)?
Aaron Digulla 2016 년

20
persist ()는 EntityExistsException을 던질 수 있습니다. 코드가 데이터를 업데이트하지 않고 삽입을 수행하고 있는지 확인하려면 persist를 사용해야합니다.
Josep Panadero 2016 년

1
merge()EntityExistsException
Sean

1
@None이기 때문에 가능 RuntimeException하지만 Javadoc에서는 언급되지 않았습니다.
Martin

154

할당 된 생성기를 사용하는 경우 persist 대신 merge를 사용하면 중복 SQL 문이 발생하여 성능에 영향을 줄 수 있습니다.

또한 관리되는 엔터티 는 최대 절전 모드에서 자동으로 관리되고 지속성 컨텍스트비울더티 검사 메커니즘 으로 데이터베이스 레코드와 동기화되므로 관리 되는 엔터티에 대한 병합 호출 도 실수 입니다.

이 모든 것이 어떻게 작동하는지 이해하려면 먼저 Hibernate가 개발자의 사고 방식을 SQL 문에서 엔티티 상태 전이 로 이동 시킨다는 것을 알아야합니다 .

Hibernate가 엔티티를 적극적으로 관리하면 모든 변경 사항이 데이터베이스에 자동으로 전파됩니다.

최대 절전 모드는 현재 연결된 엔터티를 모니터링합니다. 그러나 엔티티가 관리 되려면 올바른 엔티티 상태 여야합니다.

JPA 상태 전이를 더 잘 이해하려면 다음 다이어그램을 시각화하면됩니다.

JPA 엔티티 상태 전환

또는 Hibernate 특정 API를 사용하는 경우 :

최대 절전 모드 상태 전환

위 다이어그램에서 알 수 있듯이 엔터티는 다음 4 가지 상태 중 하나 일 수 있습니다.

  • 새로운 (일시적)

    Hibernate Session(aka Persistence Context)와 연결되지 않았으며 데이터베이스 테이블 행에 매핑 되지 않은 새로 생성 된 객체 는 New (Transient) 상태 인 것으로 간주됩니다.

    지속 되려면 EntityManager#persist메소드 를 명시 적으로 호출 하거나 전이 지속 메커니즘을 사용해야합니다.

  • 지속적 (관리)

    지속성 엔티티가 데이터베이스 테이블 행과 연관되었으며 현재 실행중인 지속성 컨텍스트에 의해 관리되고 있습니다. 이러한 엔티티에 대한 모든 변경 사항은 감지되어 데이터베이스로 전파됩니다 (세션 플러시 시간 동안). 최대 절전 모드를 사용하면 더 이상 INSERT / UPDATE / DELETE 문을 실행할 필요가 없습니다. Hibernate는 트랜잭션 쓰기-비하인드 작업 스타일을 사용하며 현재 Session플러시 시간 동안 가장 마지막 책임있는 순간에 변경 사항이 동기화됩니다 .

  • 분리

    현재 실행중인 지속성 컨텍스트가 닫히면 이전에 관리 된 모든 엔티티가 분리됩니다. 연속적인 변경 사항은 더 이상 추적되지 않으며 자동 데이터베이스 동기화가 수행되지 않습니다.

    분리 된 엔터티를 활성 최대 절전 모드 세션에 연결하려면 다음 옵션 중 하나를 선택할 수 있습니다.

    • 재 부착

      Hibernate (JPA 2.1은 아님)는 Session # update 메소드를 통한 재 연결을 지원합니다. 최대 절전 모드 세션은 주어진 데이터베이스 행에 대해 하나의 Entity 객체 만 연결할 수 있습니다. 지속성 컨텍스트는 메모리 내 캐시 (첫 번째 레벨 캐시)의 역할을하며 하나의 값 (엔티티) 만 주어진 키 (엔티티 유형 및 데이터베이스 식별자)와 연관되기 때문입니다. 현재 최대 절전 모드 세션과 이미 연결된 다른 JVM 객체 (같은 데이터베이스 행과 일치)가없는 경우에만 엔터티를 다시 연결할 수 있습니다.

    • 합병

    병합이 분리 된 엔티티 상태 (소스)를 관리 엔티티 인스턴스 (대상)에 복사하려고합니다. 병합 엔티티가 현재 세션에서 동등한 항목이 없으면 데이터베이스에서 가져옵니다. 분리 된 객체 인스턴스는 병합 작업 후에도 분리 된 상태를 유지합니다.

  • 제거

    JPA에서는 관리되는 엔터티 만 제거하도록 요구하지만 Hibernate는 분리 된 엔터티를 삭제할 수도 있습니다 (하지만 Session # delete 메서드 호출을 통해서만). 제거 된 엔티티는 삭제 만 예약되며 실제 데이터베이스 DELETE 문은 세션 플러시 시간 동안 실행됩니다.



@gstackoverflow 당신이 얻은 대답은 맞습니다. 자세한 내용은 이 기사 또는 저의 저서 고성능 Java Persistence를 참조하십시오 .
Vlad Mihalcea

따라서 orphanremoval = true에 대한 작업 순서를 변경할 가능성이 없습니까?
gstackoverflow

일반적인 경우의 작업 순서에 대한 기사. orphanRemoval에
gstackoverflow

1
사실 같은 다이어그램으로 최대 절전 모드를 설명하는 것은 불가능합니다. 분리 후 세션을 플러시 할 수없는 이유는 무엇입니까? 이미 지속 된 엔터티를 저장하려고하면 어떻게됩니까? 저장 및 유지와 관련하여 플러시 동작이 다른 이유는 무엇입니까? 1000 가지의 질문이 있는데, 아무도 분명한 논리를 가지고 있지 않습니다.
GingerBeer

37

나는 JPA가 나를 위해 생성 한 필드가 없었을 때조차도를 사용할 때 모든 것에 em.merge대해 SELECT성명서를 얻었습니다 . INSERT기본 키 필드는 내가 설정 한 UUID였습니다. 나는 그때 로 전환하여 성명서를 em.persist(myEntityObject)얻었습니다 INSERT.


3
ID를 할당하고 JPA 컨테이너가 어디에서 왔는지 알 수 없으므로 의미가 있습니다. 예를 들어 여러 응용 프로그램이 동일한 데이터베이스에 쓰는 시나리오와 같이 개체가 데이터베이스에 이미 존재할 가능성은 적습니다.
Aaron Digulla

와 비슷한 문제에 직면했습니다 merge(). 복잡한 가있는 PostgreSQL 데이터베이스가 있습니다 . 뷰는 여러 테이블의 데이터를 집계했습니다 (테이블은 구조는 동일하지만 이름은 다릅니다). 그래서 JPA는 시도 merge()했지만 실제로 JPA는 처음으로 만들었습니다 SELECT(보기 설정으로 인해 데이터베이스는 다른 테이블에서 동일한 기본 키를 가진 여러 레코드를 반환 할 수 있습니다!). ( org.hibernate.HibernateException: More than one row with the given identifier was found). 내 경우에는 persist()나를 도왔다.
flaz14

29

JPA 사양은 다음에 대해 말합니다 persist().

경우 X는 분리 된 객체가되면,이 EntityExistsException(가) 작업이 호출 지속, 또는 경우에 throw 될 수 있습니다 EntityExistsException또는 다른이 PersistenceException같은 높이에서 던져 질 수 또는 시간을 커밋합니다.

따라서 persist()객체 분리 된 객체 가 아니어야하는 경우에 적합 합니다. 코드 PersistenceException가 빨리 실패하도록 코드를 처리하는 것이 좋습니다.

하지만 사양이 명확하지 않다 , persist()을 설정할 수 있습니다 @GeneratedValue @Id개체에 대한. merge()그러나 @Id이미 생성 된 개체가 있어야합니다 .


5
" merge()하지만 @Id 이미 생성 된 개체가 있어야합니다 . " EntityManager가 오브젝트 ID 필드의 값을 찾을 수 없을 때마다 DB에 지속 (삽입)됩니다.
Omar

나는 주에 대해 명확하지 않았기 때문에 이것을 처음 이해하지 못했습니다. 이것이 나를 위해했던 것처럼 누군가를 돕기를 바랍니다. docs.jboss.org/hibernate/core/3.6/reference/en-US/html/…
RBz

1
@GeneratedValue)는 (병합 (대한 다른 의미가 없다) 및 지속
SandeepGodara

17

merge over persist를 사용하는 데 도움이되는 merge에 대한 자세한 내용은 다음과 같습니다.

원래 엔터티 이외의 관리 형 인스턴스를 반환하는 것은 병합 프로세스의 중요한 부분입니다. 동일한 식별자를 가진 엔티티 인스턴스가 지속성 컨텍스트에 이미 존재하는 경우, 제공자는 병합중인 엔티티의 상태로 상태를 겹쳐 쓰지만 이미 존재하는 관리 버전은 클라이언트로 리턴되어야합니다. 익숙한. 제공자가 지속성 컨텍스트에서 Employee 인스턴스를 업데이트하지 않은 경우 해당 인스턴스에 대한 참조는 병합중인 새 상태와 일치하지 않습니다.

새 엔티티에서 merge ()가 호출되면 persist () 조작과 유사하게 작동합니다. 엔티티를 지속성 컨텍스트에 추가하지만 원래 엔티티 인스턴스를 추가하는 대신 새 사본을 작성하고 대신 해당 인스턴스를 관리합니다. merge () 조작으로 작성된 사본은 persist () 메소드가 호출 된 것처럼 지속됩니다.

관계가있는 경우 merge () 작업은 분리 된 엔티티가 참조하는 엔티티의 관리되는 버전을 가리 키도록 관리되는 엔티티를 업데이트하려고 시도합니다. 엔터티에 영구적 인 ID가없는 개체와 관계가있는 경우 병합 작업의 결과는 정의되지 않습니다. 일부 공급자는 관리되는 복사본이 비 영구 개체를 가리 키도록 허용하는 반면 다른 공급자는 즉시 예외를 throw 할 수 있습니다. 이러한 경우에 merge () 연산은 선택적으로 계단식으로 배열되어 예외가 발생하지 않도록 할 수 있습니다. 이 섹션 뒷부분에서 merge () 작업의 계단식을 다룰 것입니다. 병합중인 엔티티가 제거 된 엔티티를 가리키는 경우 IllegalArgumentException 예외가 발생합니다.

지연로드 관계는 병합 조작에서 특별한 경우입니다. 엔티티가 분리되기 전에 지연로드 관계가 엔티티에서 트리거되지 않은 경우 엔티티가 병합 될 때 해당 관계가 무시됩니다. 관리되는 동안 관계가 트리거 된 다음 엔티티가 분리 된 동안 널로 설정되면 엔티티의 관리 된 버전도 마찬가지로 병합 중에 관계가 지워집니다. "

위의 모든 정보는 Mike Keith 및 Merrick Schnicariol의 "Pro JPA 2 Mastering the Java ™ Persistence API"에서 가져온 것입니다. 6 장. 섹션 분리 및 병합. 이 책은 실제로 저자가 JPA에 전념하는 두 번째 책입니다. 이 새로운 책은 이전의 것보다 많은 새로운 정보를 가지고 있습니다. JPA에 진지하게 참여할 사람들을 위해이 책을 읽는 것이 좋습니다. 첫 번째 답변을 익명으로 게시하여 죄송합니다.


17

merge와 사이에 약간의 차이점이 있습니다 persist(이미 게시 된 것을 다시 열거하겠습니다).

D1. merge전달 된 엔터티를 관리하지 않고 관리되는 다른 인스턴스를 반환합니다. persist다른 쪽에서는 전달 된 엔터티를 관리합니다.

//MERGE: passedEntity remains unmanaged, but newEntity will be managed
Entity newEntity = em.merge(passedEntity);

//PERSIST: passedEntity will be managed after this
em.persist(passedEntity);

D2. 당신이 개체를 제거하고 개체 등을 지속하기로 결정하는 경우 때문에, 당신은 전용) (지속으로 그렇게 할 수 merge가 발생합니다 IllegalArgumentException.

D3. ID를 수동으로 관리하기로 결정한 경우 (예 : UUID 사용), 해당 ID를 가진 기존 엔터티를 찾기 위해 merge 작업이 후속 SELECT쿼리를 트리거 하지만 persist해당 쿼리가 필요하지 않을 수 있습니다.

D4. 코드를 호출하는 코드를 단순히 신뢰하지 않고 데이터가 업데이트되지 않고 삽입되도록하려면을 사용해야하는 경우가 있습니다 persist.


8

세션에 있던 게으른로드 된 콜렉션에 액세스하려고했기 때문에 엔티티에 lazyLoading 예외가 발생했습니다.

내가 할 일은 별도의 요청으로 세션에서 엔티티를 검색 한 다음 문제가있는 jsp 페이지의 컬렉션에 액세스하려고했습니다.

이것을 완화하기 위해 컨트롤러에서 동일한 엔티티를 업데이트하고 jsp에 전달했지만 세션에 다시 저장하면 액세스 할 수 있고 예제 2의 수정 사항을 SessionScope던질 수는 없습니다 LazyLoadingException.

다음은 나를 위해 일했습니다.

// scenario 2 MY WAY
// tran starts
e = new MyEntity();
e = em.merge(e); // re-assign to the same entity "e"

//access e from jsp and it will work dandy!!

7

사용 사례가 포함되어 있기 때문에 Hibernate 문서 계몽 에서이 설명을 찾았습니다.

merge ()의 사용법과 의미는 새로운 사용자에게는 혼란스러워 보입니다. 첫째, 다른 새 엔티티 관리자에서 한 엔티티 관리자에로드 된 오브젝트 상태를 사용하지 않는 한 merge ()를 전혀 사용할 필요없습니다 . 일부 전체 응용 프로그램에서는이 방법을 사용하지 않습니다.

일반적으로 merge ()는 다음 시나리오에서 사용됩니다.

  • 애플리케이션은 첫 번째 엔티티 관리자에서 오브젝트를로드합니다.
  • 객체는 프리젠 테이션 레이어로 전달됩니다.
  • 개체가 약간 수정되었습니다.
  • 오브젝트는 비즈니스 로직 계층으로 다시 전달됩니다.
  • 응용 프로그램은 두 번째 엔터티 관리자에서 merge ()를 호출하여 이러한 수정 사항을 유지합니다.

다음은 merge ()의 정확한 의미입니다.

  • 현재 지속성 컨텍스트와 연관된 동일한 식별자를 가진 관리 형 인스턴스가있는 경우 지정된 오브젝트의 상태를 관리 형 인스턴스에 복사하십시오.
  • 현재 지속성 컨텍스트와 연관된 관리 인스턴스가없는 경우 데이터베이스에서 인스턴스를로드하거나 새 관리 인스턴스를 작성하십시오.
  • 관리 형 인스턴스가 반환됩니다
  • 주어진 인스턴스는 지속성 컨텍스트와 연관되지 않으며 분리 된 상태로 유지되며 일반적으로 삭제됩니다.

보낸 사람 : http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html


6

답변을 살펴보면 '캐스케이드'및 ID 생성과 관련하여 누락 된 세부 사항이 있습니다. 질문 참조

또한, 별도 가질 수 있음을 언급 할 가치가있다 Cascade: 병합 및 지속에 대한 주석을 Cascade.MERGE하고 Cascade.PERSIST있는이 사용 된 방법에 따라 처리됩니다.

사양은 당신의 친구입니다;)


6

JPA는 Java 플랫폼을 기반으로 구축 된 엔터프라이즈 응용 프로그램 도메인에서 명백히 단순화 된 것입니다. J2EE에서 구식 엔터티 빈의 복잡한 문제를 해결해야하는 개발자로서 JPA를 Java EE 사양에 포함시키는 것이 큰 도약이라고 생각합니다. 그러나 JPA 세부 사항을 자세히 살펴보면서 쉽지 않은 것을 발견했습니다. 이 기사에서는 겹치는 동작이 초보자뿐만 아니라 혼동을 일으킬 수있는 EntityManager의 merge 및 persist 메소드를 비교합니다. 또한 두 가지 방법을보다 일반적인 방법 결합의 특수 사례로 보는 일반화를 제안합니다.

지속 엔티티

병합 방법과 달리 persist 방법은 매우 간단하고 직관적입니다. persist 메소드 사용의 가장 일반적인 시나리오는 다음과 같이 요약 될 수 있습니다.

"새로 생성 된 엔티티 클래스 인스턴스는 persist 메소드로 전달됩니다.이 메소드가 리턴 된 후에는 엔티티가 데이터베이스에 삽입되도록 관리 및 계획됩니다. 트랜잭션이 커미트되기 전에 또는 flush 메소드가 호출 될 때 발생할 수 있습니다. 기업이 PERSIST 계단식 전략으로 표시된 관계를 통해 다른 기업을 참조하는 경우이 절차도 적용됩니다. "

여기에 이미지 설명을 입력하십시오

사양은 더 자세하게 설명되지만 이러한 세부 사항은 다소 이국적인 상황만을 다루기 때문에 기억하는 것이 중요하지 않습니다.

엔터티 병합

지속과 비교하여, 병합 동작에 대한 설명은 그렇게 간단하지 않습니다. 지속의 경우와 같이 주요 시나리오는 없으며, 프로그래머는 올바른 코드를 작성하기 위해 모든 시나리오를 기억해야합니다. JPA 디자이너는 분리 된 엔티티를 처리하는 데 주된 관심사가있는 메소드를 원했던 것 같습니다 (주로 새로 작성된 엔티티를 처리하는 지속 메소드와 반대입니다). 병합 메소드의 주요 작업은 지속성 컨텍스트 내에서 관리되지 않는 엔티티 (인수로 전달됨) 그러나이 작업은 몇 가지 시나리오로 더 나뉘어 전체 방법의 동작에 대한 명료성을 악화시킵니다.

JPA 사양의 단락을 반복하는 대신 병합 방법의 동작을 개략적으로 나타내는 순서도를 준비했습니다.

여기에 이미지 설명을 입력하십시오

그렇다면 언제 persist를 사용하고 병합해야합니까?

지속

  • 메소드가 항상 새 엔티티를 작성하고 엔티티를 업데이트하지 않기를 원합니다. 그렇지 않으면 기본 키 고유성 위반으로 인해 메소드에서 예외가 발생합니다.
  • 배치 프로세스, 상태 저장 방식으로 엔티티 처리 (게이트웨이 패턴 참조)
  • 성능 최적화

합병

  • 메소드가 데이터베이스에 엔티티를 삽입하거나 업데이트하려고합니다.
  • 상태 비 저장 방식으로 엔티티를 처리하려고합니다 (서비스의 데이터 전송 오브젝트)
  • 아직 생성되지 않았지만 아직 생성되지 않은 다른 엔터티에 대한 참조가있는 새 엔터티를 삽입하려고합니다 (관계는 MERGE로 표시되어야 함). 예를 들어, 새 앨범 또는 기존 앨범을 참조하여 새 사진을 삽입합니다.

E 관리와 PC에 관리되는 E 버전이 어떻게 다릅니 까?
GingerBeer

5

시나리오 X :

Table : Spitter (One), Table : Spittles (Many) (Spittles는 FK : spitter_id와 관계의 소유자입니다)

이 시나리오는 다음을 저장합니다. Spitter와 동일한 Spitter가 소유 한 것처럼 Spittles.

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.addSpittle(spittle3); // <--persist     
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!

시나리오 Y :

이것은 Spitter를 저장하고 2 개의 Spittles를 저장하지만 동일한 Spitter를 참조하지는 않습니다!

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.save(spittle3); // <--merge!!       
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!

1
스피터는 Graig Walls의 "Spring in Action"3 판에서 발췌 한 것입니다. Spitters는 무언가를 말하는 사람들이며 Spittle은 실제로 말하는 것입니다. Spitter가 많은 첨점을 가지고 있다는 것은 그가 Strings의 목록을 가지고 있다는 것을 의미합니다.
George Papatheodorou

1
Spring in Action을 읽지 않고도 좀 더 읽기 쉬운 예제를 사용할 수 있습니다.
wonderb0lt

1
당신은 침하거나 Spitter 테이블이라고 기록 상단에 이후 spitter 무엇인지 알 수있는 사실은 그나마 필요성, spitter 소유 다른 테이블입니다 .. 이것 저것 ...
조지 Papatheodorou

3

다른 관찰 :

merge()이러한 ID를 가진 레코드가 이미 테이블에 존재하는 경우 자동 생성 된 id (tested on IDENTITYSEQUENCE) 에 대해서만 신경을 씁니다. 이 경우 merge()레코드 업데이트를 시도합니다. 그러나 ID가 없거나 기존 레코드와 일치하지 않으면 merge()완전히 무시하고 DB에 새 ID 를 할당하도록 요청합니다. 이것은 때때로 많은 버그의 원인입니다. merge()새 레코드의 ID를 강요하는 데 사용하지 마십시오 .

persist()반면에 ID를 전달하지도 않습니다. 즉시 실패합니다. 내 경우에는 다음과 같습니다.

원인 : org.hibernate.PersistentObjectException : 분리 된 엔티티가 지속되도록 전달되었습니다.

hibernate-jpa javadoc에 힌트가있다 :

예외 : javax.persistence.EntityExistsException-엔티티가 이미 존재하는 경우 엔티티가 이미 존재하는 경우 지속 조작이 호출 될 때 EntityExistsException이 발생하거나 플러시 또는 커미트시에 EntityExistsException 또는 다른 PersistenceException이 발생 될 수 있습니다.


2
자동 생성 된 ID를 사용하지 않는 경우 새 엔터티에 ID를 수동으로 제공해야합니다. persist()ID가 있다고 불평하지 않고 동일한 ID를 가진 것이 이미 데이터베이스에있을 때만 불평합니다.
시간

1

persist 사용 시기 및 merge 사용시기에 대한 조언을 받으려면 여기로 오셨을 것 입니다. 상황에 따라 다르다고 생각합니다. 새 레코드를 만들어야 할 가능성과 지속 된 데이터를 검색하는 것이 얼마나 어려운가요?

자연 키 / 식별자를 사용할 수 있다고 가정 해 봅시다.

  • 데이터는 유지해야하지만 가끔 레코드가 존재하고 업데이트가 필요합니다. 이 경우 지속을 시도 할 수 있으며 EntityExistsException이 발생하면이를 찾고 데이터를 결합합니다.

    {entityManager.persist (entity)}를 시도하십시오

    catch (EntityExistsException 예외) {/ * 검색 및 병합 * /}

  • 지속 된 데이터는 업데이트해야하지만 가끔 데이터에 대한 레코드가 없습니다. 이 경우, 당신은 그것을 찾고 엔티티가없는 경우 지속을 수행하십시오 :

    entity = entityManager.find (key);

    if (entity == null) {entityManager.persist (entity); }

    else {/ * merge * /}

자연스러운 키 / 식별자가 없으면 엔터티의 존재 여부 또는 찾는 방법을 파악하기가 더 어려워집니다.

병합도 두 가지 방법으로 처리 할 수 ​​있습니다.

  1. 일반적으로 변경 사항이 적은 경우 관리 대상 엔티티에 적용하십시오.
  2. 변경 사항이 일반적인 경우 변경되지 않은 데이터뿐만 아니라 지속 된 엔터티에서 ID를 복사하십시오. 그런 다음 EntityManager :: merge ()를 호출하여 이전 컨텐츠를 바꾸십시오.

0

persist (entity)는 완전히 새로운 엔티티와 함께 ​​사용하여 DB에 추가해야합니다 (DB에 엔티티가 이미 존재하는 경우 EntityExistsException이 발생 함).

엔티티가 분리되어 변경된 경우 엔티티를 지속성 컨텍스트로 되돌리려면 merge (entity)를 사용해야합니다.

아마도 persist는 INSERT sql 문을 생성하고 UPDATE sql 문을 병합합니다 (하지만 확실하지 않습니다).


이것은 올바르지 않습니다. 새 e에서 merge (e)를 호출하면 지속되어야합니다.
Pedro Lamarão


JPA 스펙 버전 2.1, 섹션 3.2.7.1, 두 번째 글 머리표 : "X가 새 엔티티 인스턴스 인 경우 새 관리 엔티티 인스턴스 X '가 작성되고 X의 상태가 새 관리 엔티티 인스턴스 X'에 복사됩니다."
Pedro Lamarão
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.