최대 절전 모드에서 서로 다른 저장 방법의 차이점은 무엇입니까?


199

Hibernate는 어떤 식 으로든 객체를 가져 와서 데이터베이스에 넣는 소수의 메소드를 가지고 있습니다. 그들 사이의 차이점, 사용시기, 사용시기를 아는 하나의 지능적인 방법이없는 이유는 무엇입니까?

지금까지 확인한 방법은 다음과 같습니다.

  • save()
  • update()
  • saveOrUpdate()
  • saveOrUpdateCopy()
  • merge()
  • persist()

답변:


117

여기 방법에 대한 이해가 있습니다. 실제로 이것들은 모두 실제로 사용하지는 않지만 주로 API를 기반으로합니다 .

saveOrUpdate 일부 확인에 따라 저장 또는 업데이트를 호출합니다. 예를 들어 식별자가 없으면 save가 호출됩니다. 그렇지 않으면 업데이트가 호출됩니다.

저장 엔티티를 유지합니다. 존재하지 않는 식별자를 할당합니다. 그렇다면 업데이트를 수행하는 것입니다. 엔티티의 생성 된 ID를 리턴합니다.

update 기존 식별자를 사용하여 엔터티를 유지하려고 시도합니다. 식별자가 없으면 예외가 발생했다고 생각합니다.

saveOrUpdateCopy 더 이상 사용되지 않으며 더 이상 사용해서는 안됩니다. 대신에 ...

병합 이제 여기가 내 지식이 왜곡되기 시작합니다. 여기서 중요한 것은 일시적인 엔티티, 분리 된 엔티티 및 영구적 엔티티의 차이점입니다. 객체 상태에 대한 자세한 내용은 은 여기를 참조하십시오 . 저장 및 업데이트를 통해 영구 객체를 처리합니다. 그것들은 세션에 연결되어 있으므로 Hibernate는 무엇이 바뀌 었는지 알 수 있습니다. 그러나 일시적인 객체가 있으면 세션이 없습니다. 이 경우 업데이트를 위해 병합을 사용하고 저장을 위해 지속해야합니다.

persist 위에서 언급했듯이 이것은 일시적인 객체에서 사용됩니다. 생성 된 ID는 반환하지 않습니다.


22
이것을 대답으로 받아들이고 싶지만 한 가지 분명하지 않습니다. save ()가 update ()로 되돌아 가기 때문에 그러한 항목이 존재하면 실제로 saveOrUpdate ()와 어떻게 다릅니 까?
Henrik Paul

분리 된 인스턴스에서 해당 저장이 작동하는 곳은 어디입니까?
jrudolph

2
만약 당신이 merge / persist에 대한 설명이 일시적인 객체에서만 중요하다면 이것은 많은 의미가 있고 우리가 최대 절전 모드를 사용하는 방법에 적합합니다. 또한 병합의 경우 무결성 검사를 위해 추가 페치를 수행하는 것처럼 보이기 때문에 종종 업데이트와 비교하여 성능 제한이 있습니다.
Martin Dale Lyness

1
아래 jrudolph의 답변이 더 정확합니다.
azerole

2
최대 절전 모드를 사용하면 객체의 상태를 알 수 있으므로 프로그램을 작성할 때 왜 수동으로해야합니까? 하나의 저장 방법 만 있어야합니다.
masterxilo 2016 년

116
╔══════════════╦═══════════════════════════════╦════════════════════════════════╗
    METHOD                TRANSIENT                      DETACHED            
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
                     sets id if doesn't         sets new id even if object   
    save()         exist, persists to db,        already has it, persists    
                  returns attached object     to DB, returns attached object 
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
                     sets id on object                    throws             
   persist()       persists object to DB            PersistenceException     
                                                                             
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
                                                                             
   update()              Exception                persists and reattaches    
                                                                             
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
                copy the state of object in      copy the state of obj in    
    merge()        DB, doesn't attach it,    ║      DB, doesn't attach it,    
                  returns attached object         returns attached object    
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
                                                                             
saveOrUpdate()║           as save()                       as update()         
                                                                             
╚══════════════╩═══════════════════════════════╩════════════════════════════════╝

update일시적인 객체는 괜찮습니다. 예외는 없었습니다.
GMsoF

내가 아는 한, 어떤 식 으로든 과도 상태를 유지할 수 없습니다. 나는 분리와 지속성의 차이가있을 수 있다고 생각합니다. 수정 해주세요.
Ram

여기에는 많은 오류가 있습니다. 예를 들어 1)‘save ()’는 "첨부 된 객체"를 반환하지 않으며‘id’를 반환합니다. 2) 'persist ()'는 'id'를 설정한다고 보장하지 않으며 "objects to DB"도 아닙니다. ...
Eugen Labun

67
  • persist와 save의 미묘한 차이점에 대한 설명은 Hibernate 포럼 을 참조하십시오 . 차이점은 INSERT 문이 궁극적으로 실행되는 시간과 같습니다. save 는 식별자를 반환 하므로 INSERT 문은 트랜잭션 상태에 관계없이 즉시 실행되어야합니다 (일반적으로 나쁜 것임). Persist 는 식별자를 할당하기 위해 현재 실행중인 트랜잭션 이외의 명령문을 실행하지 않습니다. 저장 / 지속은 둘 다 임시 인스턴스 , 즉 아직 식별자가 할당되지 않은 인스턴스에서 작동하며 DB에 저장되지 않습니다.

  • 업데이트병합분리 된 인스턴스 , 즉 DB에 해당 항목이 있지만 현재 세션에 연결되지 않았거나 세션에 의해 관리되지 않는 인스턴스 에서 작동합니다 . 그들 사이의 차이점은 함수에 전달되는 인스턴스에서 발생하는 것입니다. 업데이트 가 인스턴스를 다시 연결하려고 시도합니다. 즉, 세션에 연결된 영구 엔터티의 다른 인스턴스가 없어야합니다. 그렇지 않으면 예외가 발생합니다. 그러나 merge 는 모든 값을 Session의 영구 인스턴스에 복사합니다 (현재로드되지 않은 경우로드 됨). 입력 객체는 변경되지 않습니다. 그래서 병합 보다 더 일반적이며 갱신더 많은 리소스를 사용할 수 있습니다.


자체 ID 생성기가 있으면 insert 문이 여전히 발생하지 않습니다
kommradHomer

따라서 객체가 분리 된 경우 병합은 선택을 트리거하지만 업데이트는하지 않습니까?
Gab

1
save() - If an INSERT has to be executed to get the identifier, then this INSERT happens immediately, no matter if you are inside or outside of a transaction. This is problematic in a long-running conversation with an extended Session/persistence context.인서트가 세션 외부에서 어떻게 발생할 수 있으며 왜 나쁜지 알려주십시오.
Erran Morad

면책 조항 : 오랫동안 최대 절전 모드를 사용하지 않았습니다. IMO의 문제는 이것입니다. 서명과 save () 계약은 save가 새 객체의 식별자를 반환하도록 요구합니다. 선택한 ID 생성 전략에 따라 값이 지정 될 때 DB에서 식별자를 생성합니다 INSERT. 따라서 이러한 경우 식별자를 생성하지 않고 바로 식별자를 반환 할 수 없으며 생성하려면 INSERT 바로 실행해야합니다 . 장기 실행 트랜잭션은 지금 실행되지 않고 커밋에서만 실행되므로 INSERT지금 실행하는 유일한 방법 은 tx 외부에서 실행하는 것입니다.
jrudolph

12

이 링크는 다음과 같이 잘 설명되어 있습니다.

http://www.stevideter.com/2008/12/07/saveorupdate-versus-merge-in-hibernate/

우리 모두는 가끔 우리가 그 문제를 다시 볼 때 우리가이 문제를 해결했다는 것을 알지만 그 방법을 기억할 수 없을 정도로 자주 발생하는 문제를 가지고 있습니다.

Hibernate에서 Session.saveOrUpdate ()를 사용할 때 발생하는 NonUniqueObjectException은 내 것 중 하나입니다. 복잡한 응용 프로그램에 새로운 기능을 추가하겠습니다. 모든 단위 테스트가 정상적으로 작동합니다. 그런 다음 UI를 테스트하면서 객체를 저장하려고 할 때 "동일한 식별자 값을 가진 다른 객체가 이미 세션과 연결되어 있습니다."라는 메시지와 함께 예외가 발생하기 시작합니다. 다음은 Hibernate를 사용한 Java Persistence의 코드 예제입니다.

            Session session = sessionFactory1.openSession();
            Transaction tx = session.beginTransaction();
            Item item = (Item) session.get(Item.class, new Long(1234));
            tx.commit();
            session.close(); // end of first session, item is detached

            item.getId(); // The database identity is "1234"
            item.setDescription("my new description");
            Session session2 = sessionFactory.openSession();
            Transaction tx2 = session2.beginTransaction();
            Item item2 = (Item) session2.get(Item.class, new Long(1234));
            session2.update(item); // Throws NonUniqueObjectException
            tx2.commit();
            session2.close();

이 예외의 원인을 이해하려면 분리 된 객체와 분리 된 객체에서 saveOrUpdate () 또는 update ()를 호출 할 때 발생하는 상황을 이해하는 것이 중요합니다.

개별 최대 절전 모드 세션을 닫으면 작업중인 영구 객체가 분리됩니다. 이는 데이터가 여전히 응용 프로그램의 메모리에 있지만 최대 절전 모드는 더 이상 개체의 변경 내용을 추적하지 않습니다.

분리 된 객체를 수정하고 업데이트하려면 객체를 다시 연결해야합니다. 다시 연결하는 과정에서 최대 절전 모드는 동일한 객체의 다른 사본이 있는지 확인합니다. 그것이 발견되면“실제”사본이 더 이상 무엇인지 모른다는 것을 알려 주어야합니다. 아마도 우리가 저장 될 것으로 예상되는 다른 사본들에 다른 변경이 있었지만, Hibernate는 당시에 관리하지 않았기 때문에 그것들에 대해 알지 못했습니다.

잘못된 데이터를 저장하는 대신, Hibernate는 NonUniqueObjectException을 통해 문제에 대해 알려줍니다.

그래서 우리는 무엇을해야합니까? 최대 절전 모드 3에는 merge ()가 있습니다 (Hibernate 2에서는 saveOrUpdateCopy () 사용). 이 방법을 사용하면 Hibernate는 분리 된 다른 인스턴스의 변경 사항을 저장하려는 인스턴스로 복사하여 저장하기 전에 메모리의 모든 변경 사항을 병합합니다.

        Session session = sessionFactory1.openSession();
        Transaction tx = session.beginTransaction();
        Item item = (Item) session.get(Item.class, new Long(1234));
        tx.commit();
        session.close(); // end of first session, item is detached

        item.getId(); // The database identity is "1234"
        item.setDescription("my new description");
        Session session2 = sessionFactory.openSession();
        Transaction tx2 = session2.beginTransaction();
        Item item2 = (Item) session2.get(Item.class, new Long(1234));
        Item item3 = session2.merge(item); // Success!
        tx2.commit();
        session2.close();

병합은 새로 업데이트 된 인스턴스 버전에 대한 참조를 반환한다는 점에 유의해야합니다. 세션에 항목을 다시 첨부하지 않습니다. 인스턴스 평등 (item == item3)을 테스트하면이 경우 false를 반환합니다. 이 시점부터 item3으로 작업하고 싶을 것입니다.

또한 JPA (Java Persistence API)에는 분리 및 재 부착 된 오브젝트 개념이 없으며 EntityManager.persist () 및 EntityManager.merge ()를 사용합니다.

나는 일반적으로 Hibernate를 사용할 때 saveOrUpdate ()가 내 요구에 충분하다는 것을 알았습니다. 일반적으로 같은 유형의 객체에 대한 참조를 가질 수있는 객체가있는 경우에만 병합을 사용해야합니다. 가장 최근에 예외의 원인은 코드가 참조가 재귀하지 않았 음을 확인하는 것입니다. 유효성 검사의 일부로 세션에 동일한 객체를로드하여 오류가 발생했습니다.

이 문제가 어디에서 발생 했습니까? 병합이 효과가 있었습니까 아니면 다른 솔루션이 필요 했습니까? 항상 병합을 사용하거나 특정 경우에만 필요에 따라 병합을 선호합니까


원래부터 webarchive의 기사에 링크를 사용할 수 없습니다 : web.archive.org/web/20160521091122/http://www.stevideter.com:80/...
오이겐 Labun

5

실제로 최대 절전 모드 save()persist()메서드 의 차이점 은 사용중인 생성기 클래스에 따라 다릅니다.

제너레이터 클래스가 할당 된 경우 save()persist() 메소드 간에 차이가 없습니다 . 생성자 '할당 됨'은 프로그래머로서 데이터베이스에 저장할 기본 키 값을 제공해야합니다. [이 생성기 개념을 알고 싶습니다.] 지정된 생성자 클래스가 아닌 경우 생성기 클래스 이름이 증가 평균 인 경우를 가정하십시오. 최대 절전 모드 자체는 기본 키 ID 값을 할당 된 생성기 이외의 데이터베이스에 기본 키 ID 값을 할당합니다 (기본 키 ID 값을 기억하는 데에만 사용되는 최대 절전 모드).이 경우 우리가 호출 save()하거나 persist()메소드를 호출 하면 레코드를 삽입합니다 데이터베이스는 정상적으로 들리지만, save()메서드는 최대 절전 모드에서 생성 된 기본 키 ID 값을 반환 할 수 있습니다.

long s = session.save(k);

이 경우 persist()클라이언트에게 어떠한 가치도 돌려주지 않습니다.


5

모든 최대 절전 모드 저장 방법의 차이점을 보여주는 좋은 예를 찾았습니다.

http://www.journaldev.com/3481/hibernate-session-merge-vs-update-save-saveorupdate-persist-example

간단히 말하면 위의 링크에 따르면

저장()

  • 트랜잭션 외부에서이 메소드를 호출 할 수 있습니다. 트랜잭션없이 이것을 사용하고 엔터티간에 계단식이있는 경우 세션을 비우지 않으면 기본 엔터티 만 저장됩니다.
  • 따라서 기본 객체에서 매핑 된 다른 객체가 있으면 트랜잭션을 커밋 할 때 또는 세션을 플러시 할 때 저장됩니다.

persist ()

  • 트랜잭션에서 save ()를 사용하는 것과 유사하므로 계단식 객체를 안전하게 처리합니다.

saveOrUpdate ()

  • 트랜잭션과 함께 또는 트랜잭션없이 사용할 수 있으며, 트랜잭션없이 사용 된 경우 save ()와 마찬가지로 매핑 된 엔터티는 저장되지 않으며 세션을 플러시합니다.

  • 제공된 데이터를 기반으로 쿼리를 삽입 또는 업데이트합니다. 데이터베이스에 데이터가 있으면 업데이트 쿼리가 실행됩니다.

최신 정보()

  • 엔터티 정보 만 업데이트한다는 것을 알고있는 경우에는 최대 절전 모드 업데이트를 사용해야합니다. 이 작업은 엔터티 개체를 영구 컨텍스트에 추가하고 트랜잭션이 커밋 될 때 추가 변경 내용을 추적하고 저장합니다.
  • 따라서 update를 호출 한 후에도 엔티티에 값을 설정하면 트랜잭션이 커밋 될 때 업데이트됩니다.

병합 ()

  • 최대 절전 모드 병합을 사용하여 기존 값을 업데이트 할 수 있지만이 메서드는 전달 된 엔터티 개체에서 복사본을 만들어 반환합니다. 리턴 된 오브젝트는 지속적 컨텍스트의 일부이며 변경 사항을 추적하며 전달 된 오브젝트는 추적되지 않습니다. 이것은 다른 모든 메소드와 merge ()의 주요 차이점입니다.

또한 이러한 모든 예제는 위에서 언급 한 링크를 참조하십시오.이 모든 다른 방법에 대한 예제를 보여줍니다.


3

이 기사 에서 설명했듯이 대부분의 경우 JPA 방법과 update일괄 처리 작업을 선호해야 합니다.

JPA 또는 최대 절전 모드 엔티티는 다음 네 가지 상태 중 하나 일 수 있습니다.

  • 과도 (신규)
  • 관리 형 (영구적)
  • 분리
  • 삭제 (삭제)

한 상태에서 다른 상태로의 전환은 EntityManager 또는 Session 메소드를 통해 수행됩니다.

예를 들어, JPA EntityManager는 다음 엔티티 상태 전환 방법을 제공합니다.

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

최대 절전 모드 Session모두 구현 JPA의 EntityManager방법과 같은 일부 추가 엔티티 상태 전환 방법을 제공하고 save, saveOrUpdate그리고 update.

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

지속

엔티티 상태를 일시적 (신규)에서 관리 됨 (지속됨) 으로 변경하기 위해 Hibernate에 의해 상속 된 persistJPA EntityManager가 제공 하는 방법을 사용할 수 있습니다 Session.

persist메소드 PersistEventDefaultPersistEventListenerHibernate 이벤트 리스너 가 처리 하는를 트리거합니다 .

따라서 다음 테스트 케이스를 실행할 때 :

doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    LOGGER.info(
        "Persisting the Book entity with the id: {}", 
        book.getId()
    );
});

최대 절전 모드는 다음 SQL 문을 생성합니다.

CALL NEXT VALUE FOR hibernate_sequence

-- Persisting the Book entity with the id: 1

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

가 알 id부착 이전에 할당 된 Book현재의 지속성 콘텍스트 엔티티. 관리되는 엔터티는 Map키가 엔터티 유형과 식별자로 구성되고 값이 엔터티 참조 인 구조에 저장되기 때문에 필요합니다 . 이것이 JPA EntityManager와 최대 절전 모드 Session가 1 차 캐시라고 알려진 이유 입니다.

를 호출 할 때 persist엔티티는 현재 실행중인 지속성 컨텍스트에만 연결되며 INSERT flush는 호출 될 때까지 연기 될 수 있습니다 .

엔터티 식별자를 얻을 수있는 유일한 방법이므로 INSERT를 즉시 트리거 하는 IDENTITY 생성기 는 예외입니다 . 이러한 이유로 인해 최대 절전 모드에서는 IDENTITY 생성기를 사용하여 엔터티에 대한 삽입을 일괄 처리 할 수 ​​없습니다. 이 주제에 대한 자세한 내용은 이 기사를 확인 하십시오 .

저장

Hibernate 고유의 save방법은 JPA보다 이전 버전이며 Hibernate 프로젝트가 시작된 이후부터 사용 가능합니다.

save메소드 SaveOrUpdateEventDefaultSaveOrUpdateEventListenerHibernate 이벤트 리스너 가 처리 하는를 트리거합니다 . 따라서이 save방법은 updatesaveOrUpdate방법 과 동일 합니다.

save방법의 작동 방식 을 보려면 다음 테스트 사례를 고려하십시오.

doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    Session session = entityManager.unwrap(Session.class);

    Long id = (Long) session.save(book);

    LOGGER.info(
        "Saving the Book entity with the id: {}", 
        id
    );
});

위의 테스트 케이스를 실행할 때 Hibernate는 다음 SQL 문을 생성합니다.

CALL NEXT VALUE FOR hibernate_sequence

-- Saving the Book entity with the id: 1

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

보다시피 결과는 persist메소드 호출 과 동일합니다 . 그러나, 달리 persistsave방법은 개체 식별자를 반환합니다.

자세한 내용은 이 기사를 확인 하십시오 .

최신 정보

최대 절전 모드 특정 update방법은 더티 검사 메커니즘 을 무시 하고 플러시시 엔터티를 강제로 업데이트하기위한 것입니다.

update메소드 SaveOrUpdateEventDefaultSaveOrUpdateEventListenerHibernate 이벤트 리스너 가 처리 하는를 트리거합니다 . 따라서이 update방법은 savesaveOrUpdate방법 과 동일 합니다.

update메소드의 작동 방식 을 보려면 Book한 트랜잭션에서 엔티티 를 유지하는 다음 예제를 고려하여 엔티티가 분리 된 상태 인 동안이를 수정하고 update메소드 호출을 사용하여 SQL UPDATE를 강제 실행합니다 .

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

LOGGER.info("Modifying the Book entity");

_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");
});

위의 테스트 케이스를 실행할 때 Hibernate는 다음 SQL 문을 생성합니다.

CALL NEXT VALUE FOR hibernate_sequence

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

-- Modifying the Book entity
-- Updating the Book entity

UPDATE 
    book 
SET 
    author = 'Vlad Mihalcea', 
    isbn = '978-9730228236', 
    title = 'High-Performance Java Persistence, 2nd edition'
WHERE 
    id = 1

는 것을 알 UPDATE직전 커밋, 지속성 컨텍스트 플러시 동안 실행되며, 그 이유의 Updating the Book entity메시지가 처음 기록됩니다.

@SelectBeforeUpdate불필요한 업데이트를 피하기 위해 사용

이제 분리 상태에있는 동안 엔터티가 변경되지 않은 경우에도 UPDATE가 항상 실행됩니다. 이를 방지하기 위해 @SelectBeforeUpdateHibernate 어노테이션을 사용하면 SELECT페치 loaded state된 명령문 이 트리거 되어 더티 점검 메커니즘에서 사용됩니다.

따라서 주석으로 Book엔티티에 주석을 달면 @SelectBeforeUpdate:

@Entity(name = "Book")
@Table(name = "book")
@SelectBeforeUpdate
public class Book {

    //Code omitted for brevity
}

다음 테스트 케이스를 실행하십시오.

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

doInJPA(entityManager -> {
    Session session = entityManager.unwrap(Session.class);

    session.update(_book);
});

최대 절전 모드는 다음 SQL 문을 실행합니다.

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

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

이번에 UPDATE는 Hibernate 더티 검사 메커니즘이 엔티티가 수정되지 않았 음을 감지했기 때문에 실행되지 않습니다.

저장 또는 업데이트

Hibernate 고유의 saveOrUpdate메소드는 saveand 의 별명 일뿐 update입니다.

saveOrUpdate메소드 SaveOrUpdateEventDefaultSaveOrUpdateEventListenerHibernate 이벤트 리스너 가 처리 하는를 트리거합니다 . 따라서이 update방법은 savesaveOrUpdate방법 과 동일 합니다.

이제 saveOrUpdate엔티티를 유지하거나 UPDATE다음 예제에서 설명 하는 것처럼 강제로 사용 하려고 할 때 사용할 수 있습니다 .

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");

doInJPA(entityManager -> {
    Session session = entityManager.unwrap(Session.class);
    session.saveOrUpdate(_book);
});

조심 NonUniqueObjectException

save, update에서 발생할 수있는 한 가지 문제 saveOrUpdate는 지속성 컨텍스트에 이미 다음 예제에서와 동일한 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)

병합

를 피하려면 JPA에서 제공 하고 최대 절전 모드에서 상속 한 방법 NonUniqueObjectException을 사용해야합니다 .mergeEntityManagerSession

설명한 바와 같이 ,이 문서 는이 merge엔터티 참조 영속 컨텍스트에서 발견가 있는지 데이터베이스로부터 새로운 엔티티 스냅 샷을 인출하고, 분리 된 엔티티를 복사 상태는로 전달 merge하는 방법.

merge메소드 MergeEventDefaultMergeEventListenerHibernate 이벤트 리스너 가 처리 하는를 트리거합니다 .

merge방법의 작동 방식 을 확인하기 위해 Book하나의 트랜잭션에서 엔티티 를 유지하는 다음 예제를 고려하여 엔티티가 분리 된 상태에있는 동안이를 수정하고 분리 된 엔티티를 merge서브 시퀀스 지속성 컨텍스트에 전달하십시오 .

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

LOGGER.info("Modifying the Book entity");

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

doInJPA(entityManager -> {
    Book book = entityManager.merge(_book);

    LOGGER.info("Merging the Book entity");

    assertFalse(book == _book);
});

위의 테스트 케이스를 실행할 때 Hibernate는 다음 SQL 문을 실행했습니다.

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

-- Modifying the Book entity

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

에 의해 리턴 된 엔티티 참조 mergemerge메소드에 전달 된 분리 된 참조 와 다릅니다 .

merge분리 된 엔티티 상태를 복사 할 때 JPA를 사용하는 것을 선호하지만 SELECT배치 처리 태스크를 실행할 때 추가 로 문제가 될 수 있습니다.

update따라서 현재 실행중인 지속성 컨텍스트에 이미 연결된 엔티티 참조가없고 분리 된 엔티티가 수정 된 경우 사용을 선호해야합니다 .

이 주제에 대한 자세한 내용은 이 기사를 확인 하십시오 .

결론

엔터티를 유지하려면 JPA persist방법을 사용해야합니다 . 분리 된 엔티티 상태를 복사하려면 merge선호해야합니다. 이 update방법은 일괄 처리 작업에만 유용합니다. savesaveOrUpdate단지 별칭 update당신은 아마 전혀 사용하지합니다.

일부 개발자 save는 엔터티가 이미 관리되는 경우에도 호출 하지만 관리 엔터티의 경우 지속성 컨텍스트 플러시 시간에 UPDATE가 자동으로 처리되므로 실수로 중복 이벤트가 발생합니다.

자세한 내용은 이 기사를 확인 하십시오 .


2

분리 된 객체에서 업데이트를 호출하면 객체를 변경했는지 여부에 관계없이 데이터베이스에서 항상 업데이트가 수행됩니다. 원하는 것이 아니라면 LockMode.None과 함께 Session.lock ()을 사용해야합니다.

개체가 현재 세션 범위 밖에서 변경된 경우 (분리 모드 인 경우)에만 업데이트를 호출해야합니다.



0

위의 답변 중 어느 것도 완료되지 않았습니다. Leo Theobald 답변은 가장 가까운 답변으로 보입니다.

기본 요점은 최대 절전 모드가 엔티티 상태를 처리하는 방법과 상태 변경이있을 때 엔티티를 처리하는 방법입니다. 플러시 및 커밋과 관련하여 모든 것을 볼 수 있어야합니다. 모든 사람들이 완전히 무시한 것 같습니다.

절대 절전 모드를 사용하지 마십시오. 최대 절전 모드에있는 것도 잊지 마십시오!

지속

모두 설명했듯이 Persist는 기본적으로 엔티티를 "과도"상태에서 "관리"상태로 전환합니다. 이 시점에서 슬러시 또는 커밋은 insert 문을 만들 수 있습니다. 그러나 개체는 여전히 "관리"상태로 유지됩니다. 플러시로 변경되지 않습니다.

이 시점에서 다시 "지속"하면 변경 사항이 없습니다. 우리가 지속 된 개체를 유지하려고하면 더 이상 절약 할 수 없습니다.

엔터티를 제거하려고 할 때 재미가 시작됩니다.

퇴거는 엔티티를 "관리 됨"에서 "분리됨"으로 전환하는 Hibernate의 특수 기능입니다. 분리 된 엔터티에 대한 지속성을 호출 할 수 없습니다. 그렇게하면 최대 절전 모드에서 예외가 발생하고 커밋시 전체 트랜잭션이 롤백됩니다.

병합 대 업데이트

이것들은 다른 방식으로 다룰 때 다른 일을하는 두 가지 흥미로운 기능입니다. 둘 다 엔티티를 "분리됨"상태에서 "관리"상태로 전환하려고합니다. 그러나 다르게 행동하십시오.

분리됨은 일종의 "오프라인"상태를 의미한다는 사실을 이해하십시오. 관리 됨은 "온라인"상태를 의미합니다.

아래 코드를 준수하십시오.

Session ses1 = sessionFactory.openSession();

    Transaction tx1 = ses1.beginTransaction();

    HibEntity entity = getHibEntity();

    ses1.persist(entity);
    ses1.evict(entity);

    ses1.merge(entity);

    ses1.delete(entity);

    tx1.commit();

당신이 할 때? 당신은 어떻게 될 것이라고 생각합니까? 이것이 예외를 일으킬 것이라고 말하면 맞습니다. 병합이 분리 된 상태 인 엔티티 오브젝트에 대해 작업했기 때문에 예외가 발생합니다. 그러나 객체의 상태를 변경하지는 않습니다.

장면 뒤에서 병합은 선택 쿼리를 발생시키고 기본적으로 연결된 상태의 엔터티 복사본을 반환합니다. 아래 코드를 준수하십시오.

Session ses1 = sessionFactory.openSession();

    Transaction tx1 = ses1.beginTransaction();
    HibEntity entity = getHibEntity();

    ses1.persist(entity);
    ses1.evict(entity);

    HibEntity copied = (HibEntity)ses1.merge(entity);
    ses1.delete(copied);

    tx1.commit();

위의 샘플은 병합이 새로운 엔티티를 컨텍스트에있는 지속 상태로 가져 오기 때문에 작동합니다.

업데이트를 적용하면 업데이트가 실제로 병합과 같은 엔터티의 복사본을 가져 오지 않기 때문에 동일하게 작동합니다.

Session ses1 = sessionFactory.openSession();

    Transaction tx1 = ses1.beginTransaction();

    HibEntity entity = getHibEntity();

    ses1.persist(entity);
    ses1.evict(entity);

    ses1.update(entity);

    ses1.delete(entity);

    tx1.commit();

동시에 디버그 추적에서 Update가 select like merge의 SQL 쿼리를 발생시키지 않았 음을 알 수 있습니다.

지우다

위의 예제에서 delete에 대해 이야기하지 않고 delete를 사용했습니다. 삭제는 기본적으로 엔티티를 관리 상태에서 "제거"상태로 전환합니다. 플러시 또는 커밋되면 삭제 명령을 실행하여 저장합니다.

그러나 persist 메소드를 사용하여 엔티티를 "제거 된"상태에서 "관리되는"상태로 되돌릴 수 있습니다.

위의 설명에 의문이 생겼기를 바랍니다.

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