open session in view

摘自:http://community.jboss.org/wiki/OpenSessioninView

 

1. 问题描述

A common issue in a typical (web-)application is the rendering of the view, after the main logic of the action has been completed, and therefore, the Hibernate Session has already been closed and the database transaction has ended. If you access detached objects that have been loaded in the Session inside your JSP (or any other view rendering mechanism), you might hit an unloaded collection or a proxy that isn't initialized. The exception you get is: LazyInitializationException: Session has been closed (or a very similar message).

比如页面是学生信息显示,学生实体对象包含一个课程实体列表,并且采用惰性装载。那么页面显示学生的课程信息时可能Session已关闭,事务已提交,hibernate会抛出上面的异常。

 

基本上问题出现在一次request 与 response 之间layz loading的对象上。open session in view解决方案也是应用在相应的scope内。如果是用JPA,那么对应Session的是EntityManager,相应的是open entity manager in view.

如果渲染发生在servlet中,在servlet中打开和关闭Session(Session per Request"), 这样是没问题的。但如果渲染是用JSP,那么就有这里所说的问题,这时就需要Open Session in View的filter方式。Filter后处理发生在 “渲染JSP模板生成Html” 后, 这时候才关闭Session或者EntityManager

 

2. 解决方案

2.1

为detached objects 重新开一个session

 

2.2 open session in view

in most applications you need the following: when an HTTP request has to be handled, a new Session and database transaction will begin. Right before the response is send to the client, and after all the work has been done, the transaction will be committed, and the Session will be closed. A good standard interceptor in a servlet container is a ServletFilter .

 

 

 

public class HibernateSessionRequestFilter implements Filter {
 
    private static Log log = LogFactory.getLog(HibernateSessionRequestFilter.class);
 
    private SessionFactory sf;
 
    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 init(FilterConfig filterConfig) throws ServletException {
        log.debug("Initializing filter...");
        log.debug("Obtaining SessionFactory from static HibernateUtil singleton");
        sf = HibernateUtil.getSessionFactory();
    }
 
    public void destroy() {}
 
}

 

    <filter>
        <filter-name>HibernateFilter</filter-name>
        <filter-class>my.package.HibernateSessionRequestFilter</filter-class>
    </filter>

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

 

public class ItemDAO {
 
    Session currentSession;
 
    public ItemDAO() {
        currentSession = HibernateUtil.getSessionFactory().getCurrentSession();
    }
 
    public Item getItemById(Long itemId) {
        return (Item) currentSession.load(Item.class, itemId);
    }
}

 

public String execute(HttpRequest request) {
 
    Long itemId = request.getParameter(ITEM_ID);
 
    ItemDAO dao = new ItemDAO();
 
    request.setAttribute( RESULT, dao.getItemById(itemId) );
  
    return "success";
}

 

 

3. filter for long conversation 长事务

 

If you'd like to use a single Session for several database transactions, you either have to implement it completely yourself, or you have to use the built-in "application managed" strategy:

 

 

 

To enable the application-managed "current" Session strategy, set your hibernate.current_session_context_class configuration property to org.hibernate.context.ManagedSessionContext (or simply "managed" in Hibernate 3.2). You can now bind and unbind the "current" Session with static methods, and control the FlushMode and flushing manually.

 

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();
 
                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() {}
 
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值