Eu tenho uma situação em que preciso anexar novamente objetos desanexados a uma sessão de hibernação, embora um objeto da mesma identidade PODE já existir na sessão, o que causará erros.
Agora, eu posso fazer uma de duas coisas.
getHibernateTemplate().update( obj )
Isso funciona se e somente se um objeto ainda não existir na sessão de hibernação. Exceções são lançadas informando que um objeto com o identificador fornecido já existe na sessão quando eu precisar mais tarde.getHibernateTemplate().merge( obj )
Isso funciona se e somente se houver um objeto na sessão de hibernação. Exceções são lançadas quando eu preciso que o objeto esteja em uma sessão posteriormente, se eu usar isso.
Dados esses dois cenários, como anexar genericamente sessões a objetos? Não quero usar exceções para controlar o fluxo da solução desse problema, pois deve haver uma solução mais elegante ...
refresh()
entidades desanexadas? Examinando a especificação 2.0, não vejo justificativa; apenas que não é permitido.*Reattaching a modified detached instance* A detached instance may be reattached to a new Session (and managed by this new persistence context) by calling update() on the detached object. In our experience, it may be easier for you to understand the following code if you rename the update() method in your mind to reattach()—however, there is a good reason it’s called updating.
Mais informações podem ser encontradas na seção 9.3.2lock(LockMode.NONE)
pode de fato ser chamado em um objeto transitório, e ele anexa novamente a entidade à sessão. Veja stackoverflow.com/a/3683370/14379Todas essas respostas perdem uma distinção importante. update () é usado para (re) anexar seu gráfico de objeto a uma Session. Os objetos que você passa são os que são gerenciados.
merge () não é realmente uma API de (re) anexo. Observe que merge () tem um valor de retorno? Isso ocorre porque ele retorna o gráfico gerenciado, que pode não ser o gráfico que você passou nele. merge () é uma API da JPA e seu comportamento é controlado pelas especificações da JPA. Se o objeto que você passar para mesclar () já estiver gerenciado (já associado à Sessão), esse é o gráfico com o qual o Hibernate trabalha; o objeto passado é o mesmo objeto retornado de merge (). Se, no entanto, o objeto que você passar para mesclar () for desanexado, o Hibernate cria um novo gráfico de objeto que é gerenciado e copia o estado do seu gráfico desanexado para o novo gráfico gerenciado. Novamente, tudo isso é ditado e regido pelas especificações da JPA.
Em termos de uma estratégia genérica para "garantir que essa entidade seja gerenciada ou gerenciada", isso depende de que você também queira considerar os dados ainda não inseridos. Supondo que sim, use algo como
Observe que usei saveOrUpdate () em vez de update (). Se você não deseja que os dados ainda não inseridos sejam tratados aqui, use update () em vez disso ...
fonte
Session.contains(Object)
verificações por referência. Se já houver outra Entidade representando a mesma linha na sessão e você passar uma instância desanexada, você receberá uma exceção.Session.contains(Object)
verificações por referência, se houver outra Entidade representando a mesma linha na sessão, ela retornará false e a atualizará.Resposta não diplomática: você provavelmente está procurando um contexto de persistência estendido. Essa é uma das principais razões por trás do Seam Framework ... Se você está tendo dificuldades para usar o Hibernate no Spring, em particular, consulte esta parte dos documentos do Seam.
Resposta diplomática: Isso é descrito nos documentos do Hibernate . Se você precisar de mais esclarecimentos, consulte a Seção 9.3.2 da Persistência do Java com o Hibernate, denominada "Trabalhando com objetos desanexados". Eu recomendo fortemente que você obtenha este livro se estiver fazendo algo mais que CRUD com o Hibernate.
fonte
Se você tem certeza de que sua entidade não foi modificada (ou se você concorda que alguma modificação será perdida), você pode anexá-la novamente à sessão com bloqueio.
Ele não bloqueia nada, mas obtém a entidade do cache da sessão ou (se não for encontrada lá) a lê no DB.
É muito útil evitar LazyInitException quando você está navegando em relações de entidades "antigas" (da HttpSession, por exemplo). Você primeiro "anexa novamente" a entidade.
O uso de get também pode funcionar, exceto quando você mapear a herança (que já lançará uma exceção no getId ()).
fonte
Session.lock(entity, LockMode.NONE)
falha com a exceção dizendo: não foi possível reassociar a coleção transitória não inicializada. Como pode superar isso?Session.find()
método de API. Talvez você queira dizerSession.load(Object object, Serializable id)
.Estados da entidade
A JPA define os seguintes estados da entidade:
Novo (transitório)
Um objeto recém-criado que nunca foi associado a um Hibernate
Session
(akaPersistence Context
) e não está mapeado para nenhuma linha da tabela de banco de dados é considerado no estado Novo (Transitório).Para nos tornarmos persistentes, precisamos chamar explicitamente o
EntityManager#persist
método ou fazer uso do mecanismo de persistência transitiva.Persistente (gerenciado)
Uma entidade persistente foi associada a uma linha da tabela de banco de dados e está sendo gerenciada pelo Contexto de Persistência atualmente em execução. Qualquer alteração feita em uma entidade será detectada e propagada no banco de dados (durante o tempo de liberação da sessão).
Com o Hibernate, não precisamos mais executar instruções INSERT / UPDATE / DELETE. O Hibernate emprega um estilo de trabalho de write-behind transacional e as alterações são sincronizadas no último momento responsável, durante o
Session
tempo de liberação atual .Independente
Depois que o contexto de persistência em execução no momento é fechado, todas as entidades gerenciadas anteriormente ficam desanexadas. As alterações sucessivas não serão mais rastreadas e nenhuma sincronização automática de banco de dados ocorrerá.
Transições de estado da entidade
Você pode alterar o estado da entidade usando vários métodos definidos pela
EntityManager
interface.Para entender melhor as transições de estado da entidade JPA, considere o seguinte diagrama:
Ao usar o JPA, para reassociar uma entidade desanexada a uma ativa
EntityManager
, você pode usar a operação de mesclagem .Ao usar a API nativa do Hibernate, além de
merge
, você pode reconectar uma entidade desanexada a uma sessão ativa do Hibernate usando os métodos de atualização, conforme demonstrado no diagrama a seguir:Mesclando uma entidade desanexada
A mesclagem copiará o estado da entidade desanexada (origem) para uma instância da entidade gerenciada (destino).
Considere que persistimos a
Book
entidade a seguir e agora a entidade é desanexada, pois oEntityManager
que foi usado para persistir a entidade foi fechada:Enquanto a entidade está no estado desanexado, nós a modificamos da seguinte maneira:
Agora, queremos propagar as alterações no banco de dados, para que possamos chamar o
merge
método:E o Hibernate irá executar as seguintes instruções SQL:
Se a entidade mesclada não tiver equivalente no atual
EntityManager
, uma nova captura instantânea da entidade será buscada no banco de dados.Uma vez que existe uma entidade gerenciada, a JPA copia o estado da entidade desanexada para a que atualmente é gerenciada e, durante o Contexto de Persistência
flush
, um UPDATE será gerado se o mecanismo de verificação sujo descobrir que a entidade gerenciada foi alterada.Anexando novamente uma entidade desanexada
O Hibernate, mas não o JPA, oferece suporte à recolocação através do
update
método.Um Hibernate
Session
pode associar apenas um objeto de entidade para uma determinada linha do banco de dados. Isso ocorre porque o contexto de persistência atua como um cache na memória (cache de primeiro nível) e apenas um valor (entidade) é associado a uma determinada chave (tipo de entidade e identificador de banco de dados).Uma entidade pode ser reconectada apenas se não houver outro objeto da JVM (correspondente à mesma linha do banco de dados) já associado ao Hibernate atual
Session
.Considerando que mantivemos a
Book
entidade e a modificamos quando aBook
entidade estava no estado desanexado:Podemos reconectar a entidade desanexada assim:
E o Hibernate executará a seguinte instrução SQL:
Diferentemente
merge
, a entidade desanexada fornecida será reassociada ao contexto de persistência atual e um UPDATE é agendado durante a liberação, independentemente de a entidade ter modificado ou não.Para evitar isso, você pode usar a
@SelectBeforeUpdate
anotação Hibernate, que acionará uma instrução SELECT que buscou o estado carregado, que é usado pelo mecanismo de verificação suja.Cuidado com a NonUniqueObjectException
Um problema que pode ocorrer
update
é se o contexto de persistência já contiver uma referência de entidade com o mesmo ID e do mesmo tipo, como no exemplo a seguir:Agora, ao executar o caso de teste acima, o Hibernate lançará um
NonUniqueObjectException
porque o segundoEntityManager
já contém umaBook
entidade com o mesmo identificador que passamosupdate
, e o Contexto de Persistência não pode conter duas representações da mesma entidade.Conclusão
O
merge
método deve ser preferido se você estiver usando o bloqueio otimista, pois permite evitar atualizações perdidas. Para mais detalhes sobre este tópico, consulte este artigo .A
update
é bom para atualizações em lote como ele pode impedir que a instrução SELECT adicional gerado pelamerge
operação, reduzindo assim o tempo de execução de atualização em lote.fonte
@SelectBeforeUpdate
anotação embora. Quando a seleção é acionada? Na chamadaupdate
, imediatamente antes da liberação ou isso realmente não importa (pode ser importante se o hibernate buscar todas as entidades anotadas em uma chamada antes da liberação)?@SelectBeforeUpdate
dispara o SELECT durante aflush
operação de contexto de persistência . Confira ogetDatabaseSnapshot
método noDefaultFlushEntityEventListener
para mais detalhes.Voltei ao JavaDoc
org.hibernate.Session
e encontrei o seguinte:Assim
update()
,saveOrUpdate()
,lock()
,replicate()
emerge()
são as opções de candidatos.update()
: Gerará uma exceção se houver uma instância persistente com o mesmo identificador.saveOrUpdate()
: Salve ou atualizelock()
: Descontinuadareplicate()
: Persiste o estado da instância desanexada especificada, reutilizando o valor atual do identificador.merge()
: Retorna um objeto persistente com o mesmo identificador. A instância fornecida não se associa à sessão.Portanto,
lock()
não deve ser usado imediatamente e, com base no requisito funcional, um ou mais deles podem ser escolhidos.fonte
Fiz dessa maneira em C # com NHibernate, mas deve funcionar da mesma maneira em Java:
O primeiro bloqueio foi chamado em todos os objetos porque Contains sempre era falso. O problema é que o NHibernate compara objetos por ID e tipo de banco de dados. Contém usa o
equals
método, que é comparado por referência, se não for substituído. Com esseequals
método, ele funciona sem nenhuma exceção:fonte
Session.contains(Object obj)
verifica a referência e não detectará uma instância diferente que represente a mesma linha e já esteja anexada a ela.Aqui minha solução genérica para Entidades com uma propriedade identificadora.
Esse é um dos poucos aspectos do .Net EntityFramework que eu gosto, as diferentes opções de anexação relacionadas às entidades alteradas e suas propriedades.
fonte
Eu vim com uma solução para "atualizar" um objeto do armazenamento de persistência que será responsável por outros objetos que já podem estar anexados à sessão:
fonte
Desculpe, mas não consigo adicionar comentários (ainda?).
Usando o Hibernate 3.5.0-Final
Considerando que o
Session#lock
método esta depreciado, o javadoc não sugerir o usoSession#buildLockRequest(LockOptions)#lock(entity)
e se você se certificar de suas associações têmcascade=lock
, o carregamento lento não é um problema também.Então, meu método de anexação se parece um pouco com
Os testes iniciais sugerem que funciona um tratamento.
fonte
Talvez ele se comporte um pouco diferente no Eclipselink. Para reconectar objetos desanexados sem obter dados obsoletos, geralmente:
e, opcionalmente, uma segunda etapa (para invalidar caches):
fonte
tente getHibernateTemplate (). replicate (entity, ReplicationMode.LATEST_VERSION)
fonte
No post original, existem dois métodos,
update(obj)
emerge(obj)
que são mencionados ao trabalho, mas em circunstâncias opostas. Se isso for realmente verdade, por que não testar para ver se o objeto já está na sessão primeiro e depois ligarupdate(obj)
se estiver, caso contrário, chamemerge(obj)
.O teste para a existência na sessão é
session.contains(obj)
. Portanto, acho que o seguinte pseudocódigo funcionaria:fonte
para anexar novamente esse objeto, você deve usar merge ();
esse método aceita no parâmetro sua entidade desanexada e retorna uma entidade será anexada e recarregada do banco de dados.
fonte
chamar primeiro merge () (para atualizar a instância persistente) e depois bloquear (LockMode.NONE) (para anexar a instância atual, não a retornada por merge ()) parece funcionar em alguns casos de uso.
fonte
Propriedade
hibernate.allow_refresh_detached_entity
fez o truque para mim. Mas é uma regra geral, portanto, não é muito adequado se você deseja fazê-lo apenas em alguns casos. Espero que ajude.Testado no Hibernate 5.4.9
SessionFactoryOptionsBuilder
fonte
O suporte ao Hibernate reconecta a entidade desanexada por formas servais, consulte o Guia do Usuário do Hibernate .
fonte
fonte