이것은 매우 일반적인 질문 이므로이 답변을 기반으로하는이 기사를 작성
했습니다.
엔터티 상태
JPA는 다음 엔티티 상태를 정의합니다.
새로운 (일시적)
Hibernate Session
(aka Persistence Context
)와 연결되지 않았으며 데이터베이스 테이블 행에 매핑 되지 않은 새로 생성 된 객체 는 New (Transient) 상태 인 것으로 간주됩니다.
지속 되려면 EntityManager#persist
메소드 를 명시 적으로 호출 하거나 전이 지속 메커니즘을 사용해야합니다.
지속적 (관리)
지속성 엔티티가 데이터베이스 테이블 행과 연관되었으며 현재 실행중인 지속성 컨텍스트에 의해 관리되고 있습니다. 이러한 엔티티에 대한 모든 변경 사항이 감지되어 세션 세션 시간 동안 데이터베이스에 전파됩니다.
최대 절전 모드를 사용하면 더 이상 INSERT / UPDATE / DELETE 문을 실행할 필요가 없습니다. Hibernate는 트랜잭션 쓰기-비하인드 작업 스타일을 사용하며 현재 Session
플러시 시간 동안 가장 마지막 책임있는 순간에 변경 사항이 동기화됩니다 .
분리
현재 실행중인 지속성 컨텍스트가 닫히면 이전에 관리 된 모든 엔티티가 분리됩니다. 연속적인 변경 사항은 더 이상 추적되지 않으며 자동 데이터베이스 동기화가 수행되지 않습니다.
엔터티 상태 전환
EntityManager
인터페이스에서 정의한 다양한 방법을 사용하여 엔티티 상태를 변경할 수 있습니다 .
JPA 엔티티 상태 전이를 더 잘 이해하려면 다음 다이어그램을 고려하십시오.
JPA를 사용하는 경우 분리 된 엔티티를 active에 다시 연관시키기 EntityManager
위해 병합 조작을 사용할 수 있습니다 .
을 제외하고 기본 Hibernate API를 사용하는 merge
경우 다음 다이어그램에 표시된 것처럼 업데이트 방법을 사용하여 분리 된 엔티티를 활성 Hibernate 세션에 다시 연결할 수 있습니다.
분리 된 엔터티 병합
병합이 분리 된 엔티티 상태 (소스)를 관리 엔티티 인스턴스 (대상)에 복사하려고합니다.
다음 Book
엔티티 EntityManager
를 유지했으며 이제 엔티티를 닫는 데 사용 된 엔티티가 닫히면 엔티티가 분리됩니다 .
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
return book;
});
엔티티가 분리 상태 인 동안 다음과 같이 수정합니다.
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
이제 변경 사항을 데이터베이스에 전파하여 merge
메소드를 호출 할 수 있습니다 .
doInJPA(entityManager -> {
Book book = entityManager.merge(_book);
LOGGER.info("Merging the Book entity");
assertFalse(book == _book);
});
그리고 최대 절전 모드는 다음 SQL 문을 실행합니다.
SELECT
b.id,
b.author AS author2_0_,
b.isbn AS isbn3_0_,
b.title AS title4_0_
FROM
book b
WHERE
b.id = 1
-- Merging the Book entity
UPDATE
book
SET
author = 'Vlad Mihalcea',
isbn = '978-9730228236',
title = 'High-Performance Java Persistence, 2nd edition'
WHERE
id = 1
병합 엔티티가 current에 해당하는 것이 없으면 EntityManager
데이터베이스에서 새로운 엔티티 스냅 샷을 가져옵니다.
관리 대상 엔티티가 있으면 JPA는 분리 된 엔티티의 상태를 현재 관리되는 엔티티에 복사하고 지속성 컨텍스트flush
동안 더티 검사 메커니즘 이 관리 엔티티가 변경된 것을 발견 하면 UPDATE가 생성됩니다 .
따라서를 사용할 때 merge
분리 된 객체 인스턴스는 병합 작업 후에도 분리 된 상태를 유지합니다.
분리 된 엔터티 다시 연결
최대 절전 모드이지만 JPA는이 update
방법을 통한 재 연결을 지원합니다 .
최대 절전 모드 Session
는 지정된 데이터베이스 행에 대해 하나의 엔터티 개체 만 연결할 수 있습니다. 지속성 컨텍스트는 메모리 내 캐시 (첫 번째 레벨 캐시)로 작동하고 하나의 값 (엔티티) 만 주어진 키 (엔티티 유형 및 데이터베이스 식별자)와 연관되기 때문입니다.
현재 Hibernate와 이미 연결된 다른 JVM 오브젝트 (같은 데이터베이스 행과 일치)가없는 경우에만 엔티티를 다시 첨부 할 수 있습니다 Session
.
Book
엔터티 가 지속되고 엔터티 Book
가 분리 된 상태 일 때 수정 한 것을 고려하면 다음 과 같습니다.
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
return book;
});
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
분리 된 엔티티를 다음과 같이 다시 첨부 할 수 있습니다.
doInJPA(entityManager -> {
Session session = entityManager.unwrap(Session.class);
session.update(_book);
LOGGER.info("Updating the Book entity");
});
그리고 최대 절전 모드는 다음 SQL 문을 실행합니다.
-- Updating the Book entity
UPDATE
book
SET
author = 'Vlad Mihalcea',
isbn = '978-9730228236',
title = 'High-Performance Java Persistence, 2nd edition'
WHERE
id = 1
update
방법은 당신을 필요로 하이버 네이트에 .unwrap
EntityManager
Session
와 달리 merge
제공된 분리 된 엔티티는 현재 지속성 컨텍스트와 다시 연관되며 엔티티가 수정되었는지 여부에 관계없이 플러시 중에 UPDATE가 스케줄됩니다.
이를 방지하기 위해 @SelectBeforeUpdate
Hibernate 어노테이션을 사용하여 로드 된 상태를 페치 한 SELECT 문을 트리거 한 다음 더티 점검 메커니즘에서 사용합니다.
@Entity(name = "Book")
@Table(name = "book")
@SelectBeforeUpdate
public class Book {
//Code omitted for brevity
}
NonUniqueObjectException에주의하십시오
update
지속성 컨텍스트에 이미 다음 예제에서와 동일한 ID 및 동일한 유형의 엔티티 참조가 포함되어있는 경우 발생할 수있는 한 가지 문제점입니다 .
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
Session session = entityManager.unwrap(Session.class);
session.saveOrUpdate(book);
return book;
});
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
try {
doInJPA(entityManager -> {
Book book = entityManager.find(
Book.class,
_book.getId()
);
Session session = entityManager.unwrap(Session.class);
session.saveOrUpdate(_book);
});
} catch (NonUniqueObjectException e) {
LOGGER.error(
"The Persistence Context cannot hold " +
"two representations of the same entity",
e
);
}
이제 위의 테스트 케이스를 실행할 때 Hibernate는 NonUniqueObjectException
두 번째에 EntityManager
이미 Book
전달한 것과 동일한 식별자를 가진 엔티티를 포함 update
하고 Persistence Context가 동일한 엔티티의 두 가지 표현을 보유 할 수 없기 때문에 a를 던질 것 입니다.
org.hibernate.NonUniqueObjectException:
A different object with the same identifier value was already associated with the session : [com.vladmihalcea.book.hpjp.hibernate.pc.Book#1]
at org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:651)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:284)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:227)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:92)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73)
at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:682)
at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:674)
결론
merge
방법은 당신이 업데이트 손실을 방지 할 수 있습니다 당신이 낙관적 잠금을 사용하는 경우 선호된다. 이 주제에 대한 자세한 내용은 이 기사를 확인 하십시오 .
이는 작업에 update
의해 생성 된 추가 SELECT 문을 방지 merge
하여 일괄 업데이트 실행 시간을 줄일 수 있으므로 일괄 업데이트에 유용합니다 .
refresh()
분리 된 엔티티를 허용하지 않는 이유가 있는지 궁금합니다 . 2.0 사양을 살펴보면 어떤 정당화도 보이지 않습니다. 그것은 허용되지 않습니다.