Alguém usa algum padrão diferente de Open Session In View ao trabalhar com JPA?  XML
Índice dos Fóruns » Java Avançado
Autor Mensagem
andredecotia
JWizard
[Avatar]

Membro desde: 19/10/2009 14:37:32
Mensagens: 2267
Localização: São Paulo
Offline

Alguém usa algum padrão diferente de Open Session In View ao trabalhar com JPA?


--
André AS


Analista Programador Java Web freelancer / home office
Linkedin: http://www.linkedin.com/profile/view?id=41470291&trk=tab_pro

[Email] [MSN] [ICQ]
Flavio Almeida
Java Ninja
[Avatar]

Membro desde: 06/11/2009 14:01:46
Mensagens: 251
Localização: Niterói, RJ
Offline

No lugar de usar OpenSessionInView, que é na minha opinião um dos antipatten mais perigosos com JPA + JSF (digo isso, porque já conheci muita gente reclamando da performance do JPA com JSF por não conhecerem as conseqüências desse pattern), você pode usar OpenTransactionInApplicationPhaseListener.

Diferente do OpenSessionInView, o OpenTransactionInApplicationPhaseListener é um "pouquinho" mais difícil de implementar e demanda do desenvolvedor um entendimento CLEAR de cada consulta ao banco de dados. Com certeza você terá mais trabalho e gastará mais tempo para desenvolver utilizando este filtro, mas sua aplicação terá um bom desempenho, pode ter certeza.

O problema do OpenSessionInView é que ele facilita a aparição do famoso "N + 1". Por exemplo, se você acessa algum dado lazy em uma datatable JSF com OpenSessionInView, quando o JSF for renderizar o datatable, ele fará uma consulta a mais ao banco de dados para cada registro renderizado no datatable!!! Com o OpenTransactionInApplicationPhaseListener, você receberá um lazyInitializationException, o que não é ruim, pois sinaliza para você que sua query precisa ser PLANEJADA. Nada impede que o programador PLANEJE suas consultas utilizando OpenSessionInView, mas como outros programadores menos conscienciosos podem estar envolvidos no projeto, abrir essa brecha pode ser "perigoso".

Agora, é tudo questão de bom senso. Se a sua aplicação é pequena e você precisa desenvolver rapidamente, o OpenSessionInView é uma alternativa.
O que é preciso entender é que o OpenSessionInView não é para evitar lazyInitializationException, isso é consequencia de outro problema que ele tenta solucionar: a abertura e fechamento de transações.

NÃO VÁ NO MEU BICO, tenho 13 anos de experiência na área, mas isso não quer dizer que o que digo é verdade absoluta. Já fui esclarecido várias vezes por programadores juniors neste forum (váaarias vezes). Tentei ser objetivo, mas não posso negar que as emoções afloram quando falo sobre esse assunto, uma vez que já tive que resolver vários problemas em decorrência dele devido ao trabalho de terceiros.

Como eu gosto de matar a cobra e mostrar o pau, se você se sentir inseguro com o OpenTransactionInApplicationPhaseListener, você pode dar uma olhada no livro do MyFaces, no qual o autor explica com exemplos as conseqüências desse pattern. Eu senti um alívio tão grande quando vi alguém de renome falar sobre esse assunto, pois já sinaliza esses efeitos colaterais do OpenSessionInView faz tempo.

OBS: estou desenvolvendo uma estratégia de abertura e fechamento de transação usando CDI sem a necessidade de outros plugins. Se eu tiver frutos, eu posto no GUJ. É uma solução hibrida.
OBS: como você usa JPA, talvez você tenha interesse em experimentar o conversor de Entidades genérico que desenvolvi e que o povo aqui do GUJ ajudou a melhorar: http://www.guj.com.br/java/220692-para-voce-entityconverter-para-qualquer-entidade-e-tipo-de-id

Abraço

This message was edited 14 times. Last update was at 30/01/2011 19:58:21


"o único homem educado é o homem que aprendeu a aprender" - Carl R. Rogers
andredecotia
JWizard
[Avatar]

Membro desde: 19/10/2009 14:37:32
Mensagens: 2267
Localização: São Paulo
Offline

Interessante...

--
André AS


Analista Programador Java Web freelancer / home office
Linkedin: http://www.linkedin.com/profile/view?id=41470291&trk=tab_pro

[Email] [MSN] [ICQ]
Paulo Silveira
Administrador
[Avatar]

Membro desde: 07/08/2002 18:38:50
Mensagens: 4205
Localização: São Paulo
Offline

Ola Flavio

Sua preocupacao com o n+1 é mais que valida. Mas isso não é relacionado ao open session in view diretamente. Mesmo sem mante-la aberta, se voce puxar uma entidade e chamar seus getters lazy, vai continuar tendo n+1 queries. Isso é: o responsavel pelo n+1 queries certamente é o lazy, e nao o OpenSessionInView.

Sem contar que, com JSF, o problema piora muito: algumas implementações disparam invocações aos seus getters 3, 4 vezes em um único request, ativando todas as suas proxies lazy, gerando muitas outras consultas. Esse é um problema de implementações do JSF, e não do Hibernate/OpenSessionInView.

Gostei da sua palavra: tem de ser planejado. Nao usar o lazy apenas pela magica. Alias, essa é a abordagem do pessoal do Objectify em relação ao BigTable do google: não há nada lazy, nada é feito sem você explicitamente pedir para carregar, dessa forma fica claro a quantidade de queries que você está disparando.

abracos

This message was edited 1 time. Last update was at 12/02/2011 11:20:10


http://blog.caelum.com.br twitter: @paulo_caelum


[Email] [WWW]
renanigt
Thread.start()
[Avatar]

Membro desde: 13/02/2008 13:09:59
Mensagens: 44
Offline

Então para se trabalhar com Hibernate/Jpa + JSF, a melhor solução seria utilizar OpenTransactionInApplicationPhaseListener ?!
Pois a utilização de EAGER para todos os lados mata a aplicação, então qual seria a melhor maneira de evitar os EAGER utilizando JPA + JSF ?!

Att,
Renan Montenegro

This message was edited 1 time. Last update was at 12/02/2011 12:09:52


Renan Montenegro
Twitter: renanigt
Paulo Silveira
Administrador
[Avatar]

Membro desde: 07/08/2002 18:38:50
Mensagens: 4205
Localização: São Paulo
Offline

Pois é Renan: colcoar tudo eager é impossível. Colocar tudo lazy e depois ficar fazendo fetch join para todas as queries seria também muito, muito trabalhoso.

Mas, mesmo com OpenSessionInView, se voce conhecer bem do hibernate, e fizer os ajustes necessarios no fetch-size/batch-size, além de configurar o second level cache para as coleções e queries, não apenas as entidades, pode diminuir o problema do n+1 para zero sem ter de tomar uma atitude drástica como fechar a session rapidamente.

http://blog.caelum.com.br twitter: @paulo_caelum


[Email] [WWW]
Monteiro
What is classpath?

Membro desde: 24/07/2006 17:32:39
Mensagens: 5
Offline

Essa discussão se aplica tb a implementação de API's web (tipo Twitter) que manipulam modelo persistente. Uma vez que a entidade quente chega ao marshaller (seja ele para XML ou JSon), pra montar o resultado todos os get's são invocados e a chance de problemas na inicialização de atributos lazy é mto grande.

No meu caso, a solução (pouco elegante) que adotamos foi manter, paralelo ao modelo persistente, um conjunto de VO's. Isso nos deu mais controle sobre a profundidade da informação no retorno e ajuda a contornar os LazyInitializationException. O contraponto é a chateação de manter dois modelos paralelos e a necessidade de criação de adaptadores entity/VO.
[MSN]
rponte
JavaEvangelist
[Avatar]

Membro desde: 18/02/2008 10:06:25
Mensagens: 413
Offline

Apenas um detalhe: o problema do open session in view tambem ocorre com frameworks action-like. Nao eh especifico do faces somente.

Alem do que, se nao estou enganado, esse pattern abre uma session do Hibernate, nao necessariamente uma transaçao com o banco a cada request.

Rafael Ponte
http://www.rponte.com.br/
[WWW]
Paulo Silveira
Administrador
[Avatar]

Membro desde: 07/08/2002 18:38:50
Mensagens: 4205
Localização: São Paulo
Offline

E lembrando que sempre devemos fazer da maneira mais esperta possivel: podemos fazer open session in view sem abrir uma session/transaction logo de cara, deixamos isso tambem lazy, e so abrimos se alguem vai utilizar, evitando ate mesmo a necessidade de demarcacao de que metodos vao querer session

Pois é, esse problema de granularidade ocorre em uma gama de outros lugares, sempre quando temos dois tiers diferentes envolvidos. Devemos minimizar a quantidade de informacoes, mas mais importante: o numero de roundtrips entre um tier e outro, evitando, em especial, n+1 roundtrips, com N variavel dependendo de dados. Isso pode facilmente derrubar servidores, mesmo com pouca carga no tier do cliente.

Eu evito ao maximo criar uma hierarquia paralela do model, pela dificuldade de manutencao. Como o Rafael Ponte disse no twitter, mesmo quando os dtos sao necessarios, voce nao precisa fazer um mapeamento de DTOS 1 para 1 em relacao a cada entidade que voce tem. Se justamente o problema é granularidade do servico, voce vai criar alguns poucos DTOs que carreguem bastante informacao (nem mais, nem menos do que o outro tier precisa), numa tacada so.

http://blog.caelum.com.br twitter: @paulo_caelum


[Email] [WWW]
renanigt
Thread.start()
[Avatar]

Membro desde: 13/02/2008 13:09:59
Mensagens: 44
Offline

Numa aplicação que já se encontra cheio de EAGER, qual seria a melhor solução, realmente aplicar o OpenSessionInView ?! Lembrando que a aplicação é em JSF.

Att,
Renan Teixeira Lima Verde Montenegro

Renan Montenegro
Twitter: renanigt
Paulo Silveira
Administrador
[Avatar]

Membro desde: 07/08/2002 18:38:50
Mensagens: 4205
Localização: São Paulo
Offline

Oi Renan

Se voce percebeu que o monte de eager ja esta carregando muito mais dados do que o necessario (como ver montanhas de sqls gigantes sendo disparadas quando o show_sql esta true, mesmo para um simples load de uma entidade), realmente usar o opensessioninview + colocar algumas relacoes como lazy podem melhorar _muito_ o numero de joins e queries. Mas as vezes alguns poucos @Cache em entidades e relacionamentos podem ajudar bastante. É preciso balancear.

http://blog.caelum.com.br twitter: @paulo_caelum


[Email] [WWW]
Flavio Almeida
Java Ninja
[Avatar]

Membro desde: 06/11/2009 14:01:46
Mensagens: 251
Localização: Niterói, RJ
Offline

Sua preocupacao com o n+1 é mais que valida. Mas isso não é relacionado ao open session in view diretamente. Mesmo sem mante-la aberta, se voce puxar uma entidade e chamar seus getters lazy, vai continuar tendo n+1 queries.


Paulo, talvez eu não tenha entendido corretamente a sua idéia, mas quando você diz "sem mante-la aberta", você está se referindo a Sesison/EntityManager? Se for, se ela não estiver aberta, acorrerá o Lazy Initialization Exception, ou seja, nem consulta N+1 será executada.


Apenas um detalhe: o problema do open session in view tambem ocorre com frameworks action-like. Nao eh especifico do faces somente.
Alem do que, se nao estou enganado, esse pattern abre uma session do Hibernate, nao necessariamente uma transaçao com o banco a cada request.


Rafael, realmente o problema do N+1 não é exclusivo do JSF, tampouco de qualquer outro framework, uma vez que você consegue gerar consultas N+1 em uma classe java standalone. Como trabalho com JSF, a minha idéia era evidenciar o problema que ocorre muitas vezes quando uma coleção Lazzy é renderizada em um Datatable ou qualquer componente durantente a fase de renderização da view (exemplo: apontar a coleção de uma entidade diretamente na datatable). Além disso, para os mais puristas (não sou purista, sou um cara prático , OpenSessionInView não se coaduna com a arquitetura em camadas, uma vez que você pode acessar seu banco de dados na camada de visão (RENDER VIEW). Esse pattern, em sua versão original http://community.jboss.org/wiki/OpenSessioninView faz o controle de transação.

Outro problema que não citei (baseado no pattern original), mas basta perguntar para qualquer um que já o utilizou é o que fazer quando há um erro durante a transação? O rollback fica no filtro, então, fica mais difícil dar uma mensagem específica ou controlar o fluxo da aplicação. OpenTransactionInApplicationPhaseListener sofre deste mesmo problema.

Um primeiro passo para que se possa abandonar este filtro ou minimizar seus efeitos é verificar cada consulta minuciosamente, PLANEJANDO-A sem pudor. Existe outra coisa, muito polêmica, mas que procurarei primeiro onde está a referência (não lembro) deste comentário para colar aqui. Eu sempre gosto de matar a cobra e mostrar o pau.


Gostei da sua palavra: tem de ser planejado. Nao usar o lazy apenas pela magica. Alias, essa é a abordagem do pessoal do Objectify em relação ao BigTable do google: não há nada lazy, nada é feito sem você explicitamente pedir para carregar, dessa forma fica claro a quantidade de queries que você está disparando.


Apesar de não ter "liberdade" de abandonar JPA, darei uma olhada "com carinho" no Objectify, pois compartilho da mesma visão. Valeu pela dica!


Numa aplicação que já se encontra cheio de EAGER, qual seria a melhor solução, realmente aplicar o OpenSessionInView ?! Lembrando que a aplicação é em JSF.

Renan, não é 8 nem 80, é planejamento. É por isso que o OpenSessionInView é simples, se você não planejar nada, ele funcionará, mas poderá acontecer os problemas que listei neste tópico.

Acho muito válido a participação de todos, porque esse é um assunto que precisava ser revisado.

This message was edited 4 times. Last update was at 15/02/2011 08:33:59


"o único homem educado é o homem que aprendeu a aprender" - Carl R. Rogers
Paulo Silveira
Administrador
[Avatar]

Membro desde: 07/08/2002 18:38:50
Mensagens: 4205
Localização: São Paulo
Offline

Flavio Almeida wrote:
Sua preocupacao com o n+1 é mais que valida. Mas isso não é relacionado ao open session in view diretamente. Mesmo sem mante-la aberta, se voce puxar uma entidade e chamar seus getters lazy, vai continuar tendo n+1 queries.


Paulo, talvez eu não tenha entendido corretamente a sua idéia, mas quando você diz "sem mante-la aberta", você está se referindo a Sesison/EntityManager? Se for, se ela não estiver aberta, acorrerá o Lazy Initialization Exception, ou seja, nem consulta N+1 será executada.



Nao expressei bem mesmo. quis dizer que, mesmo nao mantendno aberta a session na view, porem chamando seus getters lazy _antes_ de fecha-la (isso é, antes da view, atraves de Hibernate.initialize, ou invocando os getters lazy diretamente) voce vai passar pelos problemas do n+1. Por isso disse que nao é o open session in view o unico que pode gerar n+1. Mesmo sem open session in view isso pode acontecer.

Excelente discussao mesmo. precisa planejar

http://blog.caelum.com.br twitter: @paulo_caelum


[Email] [WWW]
Flavio Almeida
Java Ninja
[Avatar]

Membro desde: 06/11/2009 14:01:46
Mensagens: 251
Localização: Niterói, RJ
Offline

Entendi Paulo. Realmente, N+1 continua acontecendo. É como coloquei respondi acima: é possível gerar N+1 em uma classe java standalone, independente da combinação com outros frameworks.

"o único homem educado é o homem que aprendeu a aprender" - Carl R. Rogers
Flavio Almeida
Java Ninja
[Avatar]

Membro desde: 06/11/2009 14:01:46
Mensagens: 251
Localização: Niterói, RJ
Offline

Vai aqui um apelo para o pessoal que está começando a usar JPA/Hibernate: planejem suas consultas primeiro, para depois pensarem no uso de cache de segundo nível.
Mais uma vez, apareceram problemas em decorrência deste pattern para eu resolver.

"o único homem educado é o homem que aprendeu a aprender" - Carl R. Rogers
 
Índice dos Fóruns » Java Avançado
Ir para:   
Powered by JForum 2.1.8 © JForum Team