Nesse post apresento três formas de resolver erros de Lazy
Loading.
O JPA desenvolveu uma funcionalidade
chamada de Lazy Loading para os atributos de uma classe. O Lazy Loading nada
mais é do que, a informação desejada só será trazida do banco de dados quando
ela for usada.
É exatamente no momento da solicitação
de acesso a uma coleção lazy que o erro acontece. O JPA/Hibernate tenta acessar
o banco de dados para buscar a informação que foi definida como Lazy, mas a
conexão foi fechada. Por isso que a exceção acontece, pela falta de uma conexão
aberta.
Todo relacionamento que termina em Many
é lazy por default:@OneToMany, @ManyToMany. Todo relacionamento que termine em
One é EAGER por default: @OneToOne.
Para definir um campo simples (ex.: String nome) como Lazy basta fazer
@Basic(fetch=FetchType.LAZY), dessa forma o campo mesmo sendo simples terá o
seu carregamento apenas quando for usado. Todos os campos simples (ex.: String,
int, double) que se encontram com dentro da classe sem a configuração LAZY são
considerados como EAGER.
Atualmente o modo mais simples de trazer a lista na primeira vez que seu
objeto for carregado é alterando a anotação.
@OneToMany(fetch=FetchType.EAGER)
Esse tipo de abordagem é aconselhável apenas para casos em que temos a
certeza de que a lista será sempre pequena.
A segunda maneira é por Open Session in View (ou Transaction View para
outros). É um padrão de projeto onde você deixa a conexão aberta durante toda a
requisição do usuário. Quando o acesso à lista for feito pela página o
Hibernate irá realizar uma consulta no banco de dados sem problemas, nenhuma
exceção será lançada.
Esse padrão de projeto para web utiliza uma classe que implementa a
interface Filter; filtro irá receber todas as requisições feitas ao servidor e
realizar duas ações básicas: abrir a transação antes de encaminhar a requisição
e finalizar a transação na volta da requisição. Assim, é garantido que o
hibernate irá conseguir realizar as transações. Não se esqueça de configurar o
filtro no arquivo web.xml.
package
com.filter;
import
java.io.IOException;
import
javax.annotation.Resource;
import
javax.servlet.*;
import
javax.transaction.UserTransaction;
public
class
ConnectionFilter
implements
Filter {
@Override
public
void
destroy() {
}
@Resource
private
UserTransaction utx;
@Override
public
void
doFilter(ServletRequest request,
ServletResponse response, FilterChain chain)
throws
IOException, ServletException {
try
{
utx.begin();
chain.doFilter(request, response);
utx.commit();
}
catch
(Exception e) {
e.printStackTrace();
}
}
@Override
public
void
init(FilterConfig arg0)
throws
ServletException {
}
}
É necessário ter bastante cuidado com
erros nas transações. Uma mensagem de sucesso pode ser enviada pelo
ManagedBean/Servlet e um erro acontecer no filtro. Pode gerar o chamado
n+1 de consultas. O objeto A tem uma lista de objetos do tipo B, que para ser carregada,
será realizada uma nova consulta no banco. Se o objeto B tiver uma lista de
objetos do tipo C, esta por sua vez irá gerar uma nova consulta, e assim por
diante. Esse é uma coisa a se considerar nesta“abordagem”. Uma consulta
pode gerar um número grande de outras consultas.
A terceira maneira que vou abordar é
através de uma Join Query no momento da consulta. Cria-se no DAO uma consulta
alternativa, que além de buscar o objeto principal, faz-se um join trazendo
os objetos da lista que compõe o objeto principal.
public
Pessoa buscaPessoaComAnimais(Long codigo) {
Query query = entityManager.createQuery(
"select p from Pessoa p join fetch p.animais where p.codigo = :codigo"
);
query.setParameter(
"codigo"
, codigo);
Pessoa pessoa = (
Person) query.getSingleResult();
return
pessoa;
}
Assim quando necessitar de trazer a pessoa
e a lista de animais vinculados a ela, usa-se esse método criado
especificamente para isso. A única desvantagem dessa abordagem se dá pelo fato de ser necessário uma consulta para cada lista de objetos que se deseja usar.
Ainda existe outra abordagem, que se
aplica para EJB's. Caso queira verificar a outra maneira, pode dar uma olhada
no link citado na fonte.Uma dica interessante, é que alguns componentes do primefaces tem uma propriedade chamada "collectionType", como é o caso do <selectManyCheckbox>, e em alguns casos, mesmo com a configuração do sistema para trabalhar com o Lay Loading já realizada, esses componentes podem continuar apresentando esse erro. Para resolver, informe esta propriedade no componente passando qual coleção você irá usar, como no exemplo:
collectionType="java.util.ArrayList"
Fonte: http://uaihebert.com/quatro-solucoes-para-lazyinitializationexception
Nenhum comentário:
Postar um comentário