Quais são as diferenças entre os diferentes métodos de economia no Hibernate?

199

O Hibernate possui vários métodos que, de uma maneira ou de outra, pegam seu objeto e o colocam no banco de dados. Quais são as diferenças entre eles, quando usar quais e por que não existe apenas um método inteligente que saiba quando usar o quê?

Os métodos que eu identifiquei até agora são:

  • save()
  • update()
  • saveOrUpdate()
  • saveOrUpdateCopy()
  • merge()
  • persist()
Henrik Paul
fonte

Respostas:

117

Aqui está o meu entendimento dos métodos. Principalmente, eles são baseados na API, já que eu não uso todos eles na prática.

saveOrUpdate As chamadas são salvas ou atualizadas dependendo de algumas verificações. Por exemplo, se não houver identificador, save será chamado. Caso contrário, a atualização é chamada.

salvar Persiste uma entidade. Atribuirá um identificador se não existir. Se alguém faz, está essencialmente fazendo uma atualização. Retorna o ID gerado da entidade.

update Tenta persistir a entidade usando um identificador existente. Se nenhum identificador existir, acredito que uma exceção seja lançada.

saveOrUpdateCopy Isso foi descontinuado e não deve mais ser usado. Em vez disso, há ...

fundir Agora, este é o lugar onde meu conhecimento começa a vacilar. O importante aqui é a diferença entre entidades transitórias, desanexadas e persistentes. Para mais informações sobre os estados do objeto, dê uma olhada aqui . Com salvar e atualizar, você está lidando com objetos persistentes. Eles estão vinculados a uma sessão para que o Hibernate saiba o que mudou. Mas quando você tem um objeto transitório, não há sessão envolvida. Nesses casos, você precisa usar a mesclagem para atualizações e persistir para salvar.

persistir Como mencionado acima, isso é usado em objetos transitórios. Ele não retorna o ID gerado.

Lee Theobald
fonte
22
Gostaria de aceitar isso como a resposta, mas uma coisa ainda não está clara: como save () recorre à atualização (), se esse item existe, como ele difere de saveOrUpdate () na prática?
Henrik Paul
Onde é especificado, esse salvamento funcionaria em instâncias desanexadas?
jrudolph
2
Se sua descrição de mesclagem / persistência é importante apenas em objetos transitórios, isso faz muito sentido e se encaixa na maneira como usamos a hibernação. Observe também que uma mesclagem geralmente apresenta limitações de desempenho em comparação com uma atualização, pois parece fazer uma busca extra para verificações de integridade de algum tipo.
Martin Dale Lyness
1
A resposta de jrudolph abaixo é mais precisa.
Azerole 10/09/12
2
Dado que o hibernate provavelmente sabe em que estado um objeto está, por que precisamos fazer isso manualmente ao escrever o programa. Deve haver apenas um método de salvamento.
masterxilo
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()         
                                                                             
╚══════════════╩═══════════════════════════════╩════════════════════════════════╝
Sergii Shevchyk
fonte
updateum objeto transitório é bom, não recebi uma exceção.
GMsoF 24/01
O que eu sei, não podemos persistir um transitório de qualquer maneira. Eu acho que poderia haver diferença entre desapegado e persistente. Por favor me corrija.
Ram
existem muitos erros aqui ... por exemplo, 1) ´save () ´ não retorna um "objeto anexado", ele retorna o id; 2) 'persistist ()' não é garantido para definir o 'id', nem "persiste o objeto no DB"; ...
Eugen Labun
67
  • Consulte o Fórum do Hibernate para obter uma explicação das diferenças sutis entre persistir e salvar. Parece que a diferença é o momento em que a instrução INSERT é finalmente executada. Como o save retorna o identificador, a instrução INSERT deve ser executada instantaneamente, independentemente do estado da transação (que geralmente é uma coisa ruim). Persist não executará nenhuma instrução fora da transação atualmente em execução apenas para atribuir o identificador. Salvar / Persistir funcionam em instâncias transitórias , ou seja, instâncias que ainda não possuem um identificador atribuído e, como tal, não são salvas no banco de dados.

  • Atualizar e mesclar funcionam em instâncias desanexadas , ou seja, instâncias que possuem uma entrada correspondente no banco de dados, mas que atualmente não estão anexadas a (ou gerenciadas por) uma Sessão. A diferença entre eles é o que acontece com a instância que é passada para a função. update tenta anexar novamente a instância, o que significa que não deve haver nenhuma outra instância da entidade persistente conectada à sessão no momento, caso contrário, uma exceção será lançada. mesclar , no entanto, apenas copia todos os valores para uma instância persistente na sessão (que será carregada se não estiver carregada no momento). O objeto de entrada não é alterado. Então mesclar é mais geral que atualizar, mas pode usar mais recursos.

jrudolph
fonte
instrução de inserção ainda não acontece se você tem seu próprio gerador Id
kommradHomer
Portanto, no caso de um objeto desanexado, a mesclagem acionará uma seleção, enquanto a atualização não será?
Gab 17/03
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.Você pode me dizer como uma inserção pode ocorrer fora de uma sessão e por que é ruim?
Erran Morad
Isenção de responsabilidade: não uso o hibernate há muito tempo. Na IMO, a questão é esta: a assinatura e o contrato de save () exigem que save retorne um identificador para o novo objeto. Dependendo da estratégia de geração de identificação escolhida, o identificador é gerado pelo banco de dados quando um valor é INSERTed. Consequentemente, nesses casos, você não pode retornar um identificador agora sem que ele seja gerado. Para gerá-lo, você deve executar INSERT agora . Como uma transação de longa execução não é executada no momento, mas apenas no commit, a única maneira de executar o INSERTnow é executá-la fora do TX.
precisa saber é o seguinte
12

Este link explica de maneira adequada:

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

Todos nós temos os problemas que encontramos com pouca frequência que, quando os vemos novamente, sabemos que resolvemos isso, mas não conseguimos lembrar como.

A NonUniqueObjectException lançada ao usar Session.saveOrUpdate () no Hibernate é uma das minhas. Vou adicionar novas funcionalidades a um aplicativo complexo. Todos os meus testes de unidade funcionam bem. Então, ao testar a interface do usuário, tentando salvar um objeto, começo a receber uma exceção com a mensagem "um objeto diferente com o mesmo valor de identificador já estava associado à sessão". Aqui está um exemplo de código do Java Persistence with 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));
            session2.update(item); // Throws NonUniqueObjectException
            tx2.commit();
            session2.close();

Para entender a causa dessa exceção, é importante entender os objetos desanexados e o que acontece quando você chama saveOrUpdate () (ou apenas atualiza ()) em um objeto desanexado.

Quando fechamos uma sessão individual do Hibernate, os objetos persistentes com os quais trabalhamos são desanexados. Isso significa que os dados ainda estão na memória do aplicativo, mas o Hibernate não é mais responsável pelo rastreamento de alterações nos objetos.

Se modificarmos nosso objeto desanexado e quisermos atualizá-lo, teremos que anexar novamente o objeto. Durante esse processo de recolocação, o Hibernate verificará se há outras cópias do mesmo objeto. Se encontrar alguma, precisa nos dizer que não sabe mais o que é a cópia “real”. Talvez outras alterações tenham sido feitas nas outras cópias que esperamos que sejam salvas, mas o Hibernate não as conhece, porque não as estava gerenciando na época.

Em vez de salvar possíveis dados incorretos, o Hibernate nos fala sobre o problema via NonUniqueObjectException.

Então, o que devemos fazer? No Hibernate 3, temos merge () (no Hibernate 2, use saveOrUpdateCopy ()). Este método forçará o Hibernate a copiar quaisquer alterações de outras instâncias desanexadas para a instância que você deseja salvar e, assim, mescla todas as alterações na memória antes da gravação.

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

É importante observar que a mesclagem retorna uma referência à versão recém-atualizada da instância. Não está anexando novamente o item à sessão. Se você testar por exemplo a igualdade (item == item3), verá que ele retornará false nesse caso. Você provavelmente desejará trabalhar com o item3 deste ponto em diante.

Também é importante observar que a JPA (Java Persistence API) não tem um conceito de objetos desanexados e reconectados e usa EntityManager.persist () e EntityManager.merge ().

Descobri em geral que, ao usar o Hibernate, saveOrUpdate () geralmente é suficiente para minhas necessidades. Normalmente, só preciso usar a mesclagem quando tenho objetos que podem ter referências a objetos do mesmo tipo. Mais recentemente, a causa da exceção estava no código validando que a referência não era recursiva. Eu estava carregando o mesmo objeto na minha sessão como parte da validação, causando o erro.

Onde você encontrou esse problema? A mesclagem funcionou para você ou você precisava de outra solução? Você prefere sempre usar a mesclagem ou prefere usá-lo apenas conforme necessário para casos específicos

HakunaMatata
fonte
Link para o artigo sobre webarchive, uma vez que o original não está disponível: web.archive.org/web/20160521091122/http://www.stevideter.com:80/…
Eugen Labun
5

Na verdade, a diferença entre hibernação save()e persist()métodos depende da classe de gerador que estamos usando.

Se nossa classe gerador é atribuído, então não há nenhuma diferença entre save()e persist(métodos). Como o gerador 'atribuído' significa, como programador, precisamos fornecer o valor da chave primária para salvar no banco de dados corretamente [Espero que você conheça esse conceito de geradores] No caso de uma classe geradora diferente da atribuída, suponha que se o nome da nossa classe de gerador for Incrementar O hibernate ele próprio atribuirá o valor do ID da chave primária no banco de dados à direita [exceto o gerador atribuído, o hibernate usado apenas para cuidar do valor do ID da chave primária, lembre-se], portanto, neste caso, se chamarmos save()ou persist()método, ele inserirá o registro em o banco de dados normalmente Mas, ouça, o save()método pode retornar o valor do ID da chave primária que é gerado pelo hibernate e podemos vê-lo por

long s = session.save(k);

Nesse mesmo caso, persist()nunca devolverá nenhum valor ao cliente.

Hari Krishna
fonte
5

Encontrei um bom exemplo mostrando as diferenças entre todos os métodos de salvamento do hibernate:

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

Em resumo, de acordo com o link acima:

Salve ()

  • Podemos invocar esse método fora de uma transação. Se usarmos isso sem transação e tivermos uma cascata entre entidades, somente a entidade principal será salva, a menos que liberemos a sessão.
  • Portanto, se houver outros objetos mapeados a partir do objeto principal, eles serão salvos no momento da confirmação da transação ou quando liberamos a sessão.

persistir()

  • É semelhante ao uso de save () na transação, por isso é seguro e cuida de qualquer objeto em cascata.

saveOrUpdate ()

  • Pode ser usado com ou sem a transação e, assim como save (), se usado sem a transação, as entidades mapeadas não serão salvas;

  • Resultados em consultas de inserção ou atualização com base nos dados fornecidos. Se os dados estiverem presentes no banco de dados, a consulta de atualização será executada.

atualizar()

  • A atualização de hibernação deve ser usada quando sabemos que estamos atualizando apenas as informações da entidade. Esta operação adiciona o objeto de entidade ao contexto persistente e outras alterações são rastreadas e salvas quando a transação é confirmada.
  • Portanto, mesmo depois de chamar update, se definirmos algum valor na entidade, eles serão atualizados quando a transação for confirmada.

mesclar ()

  • A mesclagem de hibernação pode ser usada para atualizar valores existentes; no entanto, esse método cria uma cópia do objeto de entidade passado e o retorna. O objeto retornado faz parte do contexto persistente e é rastreado para quaisquer alterações; o objeto passado não é rastreado. Essa é a principal diferença com merge () de todos os outros métodos.

Também para exemplos práticos de todos estes, consulte o link que mencionei acima, mostra exemplos para todos esses métodos diferentes.

OutOfMind
fonte
3

Como expliquei neste artigo , você deve preferir os métodos JPA na maioria das vezes, e osupdate tarefas de processamento em lote.

Uma entidade JPA ou Hibernate pode estar em um dos quatro estados a seguir:

  • Transitório (novo)
  • Gerenciado (persistente)
  • Independente
  • Removido (excluído)

A transição de um estado para outro é feita pelos métodos EntityManager ou Session.

Por exemplo, a JPA EntityManagerfornece os seguintes métodos de transição de estado da entidade.

insira a descrição da imagem aqui

O Hibernate Sessionimplementa todos os EntityManagermétodos JPA e fornece alguns métodos adicionais de transição de estado de entidade save, como , saveOrUpdatee update.

insira a descrição da imagem aqui

Persistir

Para alterar o estado de uma entidade de Transitório (Novo) para Gerenciado (Persistido), podemos usar o persistmétodo oferecido pela JPA, EntityManagerque também é herdado pelo Hibernate Session.

O persistmétodo dispara um PersistEventque é tratado pelo DefaultPersistEventListenerouvinte de eventos do Hibernate.

Portanto, ao executar o seguinte caso de teste:

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

O Hibernate gera as seguintes instruções 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
)

Observe que o idé atribuído antes de anexar a Bookentidade ao contexto de persistência atual. Isso é necessário porque as entidades gerenciadas são armazenadas em uma Mapestrutura em que a chave é formada pelo tipo de entidade e seu identificador e o valor é a referência da entidade. Esta é a razão pela qual o JPA EntityManagere o HibernateSession são conhecidos como cache de primeiro nível.

Ao chamar persist, a entidade é anexada apenas ao Contexto de Persistência em execução no momento, e o INSERT pode ser adiado até que oflush seja chamado.

A única exceção é o gerador IDENTITY que aciona o INSERT imediatamente, pois é a única maneira de obter o identificador da entidade. Por esse motivo, o Hibernate não pode inserir em lotes para entidades usando o gerador de IDENTITY. Para mais detalhes sobre este tópico, consulte este artigo .

Salve 

O savemétodo específico do Hibernate é anterior à JPA e está disponível desde o início do projeto Hibernate.

O savemétodo dispara um SaveOrUpdateEventque é tratado pelo DefaultSaveOrUpdateEventListenerouvinte de eventos do Hibernate. Portanto, o savemétodo é equivalente aos métodos updatee saveOrUpdate.

Para ver como o savemétodo funciona, considere o seguinte caso de teste:

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

Ao executar o caso de teste acima, o Hibernate gera as seguintes instruções 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
)

Como você pode ver, o resultado é idêntico à persistchamada do método. No entanto, diferentemente persist, o savemétodo retorna o identificador da entidade.

Para mais detalhes, consulte este artigo .

Atualizar

O updatemétodo específico do Hibernate tem como objetivo ignorar o mecanismo de verificação suja e forçar uma atualização de entidade no momento da liberação.

O updatemétodo dispara um SaveOrUpdateEventque é tratado pelo DefaultSaveOrUpdateEventListenerouvinte de eventos do Hibernate. Portanto, o updatemétodo é equivalente aos métodos savee saveOrUpdate.

Para ver como o updatemétodo funciona, considere o exemplo a seguir, que persiste uma Bookentidade em uma transação, modifica-a enquanto a entidade está no estado desanexado e força o SQL UPDATE usando a updatechamada de método.

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

Ao executar o caso de teste acima, o Hibernate gera as seguintes instruções 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

Observe que ele UPDATEé executado durante a liberação do contexto de persistência, logo antes da confirmação, e é por isso que a Updating the Book entitymensagem é registrada primeiro.

Usando @SelectBeforeUpdatepara evitar atualizações desnecessárias

Agora, o UPDATE sempre será executado, mesmo que a entidade não tenha sido alterada enquanto estiver no estado desanexado. Para evitar isso, você pode usar a @SelectBeforeUpdateanotação Hibernate, que acionará uma SELECTinstrução que buscouloaded state que é usada pelo mecanismo de verificação suja.

Portanto, se anotamos a Bookentidade com a @SelectBeforeUpdateanotação:

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

    //Code omitted for brevity
}

E execute o seguinte caso de teste:

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

O Hibernate executa as seguintes instruções 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

Observe que, desta vez, não há UPDATEexecução, pois o mecanismo de verificação suja do Hibernate detectou que a entidade não foi modificada.

SaveOrUpdate

O saveOrUpdatemétodo específico do Hibernate é apenas um alias para savee update.

O saveOrUpdatemétodo dispara um SaveOrUpdateEventque é tratado pelo DefaultSaveOrUpdateEventListenerouvinte de eventos do Hibernate. Portanto, o updatemétodo é equivalente aos métodos savee saveOrUpdate.

Agora, você pode usar saveOrUpdatequando quiser persistir em uma entidade ou forçar um UPDATEconforme ilustrado pelo exemplo a seguir.

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

Cuidado com o NonUniqueObjectException

Um problema que pode ocorrer com save, updatee saveOrUpdateé se o contexto de persistência já contém uma referência de entidade com o mesmo ID e do mesmo tipo como no exemplo a seguir:

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

Agora, ao executar o caso de teste acima, o Hibernate lançará um NonUniqueObjectExceptionporque o segundo EntityManagerjá contém uma Bookentidade com o mesmo identificador que passamos update, e o Contexto de Persistência não pode conter duas representações da mesma entidade.

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)

Mesclar

Para evitar isso NonUniqueObjectException, você precisa usar o mergemétodo oferecido pela JPA EntityManagere herdado pelo Hibernate Sessiontambém.

Conforme explicado neste artigo , ele mergebusca um novo instantâneo de entidade do banco de dados se não houver uma referência de entidade encontrada no Contexto de Persistência e copia o estado da entidade desanexada passada para o mergemétodo.

O mergemétodo dispara um MergeEventque é tratado pelo DefaultMergeEventListenerouvinte de eventos do Hibernate.

Para ver como o mergemétodo funciona, considere o exemplo a seguir, que persiste em umBook entidade em uma transação, modificando-o enquanto a entidade está no estado desanexado e passando a entidade desanexada para mergeum Contexto de Persistência subsequente.

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

Ao executar o caso de teste acima, o Hibernate executou as seguintes instruções 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

Observe que a referência da entidade retornada por merge é diferente da referência desanexada que passamos para o mergemétodo.

Agora, embora você deva preferir usar o JPA merge ao copiar o estado da entidade desconectada, o extra SELECTpode ser problemático ao executar uma tarefa de processamento em lote.

Por esse motivo, você deve preferir usá- updatelo quando tiver certeza de que não há referência de entidade já anexada ao Contexto de Persistência atualmente em execução e que a entidade desanexada foi modificada.

Para mais detalhes sobre este tópico, consulte este artigo .

Conclusão

Para persistir em uma entidade, você deve usar o persistmétodo JPA . Para copiar o estado da entidade desanexada, mergedeve ser preferido. O updatemétodo é útil apenas para tarefas de processamento em lote. O savee saveOrUpdatesão apenas aliases paraupdate e você provavelmente não deve usá-los.

Alguns desenvolvedores chamam save mesmo quando a entidade já está gerenciada, mas isso é um erro e aciona um evento redundante, pois, para entidades gerenciadas, o UPDATE é tratado automaticamente no tempo de liberação do contexto de Persistência.

Para mais detalhes, consulte este artigo .

Vlad Mihalcea
fonte
2

Esteja ciente de que se você chamar uma atualização em um objeto desanexado, sempre haverá uma atualização no banco de dados, independentemente de você ter alterado ou não o objeto. Se não é o que você deseja, use Session.lock () com LockMode.None.

Você deve chamar update somente se o objeto tiver sido alterado fora do escopo da sua sessão atual (quando estiver no modo desanexado).

Bernardn
fonte
1

Nenhuma das respostas a seguir está correta. Todos esses métodos parecem iguais, mas na prática fazem coisas absolutamente diferentes. É difícil fazer comentários curtos. Melhor fornecer um link para a documentação completa sobre esses métodos: http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/objectstate.html

Anton Popovich
fonte
11
Diga-nos por que as seguintes respostas não estão corretas.
precisa saber é o seguinte
0

Nenhuma das respostas acima está completa. Embora a resposta de Leo Theobald pareça a resposta mais próxima.

O ponto básico é como o hibernate está lidando com estados de entidades e como ele lida com eles quando há uma alteração de estado. Tudo deve ser visto com relação a liberações e confirmações, que todos parecem ter ignorado completamente.

NUNCA UTILIZE O MÉTODO DE POUPANÇA DE HIBERNATE. ESQUEÇA QUE MESMO EXISTE EM HIBERNADO!

Persistir

Como todos explicaram, o Persist basicamente transita uma entidade do estado "Transitório" para o estado "Gerenciado". Nesse ponto, uma lama ou uma confirmação pode criar uma instrução de inserção. Mas a entidade ainda permanece no estado "Gerenciado". Isso não muda com o flush.

Nesse ponto, se você "persistir" novamente, não haverá alterações. E não haverá mais salvamentos se tentarmos manter uma entidade persistente.

A diversão começa quando tentamos despejar a entidade.

Um despejo é uma função especial do Hibernate que fará a transição da entidade de "Gerenciado" para "Desanexado". Não podemos chamar uma persistência em uma entidade desanexada. Se fizermos isso, o Hibernate gera uma exceção e a transação inteira é revertida no commit.

Mesclar vs Atualização

Estas são 2 funções interessantes que fazem coisas diferentes quando tratadas de maneiras diferentes. Ambos estão tentando fazer a transição da entidade do estado "Desanexado" para o estado "Gerenciado". Mas fazendo diferente.

Entenda o fato de que Desanexado significa um tipo de estado "offline". e gerenciado significa o estado "Online".

Observe o código abaixo:

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

Quando você faz isso? O que você acha que acontecerá? Se você disse que isso gerará uma exceção, está correto. Isso gerará uma exceção porque, a mesclagem funcionou no objeto de entidade, que é o estado desanexado. Mas isso não altera o estado do objeto.

Nos bastidores, a mesclagem gera uma consulta de seleção e basicamente retorna uma cópia da entidade que está no estado anexado. Observe o código abaixo:

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

O exemplo acima funciona porque a mesclagem trouxe uma nova entidade para o contexto que está no estado persistente.

Quando aplicado com o Update, o mesmo funciona bem porque o update não traz uma cópia da entidade como mesclagem.

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

Ao mesmo tempo, no rastreamento de depuração, podemos ver que o Update não levantou a consulta SQL de seleção como mesclagem.

excluir

No exemplo acima, usei delete sem falar sobre delete. A exclusão basicamente fará a transição da entidade do estado gerenciado para o estado "removido". E quando liberado ou confirmado, emitirá um comando de exclusão para armazenar.

No entanto, é possível trazer a entidade de volta ao estado "gerenciado" do estado "removido" usando o método persist.

Espero que a explicação acima tenha esclarecido qualquer dúvida.

Cerveja de gengibre
fonte