O OSIV Anti-Padrão
Em vez de deixar a camada de negócios decidir como é melhor buscar todas as associações que são necessárias para a camada de Visualização, OSIV (Sessão Aberta na Visualização) força o Contexto de Persistência a permanecer aberto para que a camada de Visualização possa acionar a inicialização do Proxy, conforme ilustrado pelo diagrama a seguir.
- O
OpenSessionInViewFilter
chama o openSession
método do subjacente SessionFactory
e obtém um novo Session
.
- O
Session
está vinculado ao TransactionSynchronizationManager
.
- O
OpenSessionInViewFilter
chama doFilter
da javax.servlet.FilterChain
referência do objeto e a solicitação é processada posteriormente
- O
DispatcherServlet
é chamado e encaminha a solicitação HTTP para o subjacente PostController
.
- O
PostController
chama o PostService
para obter uma lista de Post
entidades.
- O
PostService
abre uma nova transação e HibernateTransactionManager
reutiliza a mesma Session
que foi aberta pelo OpenSessionInViewFilter
.
- O
PostDAO
busca a lista de Post
entidades sem inicializar qualquer associação preguiçosa.
- O
PostService
confirma a transação subjacente, mas Session
não é fechado porque foi aberto externamente.
- O
DispatcherServlet
começa a renderizar a IU, que, por sua vez, navega pelas associações lazy e aciona sua inicialização.
- O
OpenSessionInViewFilter
pode fechar o Session
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ê vê 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 zombando da 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 e FetchMode.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 é mantida durante toda a fase de renderização da IU, o que aumenta o tempo de concessão da conexão e limita o rendimento geral da transação devido ao congestionamento no conjunto de conexões 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.
Spring Boot e OSIV
Infelizmente, OSIV (Open Session in View) é habilitado por padrão no Spring Boot , e OSIV é realmente uma má ideia do ponto de vista de desempenho e escalabilidade .
Portanto, certifique-se de que, no application.properties
arquivo de configuração, você tenha a seguinte entrada:
spring.jpa.open-in-view=false
Isso desabilitará o OSIV para que você possa lidar da LazyInitializationException
maneira certa .
A partir da versão 2.0, o Spring Boot emite um aviso quando o OSIV está habilitado por padrão, para que você possa descobrir esse problema muito antes que ele afete um sistema de produção.
Para obter mais detalhes sobre OSIV, consulte este artigo .