문제

Hibernate에는 어떤 식 으로든 객체를 가져 와서 데이터베이스에 넣는 소수의 방법이 있습니다. 그들 사이의 차이점은 무엇이며, 언제 사용 해야하는지, 왜 무엇을 사용 해야하는지 아는 하나의 지능형 방법이없는 이유는 무엇입니까?

지금까지 내가 식별 한 방법은 다음과 같습니다.

  • save()
  • update()
  • saveOrUpdate()
  • saveOrUpdateCopy()
  • merge()
  • persist()
도움이 되었습니까?

해결책

방법에 대한 나의 이해는 다음과 같습니다. 주로 이것들은 API 비록이 모든 것을 실제로 사용하지는 않지만.

SaveorUpdate일부 수표에 따라 저장 또는 업데이트를 호출합니다. 예 : 식별자가 없으면 저장이 호출됩니다. 그렇지 않으면 업데이트가 호출됩니다.

저장엔티티를 지속시킵니다. 존재하지 않으면 식별자를 할당합니다. 그렇다면 본질적으로 업데이트를 수행합니다. 엔티티의 생성 된 ID를 반환합니다.

업데이트기존 식별자를 사용하여 엔티티를 지속하려고 시도합니다. 식별자가 존재하지 않으면 예외가 발생한다고 생각합니다.

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

병합이제 이것은 내 지식이 흔들리기 시작하는 곳입니다. 여기서 중요한 것은 과도, 분리 및 지속적인 엔티티의 차이입니다. 물체 상태에 대한 자세한 내용은 여기서 살펴보세요. 저장 및 업데이트를 통해 지속적인 객체를 다루고 있습니다. 그들은 세션에 연결되어 있으므로 최대 절전 모드는 무엇이 바뀌 었는지 알고 있습니다. 그러나 일시적인 객체가 있으면 세션이 관련이 없습니다. 이 경우 업데이트를 위해 병합을 사용하고 저축을 위해 지속해야합니다.

지속위에서 언급했듯이 이것은 과도 물체에 사용됩니다. 생성 된 ID를 반환하지 않습니다.

다른 팁

╔══════════════╦═══════════════════════════════╦════════════════════════════════╗
║    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()         ║
║              ║                               ║                                ║
╚══════════════╩═══════════════════════════════╩════════════════════════════════╝
  • 참조 최대 절전 모드 포럼 지속과 저장 사이의 미묘한 차이에 대한 설명을 위해. 차이점은 삽입 문이 궁극적으로 실행되는 시간입니다. 부터 저장 식별자를 반환하면 거래 상태에 관계없이 삽입 문을 즉시 실행해야합니다 (일반적으로 나쁜 것). 지속 식별자를 할당하기 위해 현재 실행중인 트랜잭션 이외의 진술을 실행하지 않습니다. 두 작업을 모두 저장/지속하십시오 과도 인스턴스, IE 식별자가 아직 지정되지 않은 인스턴스는 DB에 저장되지 않습니다.

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

이 링크는 좋은 방식으로 설명합니다.

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

우리 모두는 우리가 다시 볼 때 우리가 이것을 해결했을 때, 우리가 이것을 해결했음을 알고 있지만 어떻게 기억할 수 없을 정도로 충분히 겪는 문제를 가지고 있습니다.

Hibernate에서 session.saveorupdate ()를 사용할 때 발생하지 않은 비 유니 퀴 젝트 exception은 내 중 하나입니다. 복잡한 응용 프로그램에 새로운 기능을 추가하겠습니다. 내 모든 단위 테스트는 제대로 작동합니다. 그런 다음 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 3에는 merge ()가 있습니다 (Hibernate 2에는 saveorupdatecopy ()를 사용). 이 방법은 최대 절전 모드가 다른 분리 된 인스턴스의 변경 사항을 저장하려는 인스턴스로 복사하도록하므로 저장 전에 메모리의 모든 변경 사항을 병합합니다.

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

Merge는 새로 업데이트 된 인스턴스 버전에 대한 참조를 반환한다는 점에 유의해야합니다. 세션에 항목을 다시 부착하지 않습니다. 예를 들어 평등 (item == item3)을 테스트하면이 경우 거짓을 반환 할 수 있습니다. 이 시점부터 Item3과 함께 일하고 싶을 것입니다.

JPA (Java Persistence API)에는 분리 및 재 부착 된 객체의 개념이 없으며 EntityManager.persist () 및 entityManager.Merge ()를 사용합니다.

일반적으로 최대 절전 모드를 사용할 때 SaveorupDate ()가 일반적으로 내 요구에 충분하다는 것을 알았습니다. 나는 일반적으로 동일한 유형의 객체에 대한 참조를 가질 수있는 객체가있을 때만 합병을 사용하면됩니다. 가장 최근에, 예외의 원인은 코드에서 참조가 재귀하지 않았 음을 확인하는 코드였습니다. 검증의 일부로 동일한 객체를 내 세션에로드하여 오류를 일으켰습니다.

이 문제를 어디서 겪었습니까? 병합이 당신을 위해 일 했습니까? 아니면 다른 솔루션이 필요 했습니까? 항상 합병을 사용하거나 특정 사례에 필요한 경우에만 사용하는 것을 선호합니까?

실제로 최대 절전 모드의 차이 save() 그리고 persist() 방법은 우리가 사용하는 발전기 클래스에 따라 다릅니다.

Generator 클래스가 할당되면 사이에는 차이가 없습니다. save() 그리고 persist() 방법. Generator '할당 된'을 의미하기 때문에 프로그래머로서 우리는 데이터베이스 오른쪽에 저장하기 위해 기본 키 값을 제공해야합니다 [이 생성기 개념을 알고 있기를 바랍니다. Hibernate it self는 기본 키 ID 값을 데이터베이스 오른쪽에 할당합니다 [할당 된 생성기를 제외하고, Hibernate는 기본 키 ID 값을 치료하는 데 사용됩니다. save() 또는 persist() 방법은 레코드를 데이터베이스에 정상적으로 삽입하지만 말을 듣습니다. save() 메소드는 최대 절전 모드에 의해 생성되는 기본 키 ID 값을 반환 할 수 있으며

long s = session.save(k);

이 경우에 persist() 클라이언트에게 어떤 가치도주지 않을 것입니다.

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

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

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

저장()

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

persist ()

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

SaveorUpdate ()

  • 트랜잭션의 유무에 관계없이 사용될 수 있으며 Save ()와 마찬가지로 트랜잭션없이 사용되면 매핑 된 엔티티가 저장되지 않습니다. 세션을 플러시합니다.

  • 제공된 데이터를 기반으로 삽입 또는 업데이트 쿼리 결과를 얻습니다. 데이터가 데이터베이스에있는 경우 업데이트 쿼리가 실행됩니다.

업데이트()

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

merge ()

  • 최대 절전 모드 병합은 기존 값을 업데이트하는 데 사용될 수 있지만이 방법은 전달 된 엔티티 객체에서 사본을 생성하여 반환합니다. 반환 된 객체는 지속적인 컨텍스트의 일부이며 모든 변경에 대해 추적되며 전달 된 객체는 추적되지 않습니다. 이것은 다른 모든 방법의 merge ()와의 주요 차이점입니다.

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

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

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

내가 설명했듯이 이 기사, 당신은 대부분의 시간에 JPA 방법을 선호해야합니다. update 배치 처리 작업의 경우.

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

  • 과도 (신규)
  • 관리 (영구)
  • 떨어져 있는
  • 제거 (삭제)

한 상태에서 다른 상태로의 전환은 EntityManager 또는 세션 방법을 통해 수행됩니다.

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

enter image description here

최대 절전 모드 Session 모든 JPA를 구현합니다 EntityManager 방법과 다음과 같은 추가 엔티티 상태 전환 방법을 제공합니다. save, saveOrUpdate 그리고 update.

enter image description here

지속

엔티티의 상태를 과도 (신규)에서 관리 (지속)로 변경하려면 persist JPA가 제공하는 방법 EntityManager 최대 절전 모드에 의해 상속됩니다 Session.

그만큼 persist 메소드 트리거 a PersistEvent 이에 의해 처리됩니다 DefaultPersistEventListener 최대 절전 모드 이벤트 리스너.

따라서 다음 테스트 사례를 실행할 때 :

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 첫 번째 수준 캐시라고합니다.

전화 할 때 persist, 엔티티는 현재 실행중인 지속성 컨텍스트에만 연결되어 있으며 삽입물은 flush 호출됩니다.

유일한 예외는입니다 신원 생성기 엔티티 식별자를 얻을 수있는 유일한 방법이므로 인서트를 즉시 트리거합니다. 이러한 이유로, Hibernate는 Identity Generator를 사용하여 엔티티의 인서트를 배치 할 수 없습니다. 이 주제에 대한 자세한 내용은 확인하십시오 이 기사.

구하다

최대 절전 모드-특이 적 save 방법은 JPA를 사전으로 삼고 최대 절전 모드 프로젝트가 시작된 이래로 사용할 수 있습니다.

그만큼 save 메소드 트리거 a SaveOrUpdateEvent 이에 의해 처리됩니다 DefaultSaveOrUpdateEventListener 최대 절전 모드 이벤트 리스너. 따라서, save 메소드는 다음과 같습니다 update 그리고 saveOrUpdate 행동 양식.

어떻게 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 방법 호출. 그러나와는 달리 persist,, save 메소드 엔티티 식별자를 반환합니다.

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

업데이트

최대 절전 모드-특이 적 update 방법은 더러운 검사 메커니즘 플러시 시간에 엔터티 업데이트를 강제로합니다.

그만큼 update 메소드 트리거 a SaveOrUpdateEvent 이에 의해 처리됩니다 DefaultSaveOrUpdateEventListener 최대 절전 모드 이벤트 리스너. 따라서, update 메소드는 다음과 같습니다 save 그리고 saveOrUpdate 행동 양식.

어떻게 update 메소드 작업은 다음 예를 고려하십시오 Book 한 번의 트랜잭션의 엔티티는 엔티티가 분리 상태에있는 동안 수정하고 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 불필요한 업데이트를 피하기 위해

이제 분리 상태에서 엔티티가 변경되지 않은 경우에도 업데이트는 항상 실행됩니다. 이를 방지하기 위해 사용할 수 있습니다 @SelectBeforeUpdate 동면 주석이 트리거 될 a 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);
});

Hibernate 다음 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 최대 절전 모드 더러운 검사 메커니즘이 실행 된 이후 실행되면 엔티티가 수정되지 않았 음을 발견했습니다.

SaveorUpdate

최대 절전 모드-특이 적 saveOrUpdate 메소드는 단지 별칭 일뿐입니다 save 그리고 update.

그만큼 saveOrUpdate 메소드 트리거 a SaveOrUpdateEvent 이에 의해 처리됩니다 DefaultSaveOrUpdateEventListener 최대 절전 모드 이벤트 리스너. 따라서, update 메소드는 다음과 같습니다 save 그리고 saveOrUpdate 행동 양식.

이제 사용할 수 있습니다 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
    );
}

이제 위의 테스트 사례를 실행할 때 최대 절전 모드는 NonUniqueObjectException 두 번째 EntityManager 이미 포함되어 있습니다 Book 우리가 통과 한 것과 동일한 식별자가있는 엔티티 update, 그리고 지속성 컨텍스트는 동일한 엔티티의 두 가지 표현을 보유 할 수 없습니다.

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)

병합

피하기 위해 NonUniqueObjectException, 당신은 그것을 사용해야합니다 merge JPA가 제공하는 방법 EntityManager 최대 절전 모드에 의해 상속됩니다 Session 또한.

설명 된 바와 같이 이 기사,, merge 지속성 컨텍스트에 발견 된 엔티티 참조가 없으면 데이터베이스에서 새 엔티티 스냅 샷을 가져오고 분리 된 엔티티의 상태를 복사합니다. merge 방법.

그만큼 merge 메소드 트리거 a MergeEvent 이에 의해 처리됩니다 DefaultMergeEventListener 최대 절전 모드 이벤트 리스너.

어떻게 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

엔티티 참조가 merge 우리가 전달한 분리 된 것과 다릅니다. merge 방법.

이제 JPA 사용을 선호해야합니다 merge 분리 된 엔티티 상태를 복사 할 때 추가 SELECT 배치 처리 작업을 실행할 때 문제가 될 수 있습니다.

이러한 이유로 사용하는 것을 선호해야합니다 update 현재 실행중인 지속성 컨텍스트에 이미 첨부 된 엔티티 참조가 없으며 분리 된 엔티티가 수정되었다고 확신합니다.

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

결론

엔티티를 유지하려면 JPA를 사용해야합니다. persist 방법. 분리 된 엔티티 상태를 복사하려면 merge 선호해야합니다. 그만큼 update 방법은 배치 처리 작업에만 유용합니다. 그만큼 save 그리고 saveOrUpdate 단지 별칭입니다 update 그리고 당신은 아마 그것들을 전혀 사용해서는 안됩니다.

일부 개발자는 전화를합니다 save 엔티티가 이미 관리되었지만 실수이며 관리 엔티티의 경우 업데이트가 지속성 컨텍스트 플러시 시간에서 자동으로 처리되므로 중복 이벤트를 트리거합니다.

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

다음 답변 중 어느 것도 옳지 않습니다. 이 모든 방법은 비슷해 보이지만 실제로는 절대적으로 다른 일을합니다. 짧은 의견을 제시하기는 어렵습니다. 이러한 방법에 대한 전체 문서에 대한 링크를 제공하는 것이 좋습니다.http://docs.jboss.org/hibernate/core/3.6/reference/en-us/html/objectstate.html

위의 답변 중 어느 것도 완료되지 않았습니다. Leo Theobald 답변은 가장 가까운 답변처럼 보이지만.

기본 요점은 최대 절전 모드가 기업 상태를 다루는 방법과 상태 변화가있을 때 어떻게 처리하는지입니다. 플러시와 커밋에 대해서도 모든 것이보아야하며, 모든 사람들이 완전히 무시한 것 같습니다.

최대 절전 모드 저장 방법을 사용하지 마십시오. 최대 절전 모드에 존재한다는 것을 잊어 버리십시오!

지속

모든 사람이 설명했듯이, 기본적으로 기본적으로 엔티티를 "과도"상태에서 "관리"상태로 전환합니다. 이 시점에서 슬러시 또는 커밋은 삽입 문을 만들 수 있습니다. 그러나 엔티티는 여전히 "관리 된"상태로 유지 될 것입니다. 플러시로 변하지 않습니다.

이 시점에서 다시 "지속"하면 변화가 없을 것입니다. 그리고 우리가 지속적인 엔티티를 지속하려고한다면 더 이상 절약 할 수 없습니다.

우리가 개체를 퇴거 시키려고 할 때 재미가 시작됩니다.

퇴거는 엔티티를 "관리"에서 "분리"로 전환하는 최대 절전 모드의 특수 기능입니다. 우리는 분리 된 엔티티에서 지속을 부를 수 없습니다. 우리가 그렇게하면, 최대 절전 모드는 예외를 제기하고 Commit에서 전체 거래가 롤백됩니다.

병합 대 업데이트

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

분리 된 것은 일종의 "오프라인"상태를 의미한다는 사실을 이해합니다. 관리 수단 "온라인"상태.

아래 코드를 관찰하십시오.

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

이렇게 할 때? 무슨 일이 일어날 것이라고 생각하십니까? 당신이 이것이 예외를 제기한다고 말하면, 당신은 정확합니다. Merge가 분리 된 상태 인 Entity Object에서 작업했기 때문에 예외가 발생합니다. 그러나 그것은 물체의 상태를 바꾸지 않습니다.

현장 뒤에서 Merge는 선택한 쿼리를 제기하고 기본적으로 첨부 된 상태에있는 엔티티의 사본을 반환합니다. 아래 코드를 관찰하십시오.

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

위의 샘플은 Merge가 새로운 엔티티를 지속적으로 유지하는 맥락으로 가져 왔기 때문에 작동합니다.

업데이트가 적용될 때 업데이트는 실제로 Merge와 같은 엔티티 사본을 가져 오지 않기 때문에 동일한 작업이 잘 작동합니다.

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

Debug Trace에서 동시에 업데이트가 Select Like Merge의 SQL 쿼리가 올라지지 않았 음을 알 수 있습니다.

삭제

위의 예에서는 삭제에 대해 이야기하지 않고 삭제를 사용했습니다. 삭제는 기본적으로 엔티티를 관리 상태에서 "제거 된"상태로 전환합니다. 플러시되거나 소집되면 삭제 명령을 내릴 것입니다.

그러나 지속 방법을 사용하여 엔티티를 "제거 된"상태에서 "관리"상태로 되돌릴 수 있습니다.

위의 설명이 의심을 명확히하기를 바랍니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top