Ser Lazy (preguiçoso) ou não ser?  XML
Índice dos Fóruns » Ferramentas, Frameworks e Utilitários
Autor Mensagem
saoj
Forum Spammer
[Avatar]

Membro desde: 09/03/2004 23:34:46
Mensagens: 2292
Localização: Los Angeles, EUA
Offline

A discussão aqui é se vale a pena usar Lazy Loading automático ou se é melhor fazer o lazy loading na mão mesmo.

Imagine um Carro que contém um object Motor dentro dele.



O conceito de Lazy Loading, dita que quando vc carregar o Carro do banco-de-dados, o Motor não será carregado imediatamente, mas apenas após o método getMotor() ser chamado por alguém.

Ou seja, se ninguém precisar do motor, o motor nunca será carregado do banco de dados.

A primeira vista isso parece ser uma coisa maravilhosa. Vc para e pensa: "Estou economizando acesso ao banco-de-dados!" Se meu carro tivesse outros 10 objetos dentro dele (lista de peças, rodas, bancos, etc), nenhum desses objetos seria carregado de primeira, ou seja, eu economizaria tempo, banco-de-dados, e a coisa seria feita sob demanda, a medida que alguém precisasse desses objetos e chamasse seu método get.

Agora vamos pensar por uma outra ótica:

1) Para a solução de ORM (Hibernate, iBatis, etc) implementar isso é necessário o uso de PROXIES. Isso porque quando alguém chamar o getMotor(), precisamos interceptar isso, fazer o load do motor, colocar o motor no Carro e retornar a execução para o método normal getMotor(). Para implementar proxies em Java vc precisa da implementação do JDK ou de CGLIB. Se for usar a implementação do JDK, o seu objeto (bean) precisa ser uma interface, ou seja, na minha opinião um preço muito grande a se pagar! Se for usar CGLIB, vc (e a jvm que for executar isso) precisa se sentir confortável com alteração de bytecode, para executar esse proxy "por trás dos panos". Lembre-se que tudo isso tem um custo de performance, que concordo "muitas vezes" pode ser ignorado.

2) LazyLoading acaba levando a uma prática não muito boa, que é fazer acessos ao banco-de-dados enquando uma página JSP está sendo executada. O que acontecerá se uma exception for lançada ali devido a algum erro de acesso ao banco de dados. O seu framework e a sua aplicação web estão prontos para tratar isso de forma correta? Não seria melhor que todo esse trabalho sujo fosse feito na action e quando chegássemos no JSP tudo já estivesse prontinho, ou seja, o JSP é apenas uma coisa burra que pega os resultados e exibe?

3) LazyLoading exige que vc (ou o seu framework) não feche a sessão/connection até que o JSP seja completamente executado, caso contrário vc vai ganhar um LazyInstantiationException. Ou o seu framework faz isso pra vc através de um filtro, ou vc vai ter que implementar isso não mão, provavelmente através de um ServletFilter.

Diante dos pontos acima, fica a pergunta. Será LazyLoading automático realmente uma coisa necessária e maravilhosa? Não seria melhor deixamor nós de sermos Lazy (preguiçosos) e controlarmos nós mesmos quem deve ser carregado e quando?

Voltemos ao exemplo do Carro com Motor.

Se eu sei que vou fazer um forward para uma página JSP que não vai precisar do motor do carro, ou seja, vai usar apenas o carro e não as informações do motor, eu não preciso me preocupar em carregar o motor.



E se eu sei que a minha página JSP vai precisar das informações do motor eu simplesmente carrego o motor eu mesmo: (ou quem sabe pego de um cache????)



A questão para debate é:

Precisamos realmente de LazyLoading automático ou devemos deixar de ser lazy (preguiçosos) e fazer nós mesmos esse controle de "quem é carregado, quando e onde"? Vc já teve problema com LazyInstantiationException?


Participe dos meus novos blogs:
O Poder Primário - Você no controle da sua felicidade
Sedução Tecnológica - Tutoriais, dicas e histórias de um engenheiro

[Email] [WWW]
Mrwin
JavaBaby
[Avatar]

Membro desde: 28/06/2006 14:28:10
Mensagens: 93
Localização: Recife - PE
Offline

Já tive sim saoj, muitos de meus beans são "entrelaçados", qdo chamo um, posso ou não chamar os "filhos"... já levei uma cacetada de lazyInstantionException...
Solução?
Todas os meus beans que tem alguma coleção eu sempre deixo lazy false.
Resolveu meu problema de vez...
quanto a fazer direto na mão como vc fez, ainda não tinha pensando nisto, somente testando performance é que vou verificar se vale a pena.

Todas as coisas cooperam para o bem daqueles que amam a Deus.
[Email]
foliveira81
JavaGuru

Membro desde: 10/09/2006 21:57:52
Mensagens: 202
Offline

Amigo na empresa que trabalho e outra que trabalhei sempre usamos lazy quando temos set de objetos "Coleções", isso realmente para economizar em memoria, criamos metodos para fazer laze do que precisamos e quando precisarmos, tambem usamos o cache do hibernate ajuda muito, dificilmente usamos proxy pois a necessiade de usar proxy é so quando querermos o id dos pk certo..

meu ponto de vista, usar lazy sim acho uma boa pratica porem cuidados devemos tomar sempre para nao precisar ficar vendo msg de exeção desagradavel na telas. e esse cuidados deve ser tomado assim que vc conhece o negocio digo como funciona as telas se realmente vc vai precisar dos filhoes e assim por diante !

abraços

Fernando Oliveira
[MSN]
Paulo Silveira
Administrador
[Avatar]

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

saoj wrote:
1) ... Se for usar CGLIB, vc (e a jvm que for executar isso) precisa se sentir confortável com alteração de bytecode, para executar esse proxy "por trás dos panos". Lembre-se que tudo isso tem um custo de performance, que concordo "muitas vezes" pode ser ignorado.


Custo de perfomance é constante, tendendo a zero quando voce medir o custo da execucao do SQL. Quem tem de se sentir confortavel com a proxy é o hibernate, e nao eu.... ainda bem que nem preciso ver isso na minha frente quando uso hibernate.

saoj wrote:
2) LazyLoading acaba levando a uma prática não muito boa, que é fazer acessos ao banco-de-dados enquando uma página JSP está sendo executada. O que acontecerá se uma exception for lançada ali devido a algum erro de acesso ao banco de dados. O seu framework e a sua aplicação web estão prontos para tratar isso de forma correta? Não seria melhor que todo esse trabalho sujo fosse feito na action e quando chegássemos no JSP tudo já estivesse prontinho, ou seja, o JSP é apenas uma coisa burra que pega os resultados e exibe?


No hibernate, voce pode, programaticamente, modificar se quer pegar algo que era lazy como eager.

saoj wrote:
3) LazyLoading exige que vc (ou o seu framework) não feche a sessão/connection até que o JSP seja completamente executado, caso contrário vc vai ganhar um LazyInstantiationException. Ou o seu framework faz isso pra vc através de um filtro, ou vc vai ter que implementar isso não mão, provavelmente através de um ServletFilter.


Nao exige. A conexao podera ser fechada, e a session abre outra na proxima transacao/query. Na verdade, o legal eh que o hibernate abstrai isso tudo pra mim. Pouco me importa o pooling e managing de connection dele, eqto estou programando. O entity manager tambem pode ser fechado, e alguns vendors, como o toplink, reabrem se necessario. Pessoalmente nao gosto.

saoj wrote:
Diante dos pontos acima, fica a pergunta. Será LazyLoading automático realmente uma coisa necessária e maravilhosa? Não seria melhor deixamor nós de sermos Lazy (preguiçosos) e controlarmos nós mesmos quem deve ser carregado e quando?


Se nada for lazy, todo e qualquer select vai levantar metade do banco de dados em memoria. Eu prefiro colocar o que der eager, desde que nao seja colecao ou que o caminho no grafo fique longo, e evitar ao maximo as colecoes no outro lado da ponta.

saoj wrote:
E se eu sei que a minha página JSP vai precisar das informações do motor eu simplesmente carrego o motor eu mesmo: (ou quem sabe pego de um cache????)


Tipica decisao pra ser deixada pro framework, nao pra mim. A ideia de um framework é ele trabalhar por mim, e nao o contrario. Ficar fazendo if para verificar se tem o objeto no cache é mortal, vai dar spagueti.


http://blog.caelum.com.br


Arquitetura e Design de Software: uma visão sobre a plataforma java
[Email] [WWW]
saoj
Forum Spammer
[Avatar]

Membro desde: 09/03/2004 23:34:46
Mensagens: 2292
Localização: Los Angeles, EUA
Offline


Se nada for lazy, todo e qualquer select vai levantar metade do banco de dados em memoria. Eu prefiro colocar o que der eager, desde que nao seja colecao ou que o caminho no grafo fique longo, e evitar ao maximo as colecoes no outro lado da ponta.


saoj wrote:
A discussão aqui é se vale a pena usar Lazy Loading automático ou se é melhor fazer o lazy loading na mão mesmo.


A discussão não é se lazy loading é bom ou ruim. Lazy loading é fundamental / essencial.

A questão é se o cara deve deixar o framework fazer isso de forma automática quando alguém chamar getMotor() ou se é melhor ele mesmo controlar isso, ou seja, ele mesmo carrega as coisas conforme elas forem necessárias.

Como boa prática, acho que os próprios autores do hibernate recomendam que vc force o lazy loading automático antes de dar um forward, ou seja, antes de dar o forward vc faz uma chamada a getMotor().



Nao exige. A conexao podera ser fechada, e a session abre outra na proxima transacao/query. Na verdade, o legal eh que o hibernate abstrai isso tudo pra mim. Pouco me importa o pooling e managing de connection dele, eqto estou programando. O entity manager tambem pode ser fechado, e alguns vendors, como o toplink, reabrem se necessario. Pessoalmente nao gosto.


Se a session do hibernate for fechada antes de acontecer o lazy loading automatíca, ou seja, antes de chamarem getMotor(), vc ganha uma LazyInstantiationException. E o problema é que muitas vezes getMotor() vai ser chamado na view, logo vc tem que ter certeza (ou o seu framework) de que a session só vai ser fechada APÓS a view ser executada.

Documentação do Hibernate:

Documentação do Hibernate wrote:
In a web-based application, a servlet filter can be used to close the Session only at the very end of a user request, once the rendering of the view is complete (the Open Session in View pattern). Of course, this places heavy demands on the correctness of the exception handling of your application infrastructure. It is vitally important that the Session is closed and the transaction ended before returning to the user, even when an exception occurs during rendering of the view. The servlet filter has to be able to access the Session for this approach. We recommend that a ThreadLocal variable be used to hold the current Session (see chapter 1, Section 1.4, " Playing with cats ", for an example implementation).

Participe dos meus novos blogs:
O Poder Primário - Você no controle da sua felicidade
Sedução Tecnológica - Tutoriais, dicas e histórias de um engenheiro

[Email] [WWW]
J2Alex
JavaEvangelist
[Avatar]

Membro desde: 18/01/2003 08:14:41
Mensagens: 341
Localização: São José dos Campos
Offline

Usar lazy ou não é uma questão que deve ser analisada pontualmente, dependendo exclusivamente da necessidade naquele determinado momento. Não existe uma regra única, existe a solução mais adequada para cada situação.

A grande vantagem é que o hibernate te permite configurar o padrão e mudar esse padrão em tempo de execução de acordo com a sua necessidade.

O que pode ser mais flexível que isso?

Alexandre ( J2Alex )

Desenvolvedor Java EE
ITA (Instituto Tecnológico de Aeronáutica)

Sun Certified Programmer for the Java 2 Platform, Standard Edition 5.0

Não temeis pelos dias que virão - tens a espada e tens as honras e um coração gentil.
aim icon
Paulo Silveira
Administrador
[Avatar]

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

saoj wrote:
A questão é se o cara deve deixar o framework fazer isso de forma automática quando alguém chamar getMotor() ou se é melhor ele mesmo controlar isso, ou seja, ele mesmo carrega as coisas conforme elas forem...


Entendo sua duvida. O que der para deixar o framework fazer, é sempre melhor. Se precisar mudar, deixar opcional. É como eu disse que o hibernate faz: poe LAZY, se quiser, pela query muda pra EAGER.

saoj wrote:
Como boa prática, acho que os próprios autores do hibernate recomendam que vc force o lazy loading automático antes de dar um forward, ou seja, antes de dar o forward vc faz uma chamada a getMotor().


Acho que não. E deixa o codigo feio demais essas chamadas getters que aprecem nonsense.

saoj wrote:
Se a session do hibernate for fechada antes de acontecer o lazy loading automatíca, ou seja, antes de chamarem getMotor(), vc ganha uma LazyInstantiationException. E o problema é que muitas vezes getMotor() vai ser chamado na view, logo vc tem que ter certeza (ou o seu framework) de que a session só vai ser fechada APÓS a view ser executada.



Como eu disse, NO ORACLE TOPLINK, ele reabre o entity manager se voce precisar. No Hibernate obviamente ele nao faz, e ja disseram que nao vao fazer e sao contra isso. Fechar a session depois é trivial por um interceptador ou Filter, como voce mesmo disse.

J2Alex wrote:
A grande vantagem é que o hibernate te permite configurar o padrão e mudar esse padrão em tempo de execução de acordo com a sua necessidade. O que pode ser mais flexível que isso?


Perfeito

http://blog.caelum.com.br


Arquitetura e Design de Software: uma visão sobre a plataforma java
[Email] [WWW]
carneiro
JavaEvangelist
[Avatar]

Membro desde: 07/04/2005 11:37:42
Mensagens: 328
Offline

Bem, usar LazyLoading ou não depende da situação.

Aqui no meu trabalho, havia uma tela cuja busca demorava cerca de 15 minutos! Fui analisar e vi que ele buscava todas as dependências dos objetos (e eram muitas!!!), estava com lazy="false".

Coloquei esse relacionamento como lazy="true", e nas outras buscas que eram mais pontuais trouxe as dependências na mão (Hibernate.initialize()).

No fim das contas, a busca gastava menos de 1 minuto!

Davi Luan Carneiro
Desenvolvedor JEE
[Email] [MSN]
urubatan
Moderador
[Avatar]

Membro desde: 21/09/2002 10:31:26
Mensagens: 2449
Localização: Porto Alegre/RS
Offline

bom, concordo que lazy ou não depende do caso ...
mas imagine a seguinte situação:
os objetos persistentes possuem lógica de negócio ...
e dependendo da operação, eles precisam navegar em suas coleções, e referencias para seus pais ...
mas isto só acontece em uma ou duas operações destes objetos ...
e estas operações não são chamadas sempre ...
mas estas mesmas operações são realizadas em cascata ...

neste caso, eu consigo enxergar apenas 3 soluções possíveis:
Carregar o banco de dados todo na memória pois se eu chamar um destes métodos os dados para qualquer uma das hierarquias devem estar disponíveis
Fazer com que os proprios objetos persistidos saibam acessar o banco de dados, o que acarretaria problemas e aumentaria o acoplamento da regra de negócio com a persistencia
Utilizar lazy loading automático (como o do hibernate)
partir para uma arquitetura onde os objetos persistidos sejam apenas value objects sem lógica nenhuma

lógico, este é um caso apenas, mas neste caso, o lazy loading é uma excelente opção, e praticamente obrigatório na minha opinião, visto que a primeira opção é inviável, a segunda bastante indesejável, e a ultima não me agrada nem um pouquinho ...


[]'s
Rodrigo Urubatan
http://www.urubatan.com.br - pt_BR
http://www.urubatan.info - en_US
Arquiteto J2EE
Melhor livro de RoR do brasil: http://livro.urubatan.com.br
[WWW]
saoj
Forum Spammer
[Avatar]

Membro desde: 09/03/2004 23:34:46
Mensagens: 2292
Localização: Los Angeles, EUA
Offline


Excelente colacação Urubatan. Obrigado!

Por que isso não te agrada muito:


partir para uma arquitetura onde os objetos persistidos sejam apenas value objects sem lógica nenhuma

Participe dos meus novos blogs:
O Poder Primário - Você no controle da sua felicidade
Sedução Tecnológica - Tutoriais, dicas e histórias de um engenheiro

[Email] [WWW]
urubatan
Moderador
[Avatar]

Membro desde: 21/09/2002 10:31:26
Mensagens: 2449
Localização: Porto Alegre/RS
Offline

Bom, não sou exatamente eloqüente o suficiente para detalhar o por que eu não gosto desta abordagem ...
um possível argumento é que utilizar entidades persistentes apenas como structs para carregar dados é contra a idéia de orientação a objetos ...
um dos melhores textos sobre isto que ja encontrei é o do wiki do Philip Calçado

Java não é uma linguagem que facilita muito um "modelo rico", mas eu prefiro não utilizar também um "modelo anêmico", faço algo que se aproxima ao meio termo ...
mantendo dentro dos objetos toda a lógica relacionada aos próprios dados ...
por exemplo, quando eu adiciono um objeto a uma coleção, o próprio objeto sabe se tem que alterar alguma propriedade daquele filho, ou de algum outro objeto relacionado a ele ...
se tem que fazer alguma validação e não permitir que seja adicionado aquele objeto a coleção ...

e para isto o objeto precisa ter acesso aos filhos, mas apenas se isto for necessário (caso contrario eu precisaria de todos os dados em memória)

eu ja implementei sistemas utilizando modelos anêmicos ...
mas seguindo este mesmo exemplo de adicionar um objeto a uma coleção com validações ...

eu não poderia ter um metodo "addItem" na classe nota fiscal, pois esta possivelmente não teria acesso a todos os itens ja existentes para verificar se este objeto ja estava na coleção, se precisaria atualizar algum dado, ...
eu precisaria ter um NotaFiscalManager da vida, que teria um metodo "addItem" recebendo o item e a nota fiscal como parametro o que pelo menos na minha opinião ficaria feio ...

lógico, este exemplo que eu apresentei é simples o suficiente para ser implementado de outras formas, mas acho que ja esclarece um pouco o meu ponto de vista

[]'s
Rodrigo Urubatan
http://www.urubatan.com.br - pt_BR
http://www.urubatan.info - en_US
Arquiteto J2EE
Melhor livro de RoR do brasil: http://livro.urubatan.com.br
[WWW]
TheMask
JavaBaby
[Avatar]

Membro desde: 14/04/2006 19:28:16
Mensagens: 79
Offline

urubatan wrote:Bom, não sou exatamente eloqüente o suficiente para detalhar o por que eu não gosto desta abordagem ...
um possível argumento é que utilizar entidades persistentes apenas como structs para carregar dados é contra a idéia de orientação a objetos ...
um dos melhores textos sobre isto que ja encontrei é o do wiki do Philip Calçado


Aqui, o cv tem um resumo bem pragmático desse artigo :

cv wrote:
Er... a premissa da OO nao era colocar os dados e os comportamentos relacionados a eles proximos uns dos outros?

Se sim, pra que caralhos vc vai separar os dados e comportamento em VO e BO?

**** Demaaaaaaais ****
fabio.patricio
Forum Spammer

Membro desde: 04/01/2004 02:51:33
Mensagens: 1512
Localização: Porto Alegre - RS
Offline

Ola,

Boa discussao!

Eu particularmente não gosto deste modelo de ter na action do framework esse tipo de regra. O codigo parece ficar proceduralzao, e o como o Paulo ja disse if's verificando se um objeto esta null e precisa ser carregado é um tanto quanto feio.

Na verdade essa solucacao da action parece muito com o que muita gente faz quando tem lazy=true e ta tendo problema de LazyInitialization. Usam o famoso Hibernat.initialize().

Um outro ponto importante, estes tempo eu tava dando consultoria num sistema que estava todo montado com lazy=false. O tamanho das queries chegavam a ter mais de 32 mil caracteres. A performance nem preciso comentar.

]['s

Fabio Patricio
http://blog.wansoft.com.br

[WWW] [MSN] [ICQ]
pcalcado
Moderador
[Avatar]

Membro desde: 08/03/2004 17:19:35
Mensagens: 5170
Localização: Sydney - Australia
Offline

Sobre o domínio pobre, além dos motivos citados nos artigos linkados pelo Urubatan, tem a situaçõa onde nós estamos em 2007 utilizando uma técnica que fazia sentido em 2000 e já foi vencida há quatro anos, pelo menos.

Fazer lazy loading 'na mao' com JDBC é inviável. Ainda que você disponha de estruturas de dados burras e não objetos na sua aplicação vai acabar com um DAO com métodos do tipo:



Praticamente um ou mais métodos por caso de uso. Eu já lidei com isso algumas vezes e vivi muitoe ste drama.

Phillip Calçado "Shoes"
http://fragmental.tw/
http://blog.fragmental.com.br/
"It is unfortunate that much of what is called 'object-oriented programming today is simply old style programming with fancier constructs." - Alan Kay
[Email] [WWW] [Yahoo!] [MSN]
saoj
Forum Spammer
[Avatar]

Membro desde: 09/03/2004 23:34:46
Mensagens: 2292
Localização: Los Angeles, EUA
Offline

Fazer lazy loading na mão é inviável mesmo. Falei em ao invés de deixar que o lazy loading aconteça automaticamente, vc controlaria quando e onde ele aconteceria, ou seja, vc saberia quando e onde as dependencias são realmente necessários e faria o lazy loading carregando as dependencias na mão através do framework de ORM em questão. No caso do hibernate já vi algumas pessoas sugerirem que vc force o lazy loading antes de dar um forward para a página.


Some developers consider this to be bad practice however (understandably so), because the transparent lazy fetching of the parent in the above example is not done until you are in your view rendering implementation. That means that if the SQL produced and executed by Hibernate throws any exceptions, or anything else goes wrong, your view is now responsible for handling it (e.g. your JSP); and as many of us have learned, one of the key benefits of this 2-step servlet-first approach is that any errors can be handled by the servlet, which a.) is more succeptible to pluggable error-handling approaches and b.) is prior to the response being committed, so error pages can still be loaded, etc.



Alternatively, the former solution is to pre-load any resources you know you're going to need in a view. This solution, while being more verbose, is perhaps more accurate, and provides more control if something were to go wrong. To do this, I typically still use the HibernateUtil solution. However, closing it as part of the servlet process itself (rather than through a filter) ensures that the session is out of the picture by the time the view is rendered.


Entendo porém que se vc tem um grafo de objetos de mais de 3 níveis seria muito tedioso fazer na mão o carregamento das dependencias. Não tenho dados estatísticos mas acredito que grafos de objetos de mais de 3 níveis são exceção. Mas entendo que existem...

Quanto ao domínio pobre eu concordo. Sö queria ouvir, a título de aprendizado mesmo, argumentos técnicos sobre o porque isso é mais recomendável.

Participe dos meus novos blogs:
O Poder Primário - Você no controle da sua felicidade
Sedução Tecnológica - Tutoriais, dicas e histórias de um engenheiro

[Email] [WWW]
 
Índice dos Fóruns » Ferramentas, Frameworks e Utilitários
Ir para:   
Powered by JForum 2.1.8 © JForum Team