[Resolvido] LazyInitializationException em session.load usando Interceptor para o DAO

4 respostas
jurado

Suspeito fortemente que seja algum mal entendido sobre a necessidade de beginTransaction e commit por minha parte, entao acho que vocês podem me ajudar a esclarecer:

Antes, eu nao usava interceptor para garantir que minha session do hibernate fosse aberta (beginTransaction) e fechada (commit/rollback). Meu código estava espalhado de chamadas a estes métodos. Embora feio, tudo funcionava corretamente.

Optei entao por usar o Interceptor. Porém, toda vez que chamo meu formulario de EDICAO (que recebe o id da minha entidade e executa um session.load para obter seus dados), estou recebendo um delicioso LazyInitializationException do hibernate. Nas outras chamadas como list, save, etc nao tenho nenhum problema. Estaria eu fazendo alguma bobagem? Devo considerar no Interceptor que nao é necessário um beginTransaction/commit/rollback neste caso?

Aqui vao minhas classes de exemplo:

Entidade

@Entity
public class Grupo {
	@Id
	@GeneratedValue
	private Long idGrupo;
	private String codigo;
	private String descricao;
	private Date criacao;
	private Date modificacao;

	public String getCodigo() {
		return codigo;
	}

	public Date getCriacao() {
		return criacao;
	}

	public String getDescricao() {
		return descricao;
	}

	public Long getIdGrupo() {
		return idGrupo;
	}

	public Date getModificacao() {
		return modificacao;
	}

	public void setCodigo(String codigo) {
		this.codigo = codigo;
	}

	public void setCriacao(Date criacao) {
		this.criacao = criacao;
	}

	public void setDescricao(String descricao) {
		this.descricao = descricao;
	}

	public void setIdGrupo(Long idGrupo) {
		this.idGrupo = idGrupo;
	}

	public void setModificacao(Date modificacao) {
		this.modificacao = modificacao;
	}
}

DaoFactory

@Resource
public class DaoFactory {
	private final Session session;
	private Transaction transaction;

	public DaoFactory() {
		session = HibernateUtil.getSession();
	}

	public void beginTransaction() {
		this.transaction = this.session.beginTransaction();
	}

	public void close() {
		this.session.close();
	}

	public void commit() {
		this.transaction.commit();
		this.transaction = null;
	}

	public Dao<Grupo> getGrupoDao() {
		return new Dao<Grupo>(this.session, Grupo.class);
	}

	public boolean hasTransaction() {
		return this.transaction != null;
	}

	public void rollback() {
		this.transaction.rollback();
		this.transaction = null;
	}

	//...
}

Controller

@Resource
public class GrupoController {
	private final DaoFactory daoFactory;
	private final Result result;
	private Validator validator;

	public GrupoController(DaoFactory daoFactory, Result result,
			Validator validator) {
		super();
		this.daoFactory = daoFactory;
		this.result = result;
		this.validator = validator;
	}

	public Collection<Grupo> list() {
		return this.daoFactory.getGrupoDao().listaTudo();
	}

	public void delete(Grupo grupo) {
		this.daoFactory.getGrupoDao().remove(grupo);
		result.use(Results.logic()).redirectTo(GrupoController.class).list();
	}

	public void edit(Grupo grupo) {
		if (grupo.getCodigo().isEmpty()) {
			validator.add(new ValidationMessage("Código não pode ser vazio!",
					"codigoInvalido"));
		}
		validator.onErrorUse(Results.page()).of(GrupoController.class)
				.formularioGrupo("add", grupo);
		this.daoFactory.getGrupoDao().atualiza(grupo);
		result.use(Results.logic()).redirectTo(GrupoController.class).list();
	}

	public void editar(Grupo grupo) {
		grupo = this.daoFactory.getGrupoDao().procura(grupo.getIdGrupo());
		result.use(Results.logic()).redirectTo(GrupoController.class)
				.formularioGrupo("edit", grupo);
	}

	public void adicionar() {
		result.use(Results.logic()).redirectTo(GrupoController.class)
				.formularioGrupo("add", null);
	}

	public void add(final Grupo grupo) {
		if (grupo.getCodigo().isEmpty()) {
			validator.add(new ValidationMessage("Código não pode ser vazio!",
					"codigoInvalido"));
		}
		validator.onErrorUse(Results.page()).of(GrupoController.class)
				.formularioGrupo("add", grupo);

		this.daoFactory.getGrupoDao().adiciona(grupo);
		result.use(Results.logic()).redirectTo(GrupoController.class).list();
	}

	public void formularioGrupo(String action, Grupo grupo) {
		result.include("formAction", action);
		result.include("grupo", grupo);
	}
}

Interceptor

@RequestScoped
@Intercepts
public class DaoFactoryInterceptor implements Interceptor {
	private final DaoFactory daoFactory;

	public DaoFactoryInterceptor(DaoFactory daoFactory) {
		super();
		this.daoFactory = daoFactory;
	}

	@Override
	public boolean accepts(ResourceMethod arg0) {
		return true;
	}

	@Override
	public void intercept(InterceptorStack stack, ResourceMethod method,
			Object instance) throws InterceptionException {
		System.out.println("################### Interceptando DAO para "
				+ method.getResource().getType());
		try {
			daoFactory.beginTransaction();
			stack.next(method, instance);
			daoFactory.commit();
		} finally {
			if (daoFactory.hasTransaction()) {
				daoFactory.rollback();
			}
			daoFactory.close();
		}
	}
}

O problema somente ocorre quando o controller vai renderizar o form editar(). Este é meu Dao, que chama o session.load:

public class Dao<T> {
	private final Session session;

	@SuppressWarnings("unchecked")
	private final Class classe;

	@SuppressWarnings("unchecked")
	Dao(Session session, Class classe) {
		this.session = session;
		this.classe = classe;
	}

	@SuppressWarnings("unchecked")
	public T procura(Long id) {
		return (T) this.session.load(this.classe, id);
	}

	// ...
}

Meu gerador de sessions.

public class HibernateUtil {
	private static SessionFactory factory;
	static {
		Configuration conf = new AnnotationConfiguration();
		conf.configure();
		factory = conf.buildSessionFactory();
	}

	public static Session getSession() {
		return factory.openSession();
	}
}

e esta é a exception

17/09/2009 23:13:23 org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet default threw exception
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
	at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:86)
	at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:140)
	at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
	at net.danieljurado.discadorweb.modelo.Rota_$$_javassist_22.toString(Rota_$$_javassist_22.java)
	at java.lang.String.valueOf(Unknown Source)
	at java.lang.StringBuilder.append(Unknown Source)
	at java.util.AbstractCollection.toString(Unknown Source)
	at java.lang.String.valueOf(Unknown Source)
	at java.lang.StringBuilder.append(Unknown Source)
	at br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.intercept(ParametersInstantiatorInterceptor.java:89)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:57)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:71)
	at br.com.caelum.vraptor.interceptor.InstantiateInterceptor.intercept(InstantiateInterceptor.java:54)
	at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:51)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:71)
	at br.com.caelum.vraptor.interceptor.multipart.MultipartInterceptor.intercept(MultipartInterceptor.java:58)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:57)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:71)
	at net.danieljurado.discadorweb.interceptors.DaoFactoryInterceptor.intercept(DaoFactoryInterceptor.java:33)
	at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:51)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:71)
	at br.com.caelum.vraptor.interceptor.InterceptorListPriorToExecutionExtractor.intercept(InterceptorListPriorToExecutionExtractor.java:58)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:57)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:71)
	at br.com.caelum.vraptor.core.URLParameterExtractorInterceptor.intercept(URLParameterExtractorInterceptor.java:45)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:57)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:71)
	at br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:70)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:57)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:71)
	at br.com.caelum.vraptor.core.DefaultRequestExecution.execute(DefaultRequestExecution.java:71)
	at br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:99)
	at br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:37)
	at br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:97)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849)
	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454)
	at java.lang.Thread.run(Unknown Source)

Segundo dizia a documentação, a idéia era ‘fugir’ dos LazyInitializationException, acabei dando de cara com eles :slight_smile:

4 Respostas

Lucas_Cavalcanti

Isso acontece por causa do jeito que o VRaptor faz o redirect…

ele coloca o MESMO objeto que você passou como parâmetro do redirect…

o problema é que você carregou o grupo no método editar, mandou um redirect, que fecha a sessão do hibernate e cria outra requisição,
o problema é que o hibernate não faz o select no banco qdo vc faz o load, ele só faz isso qdo vc usa o objeto…

troca o redirectTo por forwardTo (nesse caso faz sentido…)

public void editar(Grupo grupo) {

grupo = this.daoFactory.getGrupoDao().procura(grupo.getIdGrupo());

result.use(Results.logic()).[b]forwardTo<a>/b</a>

.formularioGrupo(edit, grupo);

}
jurado

Só pra eu entender, fazendo forward nao é criada uma nova request, por isso a session permanece aberta, afinal o Interceptor é RequestScoped, é isso?

Sou bem analfabeto no assunto, por isso a dúvida.

Esta forma de resolver o problema é a correta (de acordo com a arquitetura das requisicoes) ou é um WorkAround?

Pergunto isso pra tentar entender melhor como funcionam as requests :slight_smile:

Lucas_Cavalcanti

forward é na mesma requisição, redirect abre outra requisição…
forward fica com a mesma url no browser(/…/editar), redirect troca a url no browser (…/formularioGrupo)

essa é a solução que eu daria pra isso mesmo… mas o fato de não funcionar com o request é que
o vraptor usa o mesmo objeto grupo pras 2 requisições (pq vc passou o grupo por parâmetro), mas a
sessão que carregou o grupo é fechada na 1a requisição, e como vc não fez nada com o grupo, ele nem
chegou a carregar o grupo do banco de dados de verdade na 1a requisição…

daí qdo vc tenta acessar o grupo na 2a requisição ele tenta carregar o grupo de verdade, mas a sessão já
foi fechada na 1a, por isso o LazyInitializationException…

WorkAround seria chamar um getter qqer do grupo no editar só pra carregar ele no banco =) também funciona :wink:

jurado

Certo! Funcionou direitinho!

Obrigado pela aula, e pela paciencia!

Criado 17 de setembro de 2009
Ultima resposta 18 de set. de 2009
Respostas 4
Participantes 2