webapp使用Spring的OpenSessionInViewInterceptor
为每个请求打开一个Hibernate会话,在请求处理结束时关闭它。这非常有效。但是,某些请求(如文件下载)可能会持续很长时间,从而保持会话打开并且数据库连接受阻。在我们的例子中,将文件数据复制到响应输出流是控制器做的最后一件事,并且不需要Hibernate会话(从服务器文件系统读取文件数据)。这些文件下载请求可以快速占用数据库连接池中的大多数连接。
我已经给控制器一个SessionFactory
的引用,并在将文件数据复制到响应输出流之前调用以下方法:
SessionFactoryUtils.closeSession(SessionFactoryUtils.getSession(sessionFactory, false));
OpenSessionInViewInterceptor
稍后在请求过程中执行相同的操作,并且关闭会话似乎是幂等的(即多次调用此方法与调用它一次具有相同的效果)。换句话说,对这一变化的本地测试进展顺利。但它似乎有点危险;如果OpenSessionInViewInterceptor
或SessionFactoryUtils
在更高版本的Spring中发生变化,这种方法可能会以微妙的方式破坏(可能会泄漏连接?)。
最终目标是在开始发送文件数据之前关闭数据库连接,这是实现这一目标的一种方法。另一种解决方案是控制器将输入流作为属性放入请求中,并使用过滤器(在OpenSessionInViewInterceptor
之后触发)将输入流的数据复制到响应中。这有一系列挑战(例如,相关代码现在在控制器和过滤器之间分开,而不是集中在控制器中。)
有没有更好的方法来解决这个问题?
答案 0 :(得分:2)
一种选择是不将OSiV用于返回文件的控制器/操作。另一个是创建自己的版本,你更有信心(毕竟,它并不是非常复杂)。
如果interceptor / utils发生了变化,我怀疑你是否泄漏连接。在拦截器完成它之后,我对于返回文件的想法并不感到兴奋;这意味着后续拦截器(或过滤器,虽然IMO在链式拦截器中执行它可能更干净)可以获得一些悬空状态,但它让我觉得笨重且容易出错。