a APP e o Hibernate fornecem diferentes métodos para persistir novas e atualizar as entidades existentes. Você pode escolher entre os métodos de gravação e atualização da JPA persistir e fundir e hibernar.
parece que existem 2 pares de 2 métodos que fazem o mesmo. Você pode usar os métodos persistir e salvar para armazenar uma nova entidade e os métodos juntar e atualizar para armazenar as alterações de uma entidade separada no banco de dados. É por isso que muitos desenvolvedores estão se perguntando qual desses métodos eles devem usar. Vamos dar uma olhada mais de perto nos detalhes e pequenas diferenças destes métodos.
Special thanks to Steve Ebersole (Lead Developer – Hibernate ORM) who provided his feedback and great insights on some of Hibernate’s hidden implementation details!
- transições do Estado da entidade
- persistir uma nova entidade usando persistir ou salvar
- Especificação vs. API proprietária
- Return Types And Execution Of SQL Statements
- não é gerado
- Gerado com a IDENTIDADE estratégia
- Gerado com a SEQUÊNCIA de estratégia
- gerado com a estratégia da tabela
- qual escolher?
- actualizando uma entidade destacada
- o método de fusão da App
- Hibernate’s update method
- qual escolher?Não há resposta geral a estas perguntas. Como já viram, ambos os métodos têm as suas vantagens e desvantagens. Você tem que decidir para o seu caso de uso específico se o Hibernate precisa selecionar a entidade antes que ela acione a instrução de atualização SQL. E se for esse o caso, você também precisa considerar a profundidade de seu gráfico de entidade e as implicações de desempenho do comportamento de obtenção fornecido. actualizar uma entidade gerida
transições do Estado da entidade
Antes de entrarmos nos detalhes destes 4 métodos, preciso dar-lhe uma rápida introdução aos estados do ciclo de vida da entidade da App.
se uma entidade estiver ligada ao contexto de persistência actual, tem o estado do ciclo de vida gerido. Isso significa que está mapeado para um registro de banco de dados. O seu fornecedor de persistência gera as declarações de inserção e atualização de SQL necessárias para propagar todas as alterações. Uma entidade gerenciada também é armazenada no cache de 1º nível.
quando você cria uma nova entidade, ela está no estado transitório. Ele permanece neste estado até que você o anexe ao atual contexto de persistência. Vou mostrar como você pode fazer isso com a persistência da JPA e o método de poupança Hibernate, na seção seguinte. Enquanto uma entidade estiver no estado transitório, ela não é mapeada para um registro de banco de dados e não é gerenciada por qualquer contexto de persistência.
as Entidades no estado do ciclo de vida separado já não são geridas pelo contexto da persistência. Esse pode ser o caso porque você fechou o contexto de persistência ou você separou explicitamente a entidade do contexto atual. Eu vou entrar em mais detalhes sobre como você pode re-ligar essas entidades com o merge da JPA e os métodos de atualização do Hibernate em uma parte posterior deste post.
e o último estado do ciclo de vida é removido. Essas entidades estavam anteriormente no estado gerido, antes de você agendá-los para a remoção. Remover entidades está fora do escopo deste post, então eu não vou entrar em muitos detalhes sobre isso. Você pode agendar uma entidade para remoção, invocando o método de remoção na interface EntityManager.
persistir uma nova entidade usando persistir ou salvar
quando você cria um novo objeto de entidade, ele está no estado transitório do ciclo de vida. Ele não mapeia nenhum registro de banco de dados.
Author a = new Author();a.setFirstName("Thorben");a.setLastName("Janssen");
você precisa anexar a entidade a um contexto de persistência para que ela se torne gerenciada e seja persistida na base de dados. Você pode usar o método de gravação da JPA ou Hibernate para fazer isso. Ambos os métodos parecem fazer o mesmo, mas há algumas diferenças.
Especificação vs. API proprietária
a diferença mais óbvia é que a especificação da App define o método de persistência. Você pode usá-lo com todas as implementações da App. O método save, por outro lado, é hibernado-específico. Não está, portanto, Disponível em outras implementações da App.
mas isso só é relevante se você quiser ser capaz de substituir Hibernate por outra implementação da APP, como Eclipse Link ou OpenJPA.
Return Types And Execution Of SQL Statements
Another obvious difference between these 2 methods is their return type. O método de persistência da JPA devolve nulo e o método de gravação do Hibernate devolve a chave primária da entidade.
Que pode parecer uma grande diferença, especialmente quando você olhar mais de perto o Javadoc e a especificação JPA:
- O Javadoc do Hibernate salvar indica que o método gera o valor da chave primária primeira:
manter o dado instância transiente, primeiro atribuir um identificador gerado.
Javadoc Session.gravar (entidade) - você não encontra nenhuma informação sobre isso na especificação da App. Ele não define quando o valor de chave primária tem que ser atribuído. Assim, o provedor de persistência pode fazer isso a qualquer momento entre a chamada do método persistir e o flush do contexto de persistência.
na maioria dos casos, não faz qualquer diferença se você chamar o método save ou persistir. Hibernate usa o nome da classe de entidade e o valor de chave primária para armazenar a entidade no cache de primeiro nível. Por conseguinte, necessita de um valor-chave primário quando executa o método persistir.
em quase todas as situações, o Hibernato gera o valor chave primário imediatamente e despoleta uma declaração SQL se necessário, quando você chama o método persist ou save.
mas esse não é o caso se você usar a estratégia de identidade e tentar persistir uma entidade sem uma transação ativa ou com FlushMode.MANUAL. Se você chamar o método persist em uma dessas situações, hibernar atrasa a execução da instrução SQL INSERT e cria um valor de chave primária temporário. Mas se você chamar o método de gravação, Hibernate executa a instrução SQL INSERT imediatamente e recupera o valor principal da chave do banco de dados.
você pode então recuperá-lo como o valor de retorno do método save.
Author a = new Author();a.setFirstName("Thorben");a.setLastName("Janssen");Long id = (Long) em.unwrap(Session.class).save(a);
ou pode chamar o método getter do atributo chave principal da sua entidade gerida se utilizar o método persistentemente da App.
Author a = new Author();a.setFirstName("Torben");a.setLastName("Janssen");em.persist(a);Long id = a.getId();
Hibernate executa as mesmas declarações SQL quando você chama o método persist ou save. O que e quando isso acontecer depende da sua estratégia de geração de chave primária:
não é gerado
se você definir o valor-chave primário programaticamente, por exemplo, para um identificador natural, o Hibernate só executa uma declaração de inserção SQL quando ele descarrega o contexto de persistência.
14:08:34,979 INFO TestPersistSaveMerge:237 - Save entity14:08:35,052 INFO TestPersistSaveMerge:240 - Commit transaction14:08:35,123 DEBUG SQL:92 - insert into Author (firstName, lastName, version, id) values (?, ?, ?, ?)
Gerado com a IDENTIDADE estratégia
Se você usar a IDENTIDADE estratégia para gerar o valor de chave primária, o Hibernate precisa para executar a instrução INSERT quando você chamar o salvar ou persistir método para obter o valor da chave primária do banco de dados.
14:09:28,264 INFO TestPersistSaveMerge:237 - Save entity14:09:28,336 DEBUG SQL:92 - insert into Author (firstName, lastName, version) values (?, ?, ?)14:09:28,354 INFO TestPersistSaveMerge:240 - Commit transaction
Gerado com a SEQUÊNCIA de estratégia
E se você usar a SEQUÊNCIA, o Hibernate executa uma instrução SQL SELECT para obter o próximo valor da seqüência de banco de dados. Hibernar então atrasa a declaração de inserção até que ele Descarregue o contexto de persistência. Neste exemplo, o flush acontece quando a transação é comprometida.
14:10:27,994 INFO TestPersistSaveMerge:237 - Save entity14:10:28,002 DEBUG SQL:92 - select nextval ('hibernate_sequence')14:10:28,042 INFO TestPersistSaveMerge:240 - Commit transaction14:10:28,096 DEBUG SQL:92 - insert into Author (firstName, lastName, version, id) values (?, ?, ?, ?)
gerado com a estratégia da tabela
não deve usar a estratégia da tabela porque requer bloqueios ao nível de linha na tabela de chave primária e não tem uma escala boa. Se você usar esta estratégia de qualquer maneira, o Hibernate executa uma instrução SQL SELECT para obter o próximo valor primário da chave do banco de dados e escreve o novo valor para a tabela de banco de dados. Atrasa a execução da instrução SQL INSERT para a nova entidade até que ela Descarregue o contexto de persistência.
14:11:17,368 INFO TestPersistSaveMerge:237 - Save entity14:11:17,482 DEBUG SQL:92 - select tbl.next_val from hibernate_sequences tbl where tbl.sequence_name=? for update of tbl14:11:17,531 DEBUG SQL:92 - insert into hibernate_sequences (sequence_name, next_val) values (?,?)14:11:17,534 DEBUG SQL:92 - update hibernate_sequences set next_val=? where next_val=? and sequence_name=?14:11:17,584 INFO TestPersistSaveMerge:240 - Commit transaction14:11:17,655 DEBUG SQL:92 - insert into Author (firstName, lastName, version, id) values (?, ?, ?, ?)
qual escolher?
você pode esperar que o método save e persistam se comportem de forma diferente, porque há algumas diferenças entre a especificação JPA e o Javadoc dos métodos proprietários do Hibernate.
mas quase todas estas diferenças desaparecem quando olhamos para a implementação interna. Os únicos que restam são os casos de dois cantos em que hibernar pode atrasar a recuperação da chave primária, o tipo de retorno do método e o suporte por outras implementações da App.
para a maioria das aplicações, não faz qualquer diferença se você obter o valor da chave primária gerada como o tipo de retorno do método de gravação do hibernado ou do método getter do seu atributo chave primária. Desde que você não use um contexto de persistência estendida e execute todas as operações de banco de dados com uma transação ativa, eu recomendo usar o método de persistência da JPA.
actualizando uma entidade destacada
quando fecha o contexto de persistência actual ou remove explicitamente uma entidade dele, invocando os métodos claros ou separados na interface do Gestor de direitos, a entidade torna-se destacada. Isso significa que ele não é mais armazenado no cache de 1º nível e que o Hibernate não irá replicar nenhuma das alterações aplicadas ao banco de dados.
pode usar a actualização do Hibernate ou o método de junção da App para associar uma entidade destacada a um contexto de persistência. Depois de ter feito isso, o Hibernate irá atualizar o banco de dados com base nos valores dos atributos da entidade.
o efeito do método de atualização e junção parece ser o mesmo, mas como você verá nas seguintes seções, há uma diferença importante.
o método de fusão da App
o método de fusão da App copia o estado de uma entidade destacada para uma instância gerida da mesma entidade. Hibernar, portanto, executa uma instrução SQL SELECT para recuperar uma entidade gerenciada do banco de dados. Se o contexto de persistência já continha uma instância gerenciada da entidade, Hibernate usa a existente em vez disso. Em seguida, copia todos os valores de atributos para a entidade gerenciada e retorna-lo para o chamador.
Author managedAuthor = em.merge(a);
depois de activar o registo das declarações SQL, poderá ver as declarações de selecção e actualização executadas no resultado do registo.
11:37:21,172 DEBUG SQL:92 - select books0_.bookId as bookId1_2_0_, books0_.authorId as authorId2_2_0_, book1_.id as id1_1_1_, book1_.fk_author as fk_autho6_1_1_, book1_.format as format2_1_1_, book1_.publishingDate as publishi3_1_1_, book1_.title as title4_1_1_, book1_.version as version5_1_1_, author2_.id as id1_0_2_, author2_.firstName as firstNam2_0_2_, author2_.lastName as lastName3_0_2_, author2_.version as version4_0_2_ from BookAuthor books0_ inner join Book book1_ on books0_.authorId=book1_.id left outer join Author author2_ on book1_.fk_author=author2_.id where books0_.bookId=?11:37:21,180 INFO TestPersistSaveMerge:82 - Before commit11:37:21,182 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?
quando hibernar descarrega o contexto de persistência pela próxima vez, o seu mecanismo de verificação suja verifica todas as entidades geridas. Se detectar que a operação de junção alterou qualquer valor do atributo entidade, acciona a instrução de actualização SQL necessária.
há um detalhe importante que você precisa saber quando você usa o método de junção da JPA. O Hibernate copia os valores dos atributos da entidade destacada para a entidade gerida. Isto substitui quaisquer alterações que tenha realizado nesta entidade na sessão actual.
Hibernate’s update method
Hibernate’s update method doesn’t trigger an SQL SELECT statement. Apenas liga a entidade ao contexto de persistência atual. Em contraste com o método de junção da JPA, você não pode perder quaisquer alterações chamando o método de atualização. Se o contexto de persistência já contém uma instância gerenciada da entidade que você deseja atualizar, ele lança uma exceção.
em.unwrap(Session.class).update(a);
When Hibernate performs the next flush, it doesn’t perform any dirty checks. Isso não é possível porque o Hibernate não leu a última versão da entidade da base de dados. Ele apenas executa uma declaração de atualização SQL para a entidade recolocada.
11:38:28,151 INFO TestPersistSaveMerge:121 - Before commit11:38:28,153 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?
a verificação suja em falta causa uma declaração de actualização SQL desnecessária quando a entidade e o registo de base de dados correspondente contêm os mesmos valores. Isso pode ser um problema se o seu DBA registrou um gatilho de atualização para a tabela de banco de dados. Nestas situações, poderá anotar a sua entidade com o @SelectBeforeUpdate.
@Entity@SelectBeforeUpdatepublic class Author { ... }
que diz ao Hibernate para seleccionar a entidade e efectuar uma verificação suja antes de gerar a declaração de actualização SQL. Como você pode ver na saída de log, o comportamento do método de atualização é agora semelhante ao método de junção da JPA.
19:08:16,530 INFO TestPersistSaveMerge:121 - Before commit19:08:16,531 DEBUG SQL:92 - select author_.id, author_.firstName as firstNam2_0_, author_.lastName as lastName3_0_, author_.version as version4_0_ from Author author_ where author_.id=?19:08:16,592 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?
mas há uma diferença significativa entre os dois métodos. Quando você chamar o método de atualização, o Hibernate só selecionará a entidade que você forneceu como um parâmetro de método. Mas quando você chamar o método de junção da JPA, a Hibernate também selecionará todas as associações com CascadeType.MESCLAR. Você deve, portanto, preferir o método de junção da JPA se você recolocar um enorme gráfico de entidades.
qual escolher?Não há resposta geral a estas perguntas. Como já viram, ambos os métodos têm as suas vantagens e desvantagens. Você tem que decidir para o seu caso de uso específico se o Hibernate precisa selecionar a entidade antes que ela acione a instrução de atualização SQL. E se for esse o caso, você também precisa considerar a profundidade de seu gráfico de entidade e as implicações de desempenho do comportamento de obtenção fornecido.
actualizar uma entidade gerida
app e Hibernate torna muito fácil a actualização de uma entidade gerida. Se a sua entidade estiver no estado do ciclo de vida gerido, por exemplo, porque a obteve com uma consulta JPQL ou com o método find do EntityManager, só precisa de alterar os valores dos seus atributos de entidade.
em = emf.createEntityManager();em.getTransaction().begin();a = em.find(Author.class, a.getId());a.setFirstName("Thorben");log.info("Before commit");em.getTransaction().commit();em.close();
quando o Hibernate decide limpar o contexto de persistência, o mecanismo de verificação por defeito irá detectar a alteração e executar a declaração de actualização SQL necessária.
11:41:49,178 DEBUG SQL:92 - select author0_.id as id1_0_0_, author0_.firstName as firstNam2_0_0_, author0_.lastName as lastName3_0_0_, author0_.version as version4_0_0_ from Author author0_ where author0_.id=?11:41:49,191 INFO TestPersistSaveMerge:335 - Before commit11:41:49,193 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?
você não precisa, e você não deve chamar o método de poupança do Hibernate depois de atualizar uma entidade. Isso despoleta um evento SaveOrUpdate adicional sem proporcionar quaisquer benefícios. Quando o Hibernate decide limpar o contexto de persistência, irá efectuar a verificação suja de qualquer forma para detectar todas as alterações antes de executar as instruções de actualização SQL necessárias.