EntityManager.merge()
새 개체를 삽입하고 기존 개체를 업데이트 할 수 있습니다.
왜 persist()
새로운 객체를 만들 수 있는가?
답변
어느 쪽이든 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를 사용하려는 상황이 있습니다.
답변
지속과 병합은 서로 다른 두 가지 목적을위한 것입니다 (그들은 대안이 아닙니다).
(차이 정보를 확장하도록 편집 됨)
지속 :
- 데이터베이스에 새 레지스터 삽입
- 개체 관리자에 개체를 연결하십시오.
병합 :
- 동일한 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는 매우 높은 수준의 프로그래밍이므로 이것이 어디에서나 일어날 것이라고 가정 할 수는 없습니다.
답변
할당 된 생성기를 사용하는 경우 persist 대신 merge를 사용하면 중복 SQL 문이 발생하여 성능에 영향을 줄 수 있습니다.
또한 관리되는 엔터티 는 최대 절전 모드에서 자동으로 관리되고 지속성 컨텍스트 를 비울 때 더티 검사 메커니즘 으로 데이터베이스 레코드와 동기화되므로 관리 되는 엔터티에 대한 병합 호출 도 실수 입니다.
이 모든 것이 어떻게 작동하는지 이해하려면 먼저 Hibernate가 개발자의 사고 방식을 SQL 문에서 엔티티 상태 전이 로 이동 시킨다는 것을 알아야합니다 .
Hibernate가 엔티티를 적극적으로 관리하면 모든 변경 사항이 데이터베이스에 자동으로 전파됩니다.
최대 절전 모드는 현재 연결된 엔터티를 모니터링합니다. 그러나 엔티티가 관리 되려면 올바른 엔티티 상태 여야합니다.
JPA 상태 전이를 더 잘 이해하려면 다음 다이어그램을 시각화하면됩니다.
또는 Hibernate 특정 API를 사용하는 경우 :
위 다이어그램에서 알 수 있듯이 엔터티는 다음 4 가지 상태 중 하나 일 수 있습니다.
-
새로운 (일시적)
Hibernate
Session
(akaPersistence Context
)와 연결되지 않았으며 데이터베이스 테이블 행에 매핑 되지 않은 새로 생성 된 객체 는 New (Transient) 상태 인 것으로 간주됩니다.지속 되려면
EntityManager#persist
메소드 를 명시 적으로 호출 하거나 전이 지속 메커니즘을 사용해야합니다. -
지속적 (관리)
지속성 엔티티가 데이터베이스 테이블 행과 연관되었으며 현재 실행중인 지속성 컨텍스트에 의해 관리되고 있습니다. 이러한 엔티티에 대한 모든 변경 사항은 감지되어 데이터베이스로 전파됩니다 (세션 플러시 시간 동안). 최대 절전 모드를 사용하면 더 이상 INSERT / UPDATE / DELETE 문을 실행할 필요가 없습니다. Hibernate는 트랜잭션 쓰기-비하인드 작업 스타일을 사용하며 현재
Session
플러시 시간 동안 가장 마지막 책임있는 순간에 변경 사항이 동기화됩니다 . -
분리
현재 실행중인 지속성 컨텍스트가 닫히면 이전에 관리 된 모든 엔티티가 분리됩니다. 연속적인 변경 사항은 더 이상 추적되지 않으며 자동 데이터베이스 동기화가 수행되지 않습니다.
분리 된 엔터티를 활성 최대 절전 모드 세션에 연결하려면 다음 옵션 중 하나를 선택할 수 있습니다.
-
재 부착
Hibernate (JPA 2.1은 아님)는 Session # update 메소드를 통한 재 연결을 지원합니다. 최대 절전 모드 세션은 주어진 데이터베이스 행에 대해 하나의 Entity 객체 만 연결할 수 있습니다. 지속성 컨텍스트는 메모리 내 캐시 (첫 번째 레벨 캐시)의 역할을하며 하나의 값 (엔티티) 만 주어진 키 (엔티티 유형 및 데이터베이스 식별자)와 연관되기 때문입니다. 현재 최대 절전 모드 세션과 이미 연결된 다른 JVM 객체 (같은 데이터베이스 행과 일치)가없는 경우에만 엔터티를 다시 연결할 수 있습니다.
-
합병
병합이 분리 된 엔티티 상태 (소스)를 관리 엔티티 인스턴스 (대상)에 복사하려고합니다. 병합 엔티티가 현재 세션에서 동등한 항목이 없으면 데이터베이스에서 가져옵니다. 분리 된 객체 인스턴스는 병합 작업 후에도 분리 된 상태를 유지합니다.
-
-
제거
JPA에서는 관리되는 엔터티 만 제거하도록 요구하지만 Hibernate는 분리 된 엔터티를 삭제할 수도 있습니다 (하지만 Session # delete 메서드 호출을 통해서만). 제거 된 엔티티는 삭제 만 예약되며 실제 데이터베이스 DELETE 문은 세션 플러시 시간 동안 실행됩니다.
답변
나는 JPA가 나를 위해 생성 한 필드가 없었을 때조차도를 사용할 때 모든 것에 em.merge
대해 SELECT
성명서를 얻었습니다 . INSERT
기본 키 필드는 내가 설정 한 UUID였습니다. 나는 그때 로 전환하여 성명서를 em.persist(myEntityObject)
얻었습니다 INSERT
.
답변
JPA 사양은 다음에 대해 말합니다 persist()
.
경우 X는 분리 된 객체가되면,이
EntityExistsException
(가) 작업이 호출 지속, 또는 경우에 throw 될 수 있습니다EntityExistsException
또는 다른이PersistenceException
같은 높이에서 던져 질 수 또는 시간을 커밋합니다.
따라서 persist()
객체 가 분리 된 객체 가 아니어야하는 경우에 적합 합니다. 코드 PersistenceException
가 빨리 실패하도록 코드를 처리하는 것이 좋습니다.
하지만 사양이 명확하지 않다 , persist()
을 설정할 수 있습니다 @GeneratedValue
@Id
개체에 대한. merge()
그러나 @Id
이미 생성 된 개체가 있어야합니다 .
답변
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에 진지하게 참여할 사람들을 위해이 책을 읽는 것이 좋습니다. 첫 번째 답변을 익명으로 게시하여 죄송합니다.
답변
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
.