E que tipo de estratégia alternativa você usa para evitar LazyLoadExceptions?
Eu entendo que a sessão aberta em vista tem problemas com:
- Aplicativos em camadas em execução em diferentes jvm's
- As transações são confirmadas apenas no final e provavelmente você gostaria dos resultados antes.
Mas, se você sabe que seu aplicativo está sendo executado em uma única VM, por que não aliviar sua dor usando uma sessão aberta na estratégia de visualização?
java
hibernate
jpa
lazy-loading
open-session-in-view
HeDinges
fonte
fonte
Respostas:
Porque o envio de proxies possivelmente não inicializados, especialmente coleções, na camada de visualização e o acionamento do carregamento de hibernação a partir daí pode ser problemático tanto do ponto de vista de desempenho quanto de compreensão.
Entendendo :
O uso do OSIV 'polui' a camada de visualização com questões relacionadas à camada de acesso a dados.
A camada de visualização não está preparada para lidar com o
HibernateException
que pode acontecer durante o carregamento lento, mas presumivelmente a camada de acesso a dados está.Desempenho :
OSIV tende a puxar o carregamento de entidade adequado para baixo do tapete - você tende a não notar que suas coleções ou entidades são inicializadas lentamente (talvez N + 1). Mais conveniência, menos controle.
Atualização: veja o antipadrão OpenSessionInView para uma discussão mais ampla sobre este assunto. O autor lista três pontos importantes:
fonte
Para obter uma descrição mais detalhada, você pode ler meu artigo Abrir sessão no antipadrão de visualização . Caso contrário, aqui está um resumo de porque você não deve usar Open Session In View.
Open Session In View tem uma abordagem inadequada para buscar dados. Em vez de permitir que a camada de negócios decida como é melhor buscar todas as associações que são necessárias para a camada de Visualização, ela força o Contexto de Persistência a permanecer aberto para que a camada de Visualização possa acionar a inicialização do Proxy.
OpenSessionInViewFilter
chama oopenSession
método do subjacenteSessionFactory
e obtém um novoSession
.Session
está vinculado aoTransactionSynchronizationManager
.OpenSessionInViewFilter
chamadoFilter
dajavax.servlet.FilterChain
referência do objeto e a solicitação é processada posteriormenteDispatcherServlet
é chamado e encaminha a solicitação HTTP para o subjacentePostController
.PostController
chamaPostService
para obter uma lista dePost
entidades.PostService
abre uma nova transação eHibernateTransactionManager
reutiliza a mesmaSession
que foi aberta peloOpenSessionInViewFilter
.PostDAO
busca a lista dePost
entidades sem inicializar nenhuma associação preguiçosa.PostService
confirma a transação subjacente, masSession
não é fechado porque foi aberto externamente.DispatcherServlet
começa a renderizar a IU, que, por sua vez, navega pelas associações lazy e dispara sua inicialização.OpenSessionInViewFilter
pode fechar oSession
e a conexão de banco de dados subjacente também é liberada.À primeira vista, pode não parecer uma coisa terrível de se fazer, mas, uma vez que você visualiza da perspectiva do banco de dados, uma série de falhas começa a se tornar mais óbvia.
A camada de serviço abre e fecha uma transação de banco de dados, mas depois disso, não há nenhuma transação explícita acontecendo. Por esse motivo, cada instrução adicional emitida na fase de renderização da IU é executada no modo de confirmação automática. A confirmação automática pressiona o servidor de banco de dados porque cada instrução deve liberar o log de transações para o disco, causando, portanto, muito tráfego de E / S no lado do banco de dados. Uma otimização seria marcar o
Connection
como somente leitura, o que permitiria ao servidor de banco de dados evitar a gravação no log de transações.Não há mais separação de interesses porque as instruções são geradas tanto pela camada de serviço quanto pelo processo de renderização da IU. Escrever testes de integração que afirmam o número de instruções que estão sendo geradas requer passar por todas as camadas (web, serviço, DAO), enquanto o aplicativo é implantado em um contêiner da web. Mesmo ao usar um banco de dados na memória (por exemplo, HSQLDB) e um servidor web leve (por exemplo, Jetty), esses testes de integração serão mais lentos para executar do que se as camadas fossem separadas e os testes de integração de back-end usassem o banco de dados, enquanto o Os testes de integração front-end estavam simulando a camada de serviço como um todo.
A camada de IU é limitada a associações de navegação que podem, por sua vez, acionar N + 1 problemas de consulta. Embora o Hibernate ofereça
@BatchSize
associações de busca em lotes eFetchMode.SUBSELECT
para lidar com este cenário, as anotações estão afetando o plano de busca padrão, portanto, são aplicadas a todos os casos de uso de negócios. Por esse motivo, uma consulta da camada de acesso a dados é muito mais adequada porque pode ser adaptada aos requisitos atuais de busca de dados do caso de uso.Por último, mas não menos importante, a conexão com o banco de dados pode ser mantida durante toda a fase de renderização da IU (dependendo do modo de liberação da conexão), o que aumenta o tempo de concessão da conexão e limita o rendimento geral da transação devido ao congestionamento no pool de conexão do banco de dados. Quanto mais a conexão for mantida, mais outras solicitações simultâneas irão esperar para obter uma conexão do pool.
Portanto, ou você obtém a conexão mantida por muito tempo, ou adquire / libera várias conexões para uma única solicitação HTTP, colocando pressão no pool de conexão subjacente e limitando a escalabilidade.
Spring Boot
Infelizmente, Open Session in View é habilitado por padrão no Spring Boot .
Portanto, certifique-se de que, no
application.properties
arquivo de configuração, você tenha a seguinte entrada:Isso desabilitará o OSIV, para que você possa lidar da
LazyInitializationException
maneira certa .fonte
as transações podem ser confirmadas na camada de serviço - as transações não estão relacionadas ao OSIV. É o
Session
que permanece aberto, não uma transação - em execução.se as camadas do seu aplicativo estão espalhadas por várias máquinas, você praticamente não pode usar o OSIV - você deve inicializar tudo o que precisa antes de enviar o objeto pela rede.
OSIV é uma maneira agradável e transparente (ou seja - nenhum dos seus códigos sabe que isso acontece) de usar os benefícios de desempenho do carregamento lento
fonte
Eu não diria que Open Session In View é considerado uma prática ruim; o que te dá essa impressão?
Open-Session-In-View é uma abordagem simples para lidar com sessões com o Hibernate. Porque é simples, às vezes é simplista. Se você precisa de um controle refinado sobre suas transações, como ter várias transações em uma solicitação, Open-Session-In-View nem sempre é uma boa abordagem.
Como outros apontaram, existem algumas desvantagens para OSIV - você está muito mais sujeito ao problema N + 1 porque é menos provável que perceba quais transações está iniciando. Ao mesmo tempo, significa que você não precisa alterar sua camada de serviço para se adaptar a pequenas mudanças em sua visualização.
fonte
Se estiver usando um contêiner de Inversão de Controle (IoC), como o Spring, você pode querer ler sobre o escopo do bean . Essencialmente, estou dizendo ao Spring para me dar um
Session
objeto Hibernate cujo ciclo de vida abrange toda a solicitação (ou seja, ele é criado e destruído no início e no final da solicitação HTTP). Não preciso me preocupar emLazyLoadException
fechar a sessão, pois o contêiner IoC gerencia isso para mim.Conforme mencionado, você terá que pensar sobre os problemas de desempenho do N + 1 SELECT. Você sempre pode configurar sua entidade Hibernate posteriormente para fazer o carregamento de junção antecipada em locais onde o desempenho é um problema.
A solução de escopo do bean não é específica do Spring. Eu sei que o PicoContainer oferece a mesma capacidade e tenho certeza que outros containers IoC maduros oferecem algo semelhante.
fonte
Em minha própria experiência, OSIV não é tão ruim. O único arranjo que fiz foi usar duas transações diferentes: - a primeira, aberta na "camada de serviço", onde tenho a "lógica de negócios" - a segunda aberta imediatamente antes da exibição da exibição
fonte
Acabei de postar algumas orientações sobre quando usar a sessão aberta em vista no meu blog. Verifique se estiver interessado.
http://heapdump.wordpress.com/2010/04/04/should-i-use-open-session-in-view/
fonte
Estou muito enferrujado no Hibernate .. mas acho que é possível ter várias transações em uma sessão do Hibernate. Portanto, os limites da sua transação não precisam ser os mesmos dos eventos de início / parada da sessão.
OSIV, imo, é principalmente útil porque podemos evitar escrever código para iniciar um 'contexto de persistência' (também conhecido como sessão) toda vez que a solicitação precisa fazer um acesso ao banco de dados.
Em sua camada de serviço, você provavelmente precisará fazer chamadas para métodos que têm necessidades de transação diferentes, como 'Requerido, Novo requerido, etc.' A única coisa que esses métodos precisam é que alguém (ou seja, o filtro OSIV) tenha iniciado o contexto de persistência, de modo que a única coisa com que eles precisam se preocupar é - "ei, dê-me a sessão de hibernação para este tópico. Eu preciso fazer alguns Coisas DB ".
fonte
Isso não vai ajudar muito, mas você pode verificar meu tópico aqui: * Hibernate Cache1 OutOfMemory com OpenSessionInView
Eu tenho alguns problemas de OutOfMemory por causa do OpenSessionInView e muitas entidades carregadas, porque eles permanecem no cache Hibernate level1 e não são coletados como lixo (carrego muitas entidades com 500 itens por página, mas todas as entidades permanecem no cache)
fonte