Pessoal, já peço desculpas por entrar num assunto que já deve ter gerado muita discussão, mas gostaria de receber algumas orientações!
VRaptor versão 2.6.0
Hibernate versão 3.5.4
Apache Tomcat versão 6.0.28
JVM versão 1.6.0_21-ea-b05
Servidor Debian 2.6.26-2-686-bigmem
Tenho uma aplicação que esta em produção a 4 anos e praticamente 90% anotada com @OneToMany(FetchType.LAZY), exceto é claro algumas anotações @OneToOne (que são EAGER por padrão), porém existe apenas uma única classe chamada Projeto que esta com uma coleção anotada com FetchType.EAGER, isto por causa de um Transiente que esta sendo chamado dentro de uma regra “validade LÓGICA”, já que ao colocar como FetchType.LAZY eu recebo a mensagem de erro:
ERROR [LazyInitializationException] failed to lazily initialize a collection of role: br.com.faespsenar.sicp.model.Projeto.periodosDeRealizacao, no session or session was closed.
Justamente no momento da chamada da propriedade transiente:
at br.com.faespsenar.sicp.model.Projeto.getDataPrevistaTemp(Projeto.java:823)
at br.com.faespsenar.sicp.logic.ProjetoAprovadoLogic.validateAtualizaProjeto(ProjetoAprovadoLogic.java:425)
OBS: A finalidade do getDataPrevistaTemp é obter a PRIMEIRA data de realização de um Projeto.
Eu já tentei até segui as orientações do nosso amigo Camilo Lopes através do seu blog, com as dicas do pessoal do Hibernate sobre Open Session View.
Porém ao aplicar o filtro (HibernateSessionRequestFilter), eu percebi uma queda de performance em todo o sistema e também não resolveu o problema de LazyInitializationException ao chamar o transiente getDataPrevistaTemp().
NOTA: Eu particularmente não gosto dos Transientes, porém temos alguns relatórios através do JasperReport que necessitam deles.
Então vamos lá…
Segue abaixo a classe Projeto.java:
@Entity
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Proxy(lazy = true)
public class Projeto implements Comparable<Projeto>, java.lang.Cloneable {
@Id
@SequenceGenerator(name = "seq_projeto", sequenceName = "seq_projeto_id_seq", initialValue = 1, allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_projeto")
private Long id;
...
...
...
@OneToMany(mappedBy = "projeto", cascade = {CascadeType.REMOVE}, fetch = FetchType.EAGER)
@BatchSize(size=50)
@OrderBy("localRealizacao")
private Set<LocalDeRealizacao> locaisDeRealizacao;
...
...
...
@Transient
private Date dataPrevistaTemp;
/**
* Retorna a data inicial do Periodo de Realização
* @return dataPrevistaTemp
*/
public Date getDataPrevistaTemp() {
if (this.getPeriodosDeRealizacao() != null && !this.getPeriodosDeRealizacao().isEmpty())
dataPrevistaTemp = this.getPeriodosDeRealizacao().iterator().next().getDataPrevista();
else
dataPrevistaTemp = null;
return dataPrevistaTemp;
}
// Demais Getters e Setters omitidos...
}
Segue abaixo a lógica ProjetoAprovadoLogic.java:
@Component("projetoaprovado")
@InterceptedBy({ DaoInterceptor.class, FactoryInterceptor.class, LoginInterceptor.class, DownloadInterceptor.class, NoCacheInterceptor.class })
public class ProjetoAprovadoLogic {
private DaoFactory daoFactory;
private Usuario usuarioConectado;
private ClientOutput clientOutput;
...
...
...
@Out @Parameter(create=true)
private Projeto projeto;
...
...
...
public ProjetoAprovadoLogic(DaoFactory daoFactory, Usuario usuarioConectado, ClientOutput clientOutput) {
this.daoFactory = daoFactory;
this.usuarioConectado = usuarioConectado;
this.clientOutput = clientOutput;
}
...
...
...
public void validateAtualizaProjeto(ValidationErrors errors) {
Projeto p = this.daoFactory.getProjetoDao().localizarUnico(this.projeto, "id");
...
...
...
// Nesta chamada ocorre o erro!
Date dtPrevista = p.getDataPrevistaTemp();
...
...
...
}
...
...
...
}
Segue abaixo o meu interceptador DaoInterceptor.java:
public class DaoInterceptor implements Interceptor {
@Out(key="br.com.faespsenar.sicp.dao.DaoFactory")
private DaoFactory daoFactory;
public void intercept(LogicFlow flow) throws LogicException, ViewException {
daoFactory = DaoFactory.getNewDaoFactory();
// só necessário para as lógicas "antigas" que usam @In
flow.getLogicRequest().getRequestContext().setAttribute("daoFactory", daoFactory);
LogicRequest logicRequest = flow.getLogicRequest();
try {
if (daoFactory == null || !daoFactory.isOpen())
logicRequest.getRequest().getRequestDispatcher("login.expirou.logic").forward(logicRequest.getRequest(), logicRequest.getResponse());
else
flow.execute();
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (daoFactory != null) {
if (daoFactory.hasTransaction()) {
daoFactory.rollback();
}
if (daoFactory.isOpen()) {
daoFactory.close();
}
}
daoFactory = null;
}
}
}
Esta aplicação funciona muito bem e esta bem performática, porém existem partes do projeto que não há a necessidade de saber TODAS as datas de realização do Projeto, mas isso ocorre por causa do FetchType.EAGER e é neste ponto que eu gostaria de obter uma maior performance com o uso do FetchType.LAZY.
Alguma orientação ou sugestão ?
Obrigado a todos…