Pergunta

EntityManager.merge() pode inserir novos objetos e atualizar os já existentes.

Por que alguém iria querer usar persist() (que só pode criar novos objetos)?

Foi útil?

Solução

De qualquer forma irá adicionar uma entidade para um PersistenceContext, a diferença está no que você faz com a entidade depois.

Persistir leva uma instância entidade, adiciona-o ao contexto e faz essa instância conseguiu (ou seja, as atualizações futuras para a entidade serão controladas).

Merge cria uma nova instância de sua entidade, copia o estado da entidade fornecido, e faz com que a nova cópia gerenciada. O exemplo que se passa em não serão geridos (as alterações feitas não fará parte da transação - a menos que você chamar merge novamente).

Talvez um exemplo de código vai ajudar.

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)

Cenário 1 e 3 são mais ou menos equivalentes, mas existem algumas situações em que você gostaria de usar Cenário 2.

Outras dicas

persistir e merge são para duas finalidades diferentes (não são alternativas em tudo).

(editado para expandir diferenças informações)

persistem:

  • Insira um novo registo à base de dados
  • Coloque o objeto para o gerenciador de entidade.

merge:

  • Encontre um objeto unido com o mesmo id e atualizá-lo.
  • Se existe atualização e retornar o objeto já anexado.
  • Se não existe inserção do novo registo à base de dados.

persist () eficiência:

  • Poderia ser mais eficiente para inserir um novo registo para uma base de dados de merge ().
  • Não duplica o objeto original.

persist () semântica:

  • Ele garante que você está inserindo e não atualizar 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 um objeto anexado para qualquer registo no gerenciador de entidade.

merge () para uma 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 conectado a MySQL merge () poderia ser tão eficiente quanto persist () usando uma chamada para inserir opção ON DUPLICATE KEY UPDATE com, JPA é uma programação de alto nível e você não pode assumir esta vai ser a caso em todos os lugares.

Se você estiver usando o gerador atribuído, usando mala em vez de persistir pode causar uma SQL redundante declaração , desempenho, portanto, afetando.

Além disso, chamando de mesclagem para entidades geridas também é um erro desde entidades geridas são gerenciados automaticamente pelo Hibernate e seu estado é sincronizado com o registro de banco de dados pelo sujo verificação sobre lavagem do Persistence Contexto .

Para entender como tudo isso funciona, você deve primeiro saber que o Hibernate muda a mentalidade desenvolvedor de instruções SQL para transições de estado entidade .

Uma vez que uma entidade é gerenciado ativamente pelo Hibernate, todas as alterações serão automaticamente propagadas para o banco de dados.

monitores

Hibernate atualmente anexado entidades. Mas para uma entidade para tornar-se gerenciado, ele deve estar no estado entidade direita.

Em primeiro lugar, é preciso definir todos os estados entidade:

  • Novo (Transient)

    A recém-criada objeto que já não tenha sido associada a um Session Hibernate (a.k.a Persistence Context) e não está mapeado para qualquer linha da tabela de banco de dados é considerado no novo Estado (transitória).

    Para se tornar persistiu precisamos chamar de forma explícita o método EntityManager#persist ou fazer uso do mecanismo transitivo persistência.

  • Persistent (gerenciado)

    A entidade persistente tem sido associada com uma linha da tabela de banco de dados e está sendo gerido pela execução atual Persistência Contexto. Qualquer alteração feita à entidade vai ser detectado e propagadas para o banco de dados (durante a Sessão lave-time). Com Hibernate, nós já não temos para executar ENVIE atualizar as declarações / / delete. Hibernate emprega um transacional write-behind estilo de trabalho e as mudanças são sincronizado no último momento responsável, durante o Session rubor da hora atual.

  • Moradia Isolada

    Uma vez que a corrente que contexto de persistência está fechado todas as entidades anteriormente geridas soltar-se. sucessivas mudanças deixarão de ser rastreado e não sincronização de dados automática que vai acontecer.

    Para associar uma entidade separada para uma Sessão Hibernate ativo, você pode escolher uma das seguintes opções:

    • recolocar

      Hibernate (mas não JPA 2.1) suporta recolocar através do método # update Session. A Session do Hibernate pode somente associar um objeto Entity para uma determinada linha de banco de dados. Isso ocorre porque o contexto de persistência atua como um cache de memória (cache primeiro nível) e apenas um valor (entidade) está associada a uma determinada chave (tipo de entidade e identificador de banco de dados). Uma entidade pode ser recolocado somente se não houver nenhum outro objeto JVM (encontrando a mesma linha de banco de dados) já associado ao atual Session Hibernate.

    • Fusão

    A fusão vai copiar o estado entidade separada (fonte) para uma instância entidade gerida (destino). Se a entidade fusão não tem equivalente na atual sessão, um será buscada a partir do banco de dados. A instância do objeto destacado continuará a desinteressar-se mesmo depois da operação de fusão.

  • Removido

    Embora JPA exige que as entidades geridas só estão autorizados a ser removido, o Hibernate também pode excluir entidades separadas (mas apenas através de uma chamada de método de exclusão Session #). Uma entidade removida apenas está prevista para deletion e a instrução DELETE banco de dados real será executado durante a sessão embutida tempo.

Para entender as transições de estado JPA melhor, você pode visualizar o diagrama a seguir:

enter descrição da imagem aqui

Ou se você usar o específica API Hibernate:

enter descrição da imagem aqui

Eu notei que quando eu usei em.merge, eu tenho uma declaração SELECT para cada INSERT, mesmo quando não havia nenhum campo que JPA estava gerando para mim - o campo de chave primária foi um UUID que eu me definir. Mudei para em.persist(myEntityObject) e tem declarações apenas INSERT então.

A especificação JPA diz o seguinte sobre persist().

Se X é um objeto individual, o EntityExistsException pode ser acionada quando a persistir operação é invocada, ou o EntityExistsException ou de outra PersistenceException pode ser jogado em flush ou commit tempo.

Então, usando persist() seria adequado quando o objeto não devia para ser um objeto separado. Você pode preferir ter o código lançar a PersistenceException por isso falhar rápido.

a especificação é claro , conjunto persist() poder o @GeneratedValue @Id para um objecto. merge() no entanto deve ter um objeto com o @Id já gerado.

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

D1. não merge não tornar a entidade passou gerenciado, mas retorna outra instância que é gerido. persist do outro lado vai fazer a entidade passou geridos:

//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 a persistir nas costas entidade, você pode fazer isso apenas com persist (), porque merge irá lançar uma IllegalArgumentException.

D3. Se você decidiu cuidar manualmente de seus IDs (por exemplo, usando UUIDs), então um merge operação irá desencadear consultas SELECT subsequentes, a fim de olhar para as entidades existentes com esse ID, enquanto persist pode não precisar dessas consultas.

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

Alguns mais detalhes sobre fusão que irá ajudá-lo a usar a mala sobre persistem:

Retornando uma instância gerenciada diferente da entidade original é uma parte crítica da fusão processo. Se uma instância de entidade com o mesmo identificador já existe no contexto de persistência, o provedor irá substituir o seu estado com o estado da entidade que está sendo mesclado, mas o conseguiu versão que já existia deve ser devolvido ao cliente para que ele possa ser usado. Se o provedor não fez atualizar a instância Employee no contexto de persistência, qualquer referência a essa instância se tornará inconsistente com o novo ser estado fundiram em.

Quando merge () é chamado em uma nova entidade, ele se comporta de forma semelhante à operação persist (). acrescenta a entidade para o contexto de persistência, mas em vez de adicionar a instância entidade original, ele cria um novo copiar e gere essa instância vez. A cópia que é criada pela operação merge () é mantido como se o método persist () foram invocadas nele.

Na presença de relacionamentos, a operação merge () tentará atualizar a entidade gerida para apontar para as versões gerenciadas das entidades referenciadas pela entidade separada. Se a entidade tem uma relação a um objeto que não tem identidade persistente, o resultado da operação de mesclagem é Indefinido. Alguns provedores podem permitir a cópia conseguiu apontar para o objeto não-persistente, enquanto outros pode lançar uma exceção imediatamente. A operação de intercalação () pode ser facultativamente em cascata, nestes casos, para evitar uma excepção ocorra. Nós vamos cobrir cascata do merge () operação mais adiante nesta seção. Se um ser entidade resultante da concentração aponta para uma entidade removida, uma IllegalArgumentException exceção será lançada.

relações carregamento lento são um caso especial na operação de fusão. Se um carregamento lento relacionamento não foi desencadeada em uma entidade antes de se tornar individual, essa relação será ignorados quando a entidade é mesclado. Se o relacionamento foi desencadeada enquanto gerido e, em seguida, definido como nulo quando a entidade foi destacado, a versão gerenciada da entidade irá também têm a relação limpa durante a fusão. "

Toda a informação acima foi tirada de "Pro JPA 2 Mastering API Java ™ Persistence" por Mike Keith e Merrick Schnicariol. Capítulo 6. Seção desprendimento e fusão. Este livro é, na verdade, um segundo livro dedicado a JPA pelos autores. Este novo livro tem muitas novas informações, em seguida anterior. Eu realmente recomendaria a ler este livro para aqueles que vão ser seriamente envolvidos com JPA. Lamento por anonimamente a postar minha primeira resposta.

Eu estava ficando lazyLoading exceções na minha entidade porque eu estava tentando acessar uma coleção carregada preguiçoso que estava em sessão.

O que eu faria estava em um pedido separado, recuperar a entidade de sessão e, em seguida, tentar acessar uma coleção em minha página jsp que era problemático.

Para aliviar isso, eu atualizei a mesma entidade no meu controlador e passou para o meu jsp, embora eu imagino quando eu re-salvos em sessão que também estará acessível embora SessionScope e não jogar um LazyLoadingException, uma modificação do exemplo 2:

A seguir tem trabalhado 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!!

Atravessar as respostas existem alguns detalhes que faltam sobre `Cascade' e geração de id. Veja questão

Além disso, vale a pena mencionar que você pode ter anotações Cascade separados para fundir e persistindo:. Cascade.MERGE e Cascade.PERSIST que serão tratados de acordo com o método utilizado

A especificação é seu amigo;)

Eu encontrei esta explicação dos docs Hibernate esclarecedor, porque contêm um caso de uso:

O uso e semântica de merge () parece ser confuso para novos usuários. Em primeiro lugar, contanto que você não está tentando estado de uso objeto carregado em um gerenciador de entidade em um novo gerenciador de entidades, você deve não precisa usar merge () em tudo . Algumas aplicações inteiras nunca vai usar este método.

Normalmente merge () é usado no seguinte cenário:

  • As aplicativo carrega um objeto no primeiro gerenciador de entidade
  • o objeto é passado para a camada de apresentação
  • algumas modificações são feitas para o objeto
  • o objeto é passado de volta para a camada de lógica de negócios
  • a aplicação persiste estas modificações, chamando merge () em um segundo gerenciador de entidade

Aqui está a exata semântico de merge ():

  • se houver uma instância gerenciada com o mesmo identificador associado atualmente com o contexto de persistência, copie o estado do objeto dado para a instância gerenciada
  • Se não houver nenhuma instância gerenciado atualmente associado ao contexto de persistência, tentar carregá-lo a partir do banco de dados, ou criar uma nova instância gerenciada
  • a instância gerenciada é retornado
  • a instância dada não se torna associado com o contexto de persistência, ele permanece destacado e é geralmente descartado

De: http: //docs.jboss. org / hibernação / entitymanager / 3,6 / referência / en / html / objectstate.html

APP é, indiscutivelmente, uma grande simplificação no domínio da empresa aplicações construídas na plataforma Java. Como um desenvolvedor que teve que lidar com as complexidades dos antigos beans de entidade em J2EE que eu vejo o inclusão de APP entre as especificações do Java EE como um grande salto frente. No entanto, enquanto aprofundando a APP detalhes acho coisas que não são tão fácil. Neste artigo vou lidar com comparação de métodos de mesclagem do EntityManager e persistem cuja sobreposição comportamento pode causar confusão não só para um novato. Além disso I propor uma generalização que vê ambos os métodos como casos especiais de um método mais geral combinar.

entidades persistentes

Em contraste com o método de fusão a persistir método é bastante simples e intuitiva. O cenário mais comum do persistem uso do método pode ser resumida da seguinte forma:

"A instância recém-criada da classe entidade é passado para o método persistir. Após esse método retorna, a entidade é gerido e planejado para inserção no banco de dados. Pode acontecer em ou antes os commits transação ou quando o método flush é chamado. Se as referências de entidade outra entidade através de um relacionamento marcado com o PERSISTE estratégia cascata este procedimento é aplicado a ele também. "

 enter descrição da imagem aqui

A especificação vai mais em detalhes, no entanto, lembrar-lhes que não é crucial que esses detalhes apenas cobrir situações mais ou menos exóticos.

entidades Mesclando

Em comparação a persistir, a descrição do comportamento da fusão não é tão simples. Não há cenário principal, como é no caso de persistirem, e um programador deve se lembrar todos os cenários, a fim de escrever um código correto. Parece-me que os designers da APP queria ter algum método cuja principal preocupação seria a manipulação entidades separadas (como o oposto ao persistir método que lida com as entidades recém-criadas principalmente.) Principal tarefa do método merge é transferir o estado de um entidade não gerenciado (passado como o argumento) com o seu homólogo gerenciado dentro do contexto de persistência. Esta tarefa, no entanto, divide ainda mais em vários cenários que pioram a inteligibilidade do comportamento do método global.

Em vez de repetir os parágrafos a partir da especificação da APP eu preparado um diagrama de fluxo que ilustra esquematicamente o comportamento do método de fusão:

 enter descrição da imagem aqui

Então, quando devo usar persistir e quando merge?

persistem

  • Você quer que o método sempre cria uma nova entidade e nunca atualiza uma entidade. Caso contrário, o método gera uma exceção como consequência da violação de exclusividade chave principal.
  • processos em lote, manipulação de entidades de forma stateful (veja padrão de gateway).
  • otimização de desempenho

merge

  • Você quer que o método tanto inserções ou atualiza uma entidade no banco de dados.
  • Você quer entidades alça de uma forma apátrida (objetos de transferência de dados em serviços)
  • Você deseja inserir uma nova entidade que pode ter uma referência a outra entidade que pode, mas não pode ser criado ainda (relacionamento deve ser marcado MERGE). Por exemplo, a inserção de uma nova foto com uma referência a um novo ou um álbum de pré-existentes.

Cenário X:

Table: Spitter (One), Table: Spittles (Many) (Spittles é proprietário do relacionamento com uma FK: spitter_id)

Este cenário resulta em poupança:. O Spitter e ambos Spittles como se possuído por Same Spitter

        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:

Isso vai poupar o Spitter, vai salvar o 2 Spittles Mas eles não irá referenciar o mesmo Spitter!

        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!!

Outra observação:

merge() só se preocupam com um id gerado automaticamente (testado em IDENTITY e SEQUENCE) quando um registro com tal id já existe na sua mesa. Nesse caso merge() vai tentar atualizar o registro. Se, no entanto, um id está ausente ou não está combinando todos os registros existentes, merge() irá ignorá-lo completamente e pedir um db para alocar um novo. Isso às vezes é uma fonte de uma grande quantidade de bugs. Não use merge() para forçar um id para um novo recorde.

persist() por outro lado nunca vai deixar você mesmo passar um id para ele. Ele irá falhar imediatamente. No meu caso, é:

Causada por: org.hibernate.PersistentObjectException: entidade separada passou a persistir

hibernate-jpa javadoc tem uma dica:

Lança : javax.persistence.EntityExistsException - se a entidade já existe. (Se a entidade já existe, o EntityExistsException pode ser acionada quando a persistir operação é invocada, ou o outro ou EntityExistsException PersistenceException pode ser jogado em flush ou commit tempo.)

Você pode ter vindo aqui para aconselhamento sobre quando usar persistem e quando usar merge . Eu acho que isso depende da situação:. Quão provável é que você precisa para criar um novo registro e quão difícil é para recuperar dados persistiram

Vamos presumir que você pode usar uma chave / identificador natural.

  • Os dados precisam ser persistentes, mas de vez em quando existe um registro e uma atualização é necessária. Neste caso, você pode tentar uma persistem e se ele lança uma EntityExistsException, você procurá-lo e combinar os dados:

    try {EntityManager.persist (entidade)}

    catch (exceção EntityExistsException) {/ * recuperar e merge * /}

  • necessidades de dados persistentes para ser atualizado, mas de vez em quando não há nenhum registro para os dados ainda. Neste caso, você procurá-lo, e fazer um persistir se a entidade está faltando:

    entidade = EntityManager.find (chave);

    if (entidade == null) {EntityManager.persist (entidade); }

    else {/ * merge * /}

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

As fusões podem ser tratados de duas maneiras, demasiado:

  1. Se as alterações são geralmente pequenas, aplicá-las à entidade gerida.
  2. Se as mudanças são comuns, copie o ID da entidade persistente, bem como dados inalterados. Em seguida, chamar EntityManager :: merge () para substituir o conteúdo antigo.

persistem (entidade) deve ser usado com totalmente novas entidades, para adicioná-los DB (se a entidade já existe no DB haverá EntityExistsException lance).

merge (entidade) deve ser usado, para colocar entidade de volta ao contexto de persistência se a entidade foi destacada e foi alterado.

Provavelmente persistem está gerando SQL INSERT declaração e UPDATE merge instrução SQL (mas não tenho certeza).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top