如何维护每个请求打开的Hibernate会话?

时间:2015-09-03 05:27:30

标签: java hibernate session java-ee servlets

据我所知,每个请求的会话意味着每个servlet请求只能使用一个hibernate会话,而不是每个事务一个会话。

考虑这个助手类:

public class HibernateUtil {
    private static SessionFactory sessionFactory;

    // this is called by the servlet context listener
    public static void buildSessionFactory() {
        // build session factory
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    // check if the entity user already exist in the database
    public static User getUser(String email) {
        Session session = getSessionFactory().getCurrentSession();
        session.beginTransaction();
        User user = (User)session.createQuery("from User c where c.emailAddress like :email").setParameter("email", email).uniqueResult();
        session.getTransaction().commit();
        return user;
    }

    // if getUser returns null, insert this user in the database
    public static void insertUser(User user) {
        Session session = getSessionFactory().getCurrentSession();
        session.beginTransaction();
        session.save(user);
        session.getTransaction().commit();
    }

}

现在每个方法都有自己的会话对象,但是当调用寄存器servlet时我必须这样做:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    User user = // initialise user
        if (getUser(request.getParameter("email")) == null) { // session
            insertUser(user); // another session
            request.setAttribute("user", user);
            request.getRequestDispatcher("homepage.jsp").forward(request, response);
        } else {
            request.setAttribute("message", "This account already exist");
        populateFields(request);
            request.getRequestDispatcher("register.jsp").forward(request, response);
        }
}

现在,我的寄存器servlet有效地使用了两个会话,对模式进行了改进。我已经想到了可能的解决方法:

  • 在hibernateUtil类中设置全局Session对象,然后阻止getUser提交事务。我认为这意味着每个方法现在都可以使用此会话,即使在未提交事务的多个请求中也是如此。我的问题是我唯一的操作是获取用户而已?
  • 让getUser返回会话并将其传递给insertUser,我不知道,我不确定传递会话对象是否合适。
  • 只需删除帮助程序类并直接在servlet中编写代码并删除帮助程序类。我真的认为它会简化会话的管理,但我可能无法重用某些事务。

我该怎么办?是否有更好的代码可以做我想做的事情?

2 个答案:

答案 0 :(得分:0)

在这种情况下,最好的选择是设计如下:

  • 有一个抽象的Dao,它将Session session作为字段,并允许通过构造函数或setter(由您自己)注入。您的所有Daos都将成为此类的子类。
  • 服务类可以在其方法中创建Session session或作为参数接收它。通过这种方式,服务可以将session注入其方法中使用的必要daos。这也使服务能够启动和提交/回滚事务。
  • 如果您希望/需要服务应该不知道打开/关闭hibernate会话,那么您可以为将创建会话,将其注入服务,然后执行正确的方法的接口创建一个包装器。服务)。执行该方法后,此包装器将提交/回滚在会话中完成的操作。

如果您使用像Spring这样的框架来提供与Hibernate的集成,您会发现它们具有类似的设计:

  • Dao类必须允许通过构造函数参数或setter注入HibernateTemplate
  • 服务将根据需要使用dao接口。
  • 使用<transactional>@Transactional将创建一个代理类,它包装您的服务以打开会话,执行正确的服务方法,并提交/回滚操作,具体取决于在那里完成了交易管理。

答案 1 :(得分:0)

您可以实现创建会话的filter,然后将其存储在请求中,这样您就可以在有权访问请求对象的任何地方重用它:

public class SessionPerRequestFilter implements Filter {
    public static final String SESSION_ATTRIBUTE_NAME = "myapp.hibernate.session";

    private static SessionFactory sessionFactory;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // Locate the session on the request object
        Session session = (Session) request.getAttribute(SESSION_ATTRIBUTE_NAME);
        if(session == null) {
            // Create a new session
            session = HibernateUtil.getSessionFactory().openSession();
            request.setAttribute(SESSION_ATTRIBUTE_NAME, session);
        }

        try {
            // Continue the filter chain
            chain.doFilter(request, response);
        }
        finally {
            // Close the session
            session.close();
        }
    }

    @Override
    public void destroy() {
    }
}

在您的servlet中,您可以通过

获取会话
Session session = (Session) request.getAttribute(SessionPerRequestFilter.SESSION_ATTRIBUTE_NAME);

确保您只关闭您的交易而不关闭您的帮助程序类中的会话。