Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/hibernate/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何维护每个请求打开的Hibernate会话?_Java_Hibernate_Session_Jakarta Ee_Servlets - Fatal编程技术网

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

Java 如何维护每个请求打开的Hibernate会话?,java,hibernate,session,jakarta-ee,servlets,Java,Hibernate,Session,Jakarta Ee,Servlets,据我所知,每个请求使用一个会话意味着每个servlet请求只使用一个hibernate会话,而不是每个事务使用一个会话 考虑这个助手类: public class HibernateUtil { private static SessionFactory sessionFactory; // this is called by the servlet context listener public static void buildSessionFactory() {

据我所知,每个请求使用一个会话意味着每个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();
    }

}
现在每个方法都有自己的会话对象,但是当调用register 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);
        }
}
现在,我的register servlet有效地使用了两个会话,破坏了模式。我已经想到了可能的解决方法:

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

我该怎么办?还有更好的代码可以满足我的要求吗?

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

  • 有一个抽象Dao,它将有一个
    会话
    as字段,并允许通过构造函数或setter(由您决定)注入该字段。所有DAO都将是这个类的子类
  • 服务类可以在其方法中创建
    会话
    ,也可以将其作为参数接收。这样,服务可以将
    会话
    注入其方法中使用的必要DAO。这还允许服务启动和提交/回滚事务
  • 如果您希望/需要服务不知道打开/关闭hibernate会话,那么您可以为将创建会话的接口创建包装器,将其注入服务,然后执行服务的正确方法。执行该方法后,该包装器将提交/回滚会话中完成的操作
如果您使用类似Spring的框架来提供与Hibernate的集成,您将看到它们具有类似的设计:

  • Dao类必须允许通过构造函数参数或setter注入
    HibernateTemplate
  • 服务将根据需要使用dao接口
  • 使用
    @Transactional
    将创建一个代理类,该类包装您的服务以打开会话,执行服务的正确方法,并提交/回滚操作,具体取决于在此完成的事务管理配置
您可以实现创建会话并将其存储在请求中的,这样您就可以在任何可以访问请求对象的地方重用它:

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() {
    }
}
在servlets中,您可以通过

Session session = (Session) request.getAttribute(SessionPerRequestFilter.SESSION_ATTRIBUTE_NAME);

确保只关闭事务,而不关闭助手类中的会话。

我不允许这种想法,因为有些请求不需要使用数据库操作。此外,这打破了MVC模式。这是如何打破MVC的?Hibernate会话与数据库管理相关,这在模型中进行。servlet和过滤器保留在控制器中,并应向模型发送命令,以在尽可能窄的可用范围内更新数据。使用filter选项可以工作,但存在一些问题:1)hibernate会话应该存储在web请求范围内,而不是web会话范围内;2)如果我需要执行两个操作并分别提交每个操作的结果,这无法完成,因为服务将共享同一个会话,如果其中一个失败,则不会执行任何操作3)如果我只需要启动文件下载,不需要访问数据库,那么这种方法将打开/关闭从不需要的资源used@Raniz我还想知道,我是否需要为此编写一个助手类?或者直接在servlet中编写代码并删除helper类。我真的认为它会简化会话的管理。我还想知道,我是否需要为此编写一个助手类?或者直接在servlet中编写代码并删除helper类。我真的认为它会简化会话的管理。你为它创建一个助手类是什么意思?我看不出助手类如何适应这种方法,除了您当前的实用程序类,它创建了
SessionFactory
,并允许您创建
Session
。当前我的助手类还包含我的所有事务方法。如果我只是从helper中删除它们并直接在servlet中编写它们会更好吗?servlet应该不知道会话。服务/dao应该知道会话。我在这里告诉您的是,您应该将会话管理集中在一个地方。在我看来,实用程序类应该只提供获取会话的方法。如果我不使用getCurrentSession在提交后自动关闭会话,而只调用openSession呢?这样我就可以在请求结束时调用session.close。你怎么认为?