JPA EntityManager: Why use persist() over merge()?

EntityManager.merge() pode inserir novos objectos e actualizar os existentes.

porque haveria alguém de querer usar persist() (que só pode criar novos objectos)?

Author: Harshal Patil, 2009-07-01

15 answers

De qualquer forma irá adicionar uma entidade a um texto de persistência, a diferença está no que você faz com a entidade depois.

Persistir toma uma instância de entidade, adiciona-a ao contexto e torna essa instância gerida (ou seja, futuras actualizações à entidade serão seguidas).

A Merge cria uma nova instância da sua entidade, copia o estado da entidade fornecida e torna a nova cópia gerida. A instância que passar não será gerida (as alterações que fizer não farão parte do a transacção-a menos que volte a ligar para a junção).

Talvez um exemplo de código ajude.
MyEntity e = new MyEntity();

// scenario 1
// tran starts
em.persist(e); 
e.setSomeField(someValue); 
// tran ends, and the row for someField is updated in the database

// scenario 2
// tran starts
e = new MyEntity();
em.merge(e);
e.setSomeField(anotherValue); 
// tran ends but the row for someField is not updated in the database
// (you made the changes *after* merging)

// scenario 3
// tran starts
e = new MyEntity();
MyEntity e2 = em.merge(e);
e2.setSomeField(anotherValue); 
// tran ends and the row for someField is updated
// (the changes were made to e2, not e)
Os cenários 1 e 3 são aproximadamente equivalentes, mas há algumas situações em que você gostaria de usar o cenário 2.
 1479
Author: Mike, 2014-02-27 11:56:38

Persistir e mesclar são para dois fins diferentes (não são alternativas de todo).

(editado para expandir a informação sobre diferenças)

Persistir:

  • inserir um novo registo na base de Dados
  • anexar o objecto ao Gestor de entidades.

Juntar:

  • Encontre um objecto anexado com o mesmo id e actualize-o.
  • Se existir actualiza e devolve o objecto já anexado.
  • Se não existir, inserir o novo registe-se na base de dados.
Eficácia persistente ():
  • poderia ser mais eficiente para inserir um novo registro em uma base de dados do que merge().
  • Não duplica o objecto original.

Persistem () semânticas:

  • certifica-se de que está a inserir e não a actualizar por engano.

Exemplo:

{
    AnyEntity newEntity;
    AnyEntity nonAttachedEntity;
    AnyEntity attachedEntity;

    // Create a new entity and persist it        
    newEntity = new AnyEntity();
    em.persist(newEntity);

    // Save 1 to the database at next flush
    newEntity.setValue(1);

    // Create a new entity with the same Id than the persisted one.
    AnyEntity nonAttachedEntity = new AnyEntity();
    nonAttachedEntity.setId(newEntity.getId());

    // Save 2 to the database at next flush instead of 1!!!
    nonAttachedEntity.setValue(2);
    attachedEntity = em.merge(nonAttachedEntity);

    // This condition returns true
    // merge has found the already attached object (newEntity) and returns it.
    if(attachedEntity==newEntity) {
            System.out.print("They are the same object!");
    }

    // Set 3 to value
    attachedEntity.setValue(3);
    // Really, now both are the same object. Prints 3
    System.out.println(newEntity.getValue());

    // Modify the un attached object has no effect to the entity manager
    // nor to the other objects
    nonAttachedEntity.setValue(42);
}

Desta forma só existe 1 objecto em anexo para qualquer registo no Gestor da entidade.

Juntar () para um entidade com um id é algo como:

AnyEntity myMerge(AnyEntity entityToSave) {
    AnyEntity attached = em.find(AnyEntity.class, entityToSave.getId());
    if(attached==null) {
            attached = new AnyEntity();
            em.persist(attached);
    }
    BeanUtils.copyProperties(attached, entityToSave);

    return attached;
}

Embora se ligado ao MySQL merge() pudesse ser tão eficiente como persistir () usando uma chamada para inserir com a opção DUPLICATE KEY UPDATE, a JPA é uma programação de alto nível e você não pode assumir que isso vai ser o caso em toda parte.

 153
Author: Josep Panadero, 2013-07-15 09:11:41

Se estiver a usar o gerador atribuído, se usar a junção em vez de persistir, poderá causar uma declaração SQL redundante, afectando assim o desempenho.

Também, chamada direta para as entidades gerenciadas também é um erro, dado que as entidades gerenciadas são gerenciados automaticamente pelo Hibernate e seu estado é sincronizado com o banco de dados de registro por sujo mecanismo de verificação na liberar o Contexto persistente.

Para entender como tudo isto funciona, você deve primeiro saber que o Hibernate muda a mentalidade do desenvolvedor das declarações SQL paratransições do estado da entidade .

Uma vez que uma entidade seja gerida activamente pelo Hibernate, todas as alterações serão automaticamente propagadas para a base de dados.

Os Monitores hibernam actualmente em entidades ligadas. Mas para que uma entidade se torne gerida, ela deve estar no estado da entidade certa.

Primeiro, temos de definir todos os estados da entidade:
  • Novo (Transitório)

    Um objecto recentemente criado que nunca tenha sido associado a um hibernado {[[0]} (t. c. p. Persistence Context) e não esteja mapeado para qualquer linha de tabela de bases de dados é considerado como estando no novo estado (transitório).

    Para persistir precisamos chamar explicitamente o método EntityManager#persist ou fazer uso do mecanismo de persistência transitiva.

  • Persistente (Controlado)

    Uma entidade persistente foi associada a uma linha de tabela de bases de dados e está a ser gerida pelo contexto actual de persistência em execução. Qualquer alteração feita a essa entidade será detectada e propagada para o banco de dados (durante o tempo de descarga de sessão). Com hibernar, não temos mais que executar INSERT/UPDATE/DELETE declarações. O Hibernate emprega um estilo de trabalho e as alterações são sincronizadas no último momento responsável, durante o actual Session Tempo de descarga.

  • Desligado

    Uma vez o actual contexto de persistência em execução está fechado todas as entidades anteriormente geridas tornam-se desapegadas. Mudanças sucessivas não serão mais rastreadas e nenhuma sincronização automática de banco de dados vai acontecer.

    Para associar uma entidade separada a uma sessão de hibernação activa, poderá escolher uma das seguintes opções:

    • Recolocação

      Hibernar (mas não a JPA 2.1) suporta a recolocação através do método#update da sessão. Uma sessão de hibernação só pode associar um objecto de entidade a uma dada base de dados linha. Isto porque o contexto de persistência atua como um cache de memória (cache de primeiro nível) e apenas um valor (entidade) está associado a uma dada chave (tipo de entidade e identificador de banco de dados). Uma entidade só pode ser recolocada se não existir outro objecto JVM (correspondente à mesma linha de base de dados) já associado à sessão de hibernação actual.

    • Juntar

    A junção irá copiar o estado da entidade destacada (fonte) para uma instância da entidade gerida (destino). Se a entidade resultante da fusão não tiver equivalente na sessão actual, será obtida uma da base de dados. A instância do objecto separado continuará a ser desligada mesmo após a operação de junção.

  • Removido

    Embora a App exija que as entidades geridas só possam ser removidas, o Hibernate também pode apagar as entidades destacadas (mas apenas através de uma chamada de método#delete sessão). Uma entidade removida só está agendada para remoção e a declaração de remoção da base de dados actual será executado durante o tempo de descarga da sessão.

Para compreender melhor as transições de estado da APP, pode visualizar o seguinte diagrama:

enter image description here

Ou se utilizar a API específica do Hibernato:

enter image description here

 113
Author: Vlad Mihalcea, 2018-01-04 06:22:59

Eu notei que quando eu usei {[[0]}, eu recebi uma declaração SELECT para cada {[[2]}, mesmo quando não havia nenhum campo que a App estava gerando para mim--o campo chave primária era um UUID que eu me coloquei. Eu mudei para em.persist(myEntityObject) e recebi apenas INSERT depoimentos então.

 37
Author: Sarah Vessels, 2012-01-18 21:14:31

A especificação da App diz o seguinte sobre {[[0]}.

Se X é um objecto isolado, o EntityExistsException pode ser atirado quando o persistir. a operação é invocada, ou o EntityExistsException ou outro PersistenceException pode ser lançado na altura da descarga ou do commit.

Então usar {[[0]} seria adequado quando o objecto não deveria ser um objecto separado. Você pode preferir ter o código jogar o PersistenceException para que ele falhe rápido.

Embora a especificação seja não é claro, persist() pode definir o @GeneratedValue @Id por um objecto. merge() no entanto, deve ter um objecto com o @Id já gerado.

 28
Author: Raedwald, 2017-05-23 12:34:48

Mais alguns detalhes sobre a junção que o ajudarão a usar a junção sobre persistem:

Devolver uma instância gerida para além da entidade original é uma parte crítica da fusão processo. Se uma instância de entidade com o mesmo identificador já existir no contexto da persistência, o o fornecedor irá substituir o seu estado com o estado da entidade que está a ser objecto de fusão, mas o gestor a versão que já existia deve ser devolvida ao cliente para que possa ser usada. Se o Provedor não actualizar a instância do trabalhador no contexto da persistência, todas as referências a essa instância tornar-se-ão inconsistente com a fusão do Novo Estado.

Quando a merge() é invocada numa nova entidade, ela comporta-se de forma semelhante à operação persist (). Acrescenta a entidade ao contexto de persistência, mas em vez de adicionar a instância de entidade original, cria um novo copiar e gerenciar essa instância em vez disso. A cópia que é criada pela operação merge () é persistir como se o método persist() fosse invocado nele.

Na presença de relações, a operação merge() tentará actualizar a entidade gerida indicar as versões geridas das entidades referenciadas pela entidade destacada. Se a entidade tiver um relação com um objeto que não tem identidade persistente, o resultado da operação de fusão é indefinido. Alguns provedores podem permitir que a cópia gerenciada aponte para o objeto não-persistente, enquanto outros podem atirar uma excepção imediatamente. A operação merge() pode ser opcionalmente em cascata nestes casos para evitar a ocorrência de uma excepção. Vamos cobrir a cascata da junção() operação mais tarde nesta seção. Se uma entidade resultante da fusão apontar para uma entidade removida, um A excepção de excepção de recusa de embarque ilegal será rejeitada.

As relações de carga preguiçosa são um caso especial na operação de fusão. Se um preguiçoso-carregar a relação não foi desencadeada sobre uma entidade antes de se destacar, que a relação será ignorado quando a entidade é fundida. Se a relação foi desencadeada enquanto gerida e, em seguida, configurada como nula enquanto a entidade foi destacada, a versão gerenciada da entidade também terá a relação compensada durante a fusão."
Todas as informações acima foram tiradas da" Pro JPA 2 Mastering the Java™ Persistence API " por Mike Keith e Merrick Schnicariol. Capítulo 6. Separação e fusão da secção. Este livro é na verdade um segundo livro dedicado à App por autores. Este novo livro tem muitas novas informações, então antigo. Eu realmente recomeçou a ler este livro para aqueles que estarão seriamente envolvidos com a App. Peço desculpa por ter postado a minha primeira resposta.
 16
Author: Khurshed Salimov, 2012-10-05 14:11:52

Existem mais algumas diferenças entre merge e persist (vou enumerar novamente as já publicadas aqui):

D1. merge não torna a entidade passada gerida, mas devolve outra instância que é gerida. persist por outro lado fará com que a entidade passada seja gerida:

//MERGE: passedEntity remains unmanaged, but newEntity will be managed
Entity newEntity = em.merge(passedEntity);

//PERSIST: passedEntity will be managed after this
em.persist(passedEntity);

D2. Se você remover uma entidade e, em seguida, decidir persistir a entidade de volta, você pode fazer isso apenas com persist (), porque merge irá lançar um IllegalArgumentException.

D3. Se você decidir tome cuidado manualmente com as suas identificações (por exemplo, usando UUIDs), depois a merge a operação irá desencadear as consultas subsequentes SELECT a fim de procurar entidades existentes com esse ID, enquanto persist pode não precisar dessas consultas.

D4. Existem casos em que você simplesmente não confia no código que chama o seu código, e a fim de se certificar de que nenhum dado é atualizado, mas sim inserido, você deve usar persist.

 15
Author: Andrei I, 2013-12-02 14:13:56
Estava a receber exceções na minha entidade porque estava a tentar aceder a uma colecção preguiçosa que estava em sessão.

O que eu faria era num pedido separado, recuperar a entidade da sessão e depois tentar aceder a uma colecção na minha página jsp que era problemática.

Para aliviar isto, actualizei a mesma entidade no meu controlador e passei-a para o meu jsp, embora imagine que, quando voltar a gravar em sessão, também estará acessível embora ... lançar um LazyLoadingException, uma modificação do exemplo 2:

O seguinte funcionou para mim:

// scenario 2 MY WAY
// tran starts
e = new MyEntity();
e = em.merge(e); // re-assign to the same entity "e"

//access e from jsp and it will work dandy!!
 8
Author: logixplayer, 2012-01-21 11:01:11
Vendo as respostas, faltam alguns detalhes sobre a "Cascade" e a geração id. ver Pergunta Além disso, vale a pena mencionar que você pode ter anotações separadas {[[[0]} para a fusão e persistência: Cascade.MERGE e Cascade.PERSIST que serão tratadas de acordo com o método utilizado.

O spec é teu amigo;}

 6
Author: Ioannis Deligiannis, 2017-05-23 11:47:26

Cenário X:

Tabela: Spitter (um), tabela: Spittles (muitos) (Spittles é dono da relação com um FK:spitter_id)

Este cenário resulta em poupança: o cuspidor e ambos os cuspidores como se pertencessem ao mesmo cuspidor.
        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.addSpittle(spittle3); // <--persist     
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!

Cenário Y:

Isto salvará o cuspidor, salvará os dois cuspidores, mas eles não referirão o mesmo cuspidor!
        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.save(spittle3); // <--merge!!       
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!
 5
Author: George Papatheodorou, 2012-10-28 18:06:38

Encontrei esta explicação dos documentos hibernados esclarecedora, porque contêm um caso de uso:

O uso e semântica do merge() parece ser confuso para novos usuários. Em primeiro lugar, desde que não esteja a tentar usar o estado do objecto carregado num gestor de entidades noutro novo gestor de entidades, não deverá usar o merge() de todo. Algumas aplicações inteiras nunca usarão este método.

Normalmente, o merge () é utilizado no seguinte cenário:

  • a aplicação carrega um objecto no primeiro gestor de entidades
  • o objecto passa para a camada de apresentação
  • algumas modificações são feitas ao objecto
  • o objecto é passado para a camada lógica dos negócios
  • a aplicação persiste estas modificações ao invocar o merge () num segundo Gestor de entidades

Aqui está a semântica exacta do merge ():

  • Se houver uma instância gerida com a mesma identificador actualmente associado ao contexto de persistência, copiar o estado do objecto dado para a instância gerida
  • Se não existir uma instância gerida actualmente associada ao contexto de persistência, tente carregá-la da base de dados ou criar uma nova instância gerida
  • a instância gerida é devolvida
  • a dada instância não se associa ao contexto de persistência, mantém-se destacada e é geralmente descartada

De: http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html

 5
Author: Ray Hulha, 2016-03-18 16:28:54
A App é indiscutivelmente uma grande simplificação no domínio das empresas. aplicações construídas na plataforma Java. Como um desenvolvedor que teve que lidar com as complexidades dos antigos feijões entidade em J2EE eu vejo o inclusão da APP entre as especificações Java EE como um grande salto frente. No entanto, ao aprofundar os detalhes da App, acho que coisas que não são tão fáceis. Neste artigo eu trato da comparação de os métodos de fusão e de persistência do titular sobreposicao o comportamento pode causar confusão não só a um novato. Além Disso, propor uma generalização que considere ambos os métodos como casos especiais de método mais geral combinar.

Entidades persistentes

Em contraste com o método de junção, o método de persistência é bastante simples e intuitivo. O cenário mais comum da utilização do método persist pode ser resumido da seguinte forma:

" uma instância recentemente criada da classe de entidade é passada para a persistir metodo. Depois que este método retorna, a entidade é gerenciada e planejada para inserção no banco de dados. Pode acontecer em ou antes da transação commits ou quando o método flush é chamado. Se a entidade referenciar outra entidade através de um relacionamento marcado com a estratégia de cascata persistir, este procedimento também é aplicado a ela."

enter image description here

A especificação entra mais em detalhes, no entanto, lembrá-los não é crucial, uma vez que estes detalhes cobrem mais ou menos exóticos apenas situações.

Entidades que se fundem

Em comparação com persistir, a descrição do comportamento da fusão não é assim tão simples. Não há nenhum cenário principal, como é no caso de persistir, e um programador deve se lembrar de todos os cenários para escrever um código correto. Parece-me que os designers da App queriam ter algum método cuja principal preocupação seria lidar com entidades destacadas (como o oposto do método persistent que lida com entidades recém-criadas principalmente.) A principal tarefa do método merge é transferir o estado de uma entidade não gerenciada (passada como argumento) para sua contraparte gerenciada dentro do contexto de persistência. Esta tarefa, no entanto, divide-se ainda mais em vários cenários que agravam a inteligibilidade do comportamento geral do método.

Em vez de repetir parágrafos da especificação da App, preparei um diagrama de fluxo que descreve esquematicamente o comportamento da junção método:

enter image description here

Então, quando devo usar persistir e quando me fundir?

Persistir

  • queres que o método crie sempre uma nova entidade e nunca actualize uma entidade. Caso contrário, o método lança uma exceção como consequência da principal violação de singularidade chave.
  • processos em lote, manipulação de Entidades de uma forma de Estado (ver padrão de Gateway).
  • desempenho optimization

Juntar

  • quer que o método insira ou actualize uma entidade na base de dados.
  • você quer lidar com as Entidades de uma forma apátrida (objectos de transferência de dados nos Serviços)
  • deseja inserir uma nova entidade que possa ter uma referência a outra entidade que pode, mas ainda não pode, ser criada (a relação tem de ser marcada MERGE). Por exemplo, inserir uma nova foto com uma referência a uma nova ou pré-existente album.
 5
Author: Amit Gujarathi, 2017-06-06 12:36:08

Pode ter vindo aqui para obter conselhos sobre quando usar persistir e quando usar juntar. Eu acho que isso depende da situação: quão provável é que você precisa criar um novo registro e quão difícil é recuperar dados persistidos.

Vamos presumir que pode usar uma chave/identificador natural.
  • Os dados têm de ser persistidos, mas de vez em quando existe um registo e é necessária uma actualização. Neste caso você poderia tentar um persistir e se ele lança um EntityExistsException, procura-o e combina os dados:

    Tenta { entityManager.persistem (entidade)}

    Captura (excepção do Direito à excepção da excepção da excepção da excepção da excepção) {/*recuperação e junção */}

  • Os dados persistidos têm de ser actualizados, mas de vez em quando ainda não há registo dos dados. Neste caso, você procura e persiste se a entidade estiver faltando:

    ([1]}Entity = entityManager.find (chave);

    If (entity = null) { entityManager.persistir (entidade); }

    Else {/*merge */}

Se você não tem chave/identificador natural, você terá um tempo mais difícil para descobrir se a entidade existe ou não, ou como procurá-lo.

As fusões também podem ser tratadas de duas formas:

  1. Se as alterações são geralmente pequenas, aplica-as à entidade gerida.
  2. Se as alterações forem comuns, Copie o ID da entidade persistida, bem como os dados inalterados. Em seguida, ligue o EntityManager:: merge () para substituir o antigo conteudo.
 1
Author: Peter Willems, 2018-01-06 20:15:10

Persist(entidade) deve ser usado com entidades totalmente novas, para adicioná-las a DB (se a entidade já existe em DB haverá EntityExistsException throw).

A junção (entidade) deve ser usada, para colocar a entidade de volta ao contexto de persistência se a entidade foi destacada e foi alterada.

Provavelmente persistir está a gerar a instrução INSERT sql e a instrução merge UPDATE sql (mas não tenho a certeza).

 0
Author: Krystian, 2015-11-04 12:38:37

Outra observação:

merge() só se importará com um id gerado automaticamente(testado em IDENTITY e SEQUENCE) quando já existir um registo com esse id na sua tabela. Nesse caso {[[0]} irá tentar actualizar o registo. Se, no entanto, um id estiver ausente ou não corresponder a nenhum registro existente, merge() irá ignorá-lo completamente e pedir a um db para alocar um novo. Isto às vezes é uma fonte de muitos bugs. Não utilize merge() para forçar um id para um novo registo.

persist() por outro lado nunca te vou deixar passar uma identificação. Vai falhar imediatamente. No meu caso, é:

Causada por: org.Hibernar.PersistentObjectException: unifached entity passou a persistir

Hibernar-jpa javadoc tem uma dica:

Javax.persistência.EntityExistsException-se a entidade existir. (Se a entidade já existe, o A excepção de direito pode ser lançada quando a operação persistir for invocados, ou EntityExistsException or another PersistenceException pode ser jogado no flush ou tempo de commit.)

 0
Author: yuranos87, 2018-05-01 15:06:49