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.

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.

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 .