Java 如何维护每个请求打开的Hibernate会话?
据我所知,每个请求使用一个会话意味着每个servlet请求只使用一个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() {
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会话,那么您可以为将创建会话的接口创建包装器,将其注入服务,然后执行服务的正确方法。执行该方法后,该包装器将提交/回滚会话中完成的操作
- 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。你怎么认为?