Ajuda com Open Session In View

8 respostas
joncarv

Olá, pessoal!

Preciso tratar duas coleções em uma mesma sessão, então me recomendaram o uso do Open Session in View no Hibernate.
Estou um pouco confuso.

Aqui está meu filtro, referente ao Open Session in View, exatamente como está na documentação do Hibernate:

public class HibernateSessionRequestFilter implements Filter {

    private static Log log = LogFactory.getLog(HibernateSessionRequestFilter.class);
    private SessionFactory sf;

    public void init(FilterConfig filterConfig) throws ServletException {
        log.debug("Initializing filter...");
        log.debug("Obtaining SessionFactory from static HibernateUtil singleton");
        sf = HibernateUtil.getSessionFactory();

    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        try {
            log.debug("Starting a database transaction");
            sf.getCurrentSession().beginTransaction();

            // Call the next filter (continue request processing)
            chain.doFilter(request, response);

            // Commit and cleanup
            log.debug("Committing the database transaction");            
            sf.getCurrentSession().getTransaction().commit();

        } catch (StaleObjectStateException staleEx) {
            log.error("This interceptor does not implement optimistic concurrency control!");
            log.error("Your application will not work until you add compensation actions!");
            // Rollback, close everything, possibly compensate for any permanent changes
            // during the conversation, and finally restart business conversation. Maybe
            // give the user of the application a chance to merge some of his work with
            // fresh data... what you do here depends on your applications design.
            throw staleEx;
        } catch (Throwable ex) {
            // Rollback only
            ex.printStackTrace();
            try {
                if (sf.getCurrentSession().getTransaction().isActive()) {
                    log.debug("Trying to rollback database transaction after exception");
                    sf.getCurrentSession().getTransaction().rollback();
                }
            } catch (Throwable rbEx) {
                log.error("Could not rollback transaction after exception!", rbEx);
            }

            // Let others handle it... maybe another interceptor for exceptions?
            throw new ServletException(ex);
        }
    }

    public void destroy() {
        //throw new UnsupportedOperationException("Not supported yet.");
    }
}

E aqui está o meu HibernateUtil:

public class HibernateUtil {

    private static final SessionFactory sessionFactory = buildSessionFactory();

    private static SessionFactory buildSessionFactory() {
        try {           
            return new AnnotationConfiguration()
                .setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect")
                .setProperty("hibernate.connection.driver_class", "org.postgresql.Driver")
                .setProperty("hibernate.connection.url", "jdbc:postgresql://localhost:5432/coopconvenios")
                .setProperty("hibernate.connection.username", "postgres")
                .setProperty("hibernate.connection.password", "123456")
                .setProperty("hibernate.show_sql", "false")
                .setProperty("hibernate.format_sql", "true")
                .setProperty("hibernate.hbm2ddl.auto", "update")
                .setProperty("hibernate.current_session_context_class", "thread")
                .setProperty("hibernate.connection.provider_class", "org.hibernate.connection.C3P0ConnectionProvider")
                .setProperty("hibernate.c3p0.acquire_increment", "1")
                .setProperty("hibernate.c3p0.idle_test_period", "100")
                .setProperty("hibernate.c3p0.min_size", "5")
                .setProperty("hibernate.c3p0.max_size", "25")
                .setProperty("hibernate.c3p0.max_statements", "50")
                .setProperty("hibernate.c3p0.timeout", "1800")
                .addAnnotatedClass(Titular.class)
                .addAnnotatedClass(Dependente.class)
                .addAnnotatedClass(Orgao.class)
                .addAnnotatedClass(Instituicao.class)
                .addAnnotatedClass(Alocacao.class)
                .addAnnotatedClass(Endereco.class)
                .addAnnotatedClass(PreferenciaCobranca.class)
                .addAnnotatedClass(Conta.class)
                .addAnnotatedClass(Prestador.class)
                .addAnnotatedClass(Plano.class)
                .addAnnotatedClass(FaixaEtaria.class)
                .addAnnotatedClass(ContratoDePlano.class)
                .addAnnotatedClass(ContratoDeServico.class)
                .addAnnotatedClass(TipoContrato.class)
                .addAnnotatedClass(Movimento.class)
                .buildSessionFactory();
            
        }
        catch (Throwable ex) {
            // Make sure you log the exception, as it might be swallowed
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

}

O que não ficou claro pra mim é como utilizar o filtro. Estou um pouco confuso. Como associo o HibernateUtil com o filtro?
Espero que possam me ajudar!
Obrigado!

8 Respostas

leandronsp

Você mapeou o filtro no web.xml?

Dê uma olhada neste exemplo:
http://community.jboss.org/wiki/OpenSessioninView#A8

Abraço

joncarv

Sim, no web.xml está exatamente como no link que me mandou:

<filter>
        <filter-name>HibernateFilter</filter-name>
        <filter-class>br.com.credrionorte.util.HibernateSessionRequestFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>HibernateFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

O problema é que continua dando erro de “lazily initializing a collection…”. Devo manter minhas coleções mapeadas como Lazy?
Devo chamar o filtro em algum lugar?

leandronsp

Não. Creio que vc não precisa chamar o filtro de nenhum lugar. Acho que o hibernate tá estabelecendo a sessão apenas no request, sendo que poderia estar no escopo da sessão da aplicação.

Tenta mudar a seguinte linha:

.setProperty("hibernate.current_session_context_class", "thread")

Para:

.setProperty("hibernate.current_session_context_class", "managed")
joncarv

Bom, testei!
Se deixo como “thread”, tenho o erro: “failed to lazily initialize a collection of role…”.
Se troco para “managed”, como sugeriu, o erro passa a ser “org.hibernate.HibernateException: No session currently bound to execution context”.
:frowning:

leandronsp

Eh, nesse caso, como o hibernate passou a gerenciar o contexto da sessão, parece que seu filtro não está trabalhando de forma correta (agora, com essa alteração).

Na documentação tem uma parte que fala como usar o current_session managed. Vc tbm terá que fazer algumas modificações no seu filtro.

Vá na parte “What about the extended Session pattern for long Conversations?” da documentação:
http://community.jboss.org/wiki/OpenSessioninView

joncarv

Realmente, cara!
Eu não havia prestado atenção nessa segunda opção do tutorial!
Funcionou. Ele exibiu a coleção na camada de apresentação mesmo estando mapeada como Lazy!
Muito obrigado, amigo!

joncarv

Infelizmente, encontrei um erro!
Funcionou pra visualização da coleção, mas na mesma tela de visualização, os dados podem ser alterados.
Quando clico em algum campo pra alterar qualquer informação aparece um erro de NullPointerException na linha: currentSession.close();
Segue abaixo o código do filtro, referente ao Open Session in View:

public class HibernateSessionConversationFilter implements Filter {

    private static Log log = LogFactory.getLog(HibernateSessionConversationFilter.class);
    private SessionFactory sf;
    public static final String HIBERNATE_SESSION_KEY = "hibernateSession";
    public static final String END_OF_CONVERSATION_FLAG = "endOfConversation";

    public void doFilter(ServletRequest request,
            ServletResponse response,
            FilterChain chain)
            throws IOException, ServletException {

        org.hibernate.classic.Session currentSession;

        // Try to get a Hibernate Session from the HttpSession
        HttpSession httpSession =
                ((HttpServletRequest) request).getSession();
        Session disconnectedSession =
                (Session) httpSession.getAttribute(HIBERNATE_SESSION_KEY);

        try {

            // Start a new conversation or in the middle?
            if (disconnectedSession == null) {
                log.debug(">>> New conversation");
                currentSession = sf.openSession();
                currentSession.setFlushMode(FlushMode.NEVER);
            } else {
                log.debug("< Continuing conversation");
                currentSession = (org.hibernate.classic.Session) disconnectedSession;
            }

            log.debug("Binding the current Session");
            ManagedSessionContext.bind(currentSession);

            log.debug("Starting a database transaction");
            currentSession.beginTransaction();

            log.debug("Processing the event");
            chain.doFilter(request, response);

            log.debug("Unbinding Session after processing");
            currentSession = ManagedSessionContext.unbind(sf);

            // End or continue the long-running conversation?
            if (request.getAttribute(END_OF_CONVERSATION_FLAG) != null
                    || request.getParameter(END_OF_CONVERSATION_FLAG) != null) {

                log.debug("Flushing Session");
                currentSession.flush();

                log.debug("Committing the database transaction");
                currentSession.getTransaction().commit();

                log.debug("Closing the Session");
                currentSession.close();

                log.debug("Cleaning Session from HttpSession");
                httpSession.setAttribute(HIBERNATE_SESSION_KEY, null);

                log.debug("<<< End of conversation");

            } else {

                log.debug("Committing database transaction");
                currentSession.getTransaction().commit();

                log.debug("Storing Session in the HttpSession");
                httpSession.setAttribute(HIBERNATE_SESSION_KEY, currentSession);

                log.debug("> Returning to user in conversation");
            }

        } catch (StaleObjectStateException staleEx) {
            log.error("This interceptor does not implement optimistic concurrency control!");
            log.error("Your application will not work until you add compensation actions!");
            // Rollback, close everything, possibly compensate for any permanent changes
            // during the conversation, and finally restart business conversation. Maybe
            // give the user of the application a chance to merge some of his work with
            // fresh data... what you do here depends on your applications design.
            throw staleEx;
        } catch (Throwable ex) {
            // Rollback only
            try {
                if (sf.getCurrentSession().getTransaction().isActive()) {
                    log.debug("Trying to rollback database transaction after exception");
                    sf.getCurrentSession().getTransaction().rollback();
                }
            } catch (Throwable rbEx) {
                log.error("Could not rollback transaction after exception!", rbEx);
            } finally {
                log.error("Cleanup after exception!");

                // Cleanup
                log.debug("Unbinding Session after exception");
                currentSession = ManagedSessionContext.unbind(sf);

                log.debug("Closing Session after exception");
                currentSession.close(); //O ERRO ACONTECE NESTA LINHA

                log.debug("Removing Session from HttpSession");
                httpSession.setAttribute(HIBERNATE_SESSION_KEY, null);

            }

            // Let others handle it... maybe another interceptor for exceptions?
            throw new ServletException(ex);
        }

    }

    public void init(FilterConfig filterConfig) throws ServletException {
        log.debug("Initializing filter...");
        log.debug("Obtaining SessionFactory from static HibernateUtil singleton");
        sf = HibernateUtil.getSessionFactory();
    }

    public void destroy() {
    }
}

no HibernateUtil está configurado assim:

.setProperty("hibernate.current_session_context_class", "managed")

Alguém sabe dizer o que pode estar causando o erro?

leandronsp

O último nível do stack foi nessa linha?
Parece o seguinte, que o currentSession tá nulo. E em uma linha antes, na

currentSession = ManagedSessionContext.unbind(sf);

provavelmente o ManagedSessionContext.unbind(sf) devolveu um retorno nulo. Talvez o próprio “sf” chegou nulo, é oq me parece, e por isso perguntei se aquela linha foi a ultima na pilha do stack. Tem como postar seu stack ae?

Como ficaram as mensagens nos logs?

Criado 3 de abril de 2010
Ultima resposta 3 de abr. de 2010
Respostas 8
Participantes 2