Eu me deparei com uma situação (que acho estranha, mas possivelmente bastante normal) em que uso o EntityManager.getReference (LObj.getClass (), LObj.getId ()) para obter uma entidade de banco de dados e, em seguida, passar o objeto retornado para ser persistido em outra tabela.
Basicamente, o fluxo era assim:
classe TFacade { createT (FObj, AObj) { T TObj = novo T (); TObj.setF (FObj); TObj.setA (AObj); ... EntityManager.persist (TObj); ... L LObj = A.getL (); FObj.setL (LObj); FFacade.editF (FObj); } } @ TransactionAttributeType.REQUIRES_NEW class FFacade { editF (FObj) { L LObj = FObj.getL (); LObj = EntityManager.getReference (LObj.getClass (), LObj.getId ()); ... EntityManager.merge (FObj); ... FLHFacade.create (FObj, LObj); } } @ TransactionAttributeType.REQUIRED classe FLHFacade { createFLH (FObj, LObj) { FLH FLHObj = novo FLH (); FLHObj.setF (FObj); FLHObj.setL (LObj); .... EntityManager.persist (FLHObj); ... } }
Eu estava recebendo a seguinte exceção "java.lang.IllegalArgumentException: Unknown entity: com.my.persistence.L $$ EnhancerByCGLIB $$ 3e7987d0"
Depois de examinar isso por um tempo, finalmente descobri que era porque eu estava usando o método EntityManager.getReference () que estava recebendo a exceção acima, pois o método estava retornando um proxy.
Isso me faz pensar: quando é aconselhável usar o método EntityManager.getReference () em vez do método EntityManager.find () ?
EntityManager.getReference () lança uma EntityNotFoundException se não puder encontrar a entidade que está sendo pesquisada, o que é muito conveniente por si só. O método EntityManager.find () simplesmente retorna nulo se não puder encontrar a entidade.
Com relação aos limites da transação, parece-me que você precisaria usar o método find () antes de passar a entidade recém-encontrada para uma nova transação. Se você usar o método getReference (), provavelmente acabará em uma situação semelhante à minha, com a exceção acima.
fonte
Respostas:
Eu geralmente uso o método getReference quando não preciso acessar o estado do banco de dados (quero dizer, o método getter). Apenas para alterar o estado (quero dizer, o método setter). Como você deve saber, getReference retorna um objeto proxy que usa um recurso poderoso chamado verificação suja automática. Suponha o seguinte
Se eu chamar o método find , o provedor JPA, nos bastidores, chamará
Se eu chamar o método getReference , o provedor JPA, nos bastidores, chamará
E você sabe por quê ???
Ao chamar getReference, você obterá um objeto proxy. Algo como este (o provedor JPA se encarrega de implementar este proxy)
Portanto, antes da confirmação da transação, o provedor JPA verá o sinalizador stateChanged para atualizar OU NÃO a entidade pessoa. Se nenhuma linha for atualizada após a instrução de atualização, o provedor JPA lançará EntityNotFoundException de acordo com a especificação JPA.
Saudações,
fonte
SELECT
antesUPDATE
, não importa qual defind()
/getReference()
eu uso. O que é pior,SELECT
atravessa relações NON-LAZY (emissão de novoSELECTS
), embora eu queira apenas atualizar um único campo em uma entidade.Conforme expliquei neste artigo , supondo que você tenha uma
Post
entidade pai e uma filha,PostComment
conforme ilustrado no diagrama a seguir:Se você ligar
find
ao tentar definir a@ManyToOne
post
associação:O Hibernate irá executar as seguintes instruções:
A consulta SELECT é inútil desta vez porque não precisamos que a entidade Post seja buscada. Queremos apenas definir a coluna de chave estrangeira post_id subjacente.
Agora, se você usar em
getReference
vez disso:Desta vez, o Hibernate emitirá apenas a instrução INSERT:
Ao contrário
find
, ogetReference
only retorna uma entidade Proxy que possui apenas o identificador definido. Se você acessar o Proxy, a instrução SQL associada será disparada enquanto o EntityManager ainda estiver aberto.No entanto, neste caso, não precisamos acessar o Proxy da entidade. Queremos apenas propagar a chave estrangeira para o registro da tabela subjacente, portanto, carregar um proxy é suficiente para este caso de uso.
Ao carregar um Proxy, você precisa estar ciente de que uma LazyInitializationException pode ser lançada se você tentar acessar a referência de Proxy após o EntityManager ser fechado. Para obter mais detalhes sobre como lidar com o
LazyInitializationException
, consulte este artigo .fonte
hibernate.jpa.compliance.proxy
propriedade de configuração , para que você possa escolher compatibilidade com JPA ou melhor desempenho de acesso a dados.getReference
é necessário se é apenas o suficiente para definir uma nova instância do modelo com o conjunto de PK. o que estou perdendo?Como uma referência é 'gerenciada', mas não hidratada, ela também pode permitir que você remova uma entidade por ID, sem precisar carregá-la primeiro na memória.
Como você não pode remover uma entidade não gerenciada, é simplesmente bobagem carregar todos os campos usando find (...) ou createQuery (...), apenas para excluí-la imediatamente.
fonte
EntityManager.getReference()
é realmente um método sujeito a erros e há realmente muito poucos casos em que um código de cliente precisa usá-lo.Pessoalmente, nunca precisei usá-lo.
EntityManager.getReference () e EntityManager.find (): nenhuma diferença em termos de sobrecarga
Não concordo com a resposta aceita e principalmente:
Não é o comportamento que obtenho com o Hibernate 5 e o javadoc do
getReference()
não diz tal coisa:EntityManager.getReference()
poupa uma consulta para recuperar a entidade em dois casos:1) se a entidade estiver armazenada no contexto de persistência, esse é o cache de primeiro nível.
E esse comportamento não é específico para
EntityManager.getReference()
,EntityManager.find()
também poupará uma consulta para recuperar a entidade se a entidade estiver armazenada no contexto de persistência.Você pode verificar o primeiro ponto com qualquer exemplo.
Você também pode contar com a implementação real do Hibernate.
Na verdade,
EntityManager.getReference()
depende docreateProxyIfNecessary()
método daorg.hibernate.event.internal.DefaultLoadEventListener
classe para carregar a entidade.Aqui está sua implementação:
A parte interessante é:
2) Se não manipularmos efetivamente a entidade, ecoando para a busca preguiçosa do javadoc.
Na verdade, para garantir o carregamento efetivo da entidade, é necessário invocar um método nela.
Então o ganho estaria relacionado a um cenário onde queremos carregar uma entidade sem a necessidade de utilizá-la? No quadro de aplicativos, essa necessidade é realmente incomum e, além disso, o
getReference()
comportamento também é muito enganoso se você ler a próxima parte.Por que favorecer EntityManager.find () em vez de EntityManager.getReference ()
Em termos de overhead,
getReference()
não é melhor dofind()
que o discutido no ponto anterior.Então, por que usar um ou outro?
A invocação
getReference()
pode retornar uma entidade obtida lentamente.Aqui, a busca lenta não se refere aos relacionamentos da entidade, mas à própria entidade.
Isso significa que, se invocarmos
getReference()
e o contexto de persistência for fechado, a entidade pode nunca ser carregada e, portanto, o resultado é realmente imprevisível. Por exemplo, se o objeto proxy for serializado, você poderá obter umanull
referência como resultado serializado ou se um método for chamado no objeto proxy, uma exceção comoLazyInitializationException
é lançada.Isso significa que o lançamento
EntityNotFoundException
disso é o principal motivo a ser usadogetReference()
para lidar com uma instância que não existe no banco de dados, pois uma situação de erro pode nunca ser executada enquanto a entidade não existir.EntityManager.find()
não tem a ambição de jogarEntityNotFoundException
se a entidade não for encontrada. Seu comportamento é simples e claro. Você nunca terá surpresa, pois ele retorna sempre uma entidade carregada ounull
(se a entidade não for encontrada) mas nunca uma entidade sob a forma de um proxy que pode não estar efetivamente carregada.Portanto,
EntityManager.find()
deve ser favorecido na maioria dos casos.fonte
Não concordo com a resposta selecionada e, como davidxxx corretamente apontou, getReference não fornece tal comportamento de atualizações dinâmicas sem select. Eu fiz uma pergunta sobre a validade desta resposta, veja aqui - não é possível atualizar sem emitir select on using setter após getReference () do Hibernate JPA .
Sinceramente, não vi ninguém que realmente usou essa funcionalidade. QUALQUER LUGAR. E eu não entendo por que é tão votado.
Agora, em primeiro lugar, não importa o que você chame em um objeto proxy de hibernação, um setter ou um getter, um SQL é disparado e o objeto é carregado.
Mas então pensei, e se o proxy getReference () JPA não fornecer essa funcionalidade. Posso apenas escrever meu próprio proxy.
Agora, todos nós podemos argumentar que as seleções em chaves primárias são tão rápidas quanto uma consulta pode chegar e não é algo que deva ser evitado. Mas para aqueles de nós que não podem lidar com isso por um motivo ou outro, abaixo está uma implementação de tal proxy. Mas antes de ver a implementação, veja seu uso e como é simples de usar.
USO
E isso dispararia a seguinte consulta -
e mesmo se você quiser inserir, você ainda pode fazer PersistenceService.save (new Order ("a", 2)); e dispararia uma inserção como deveria.
IMPLEMENTAÇÃO
Adicione isso ao seu pom.xml -
Faça esta classe para criar proxy dinâmico -
Faça uma interface com todos os métodos -
Agora, faça um interceptor que permitirá que você implemente esses métodos em seu proxy -
E a classe de exceção -
Um serviço para salvar usando este proxy -
fonte