Hibernate OpenSessionInViewFilter 过早关闭会话?

Posted

技术标签:

【中文标题】Hibernate OpenSessionInViewFilter 过早关闭会话?【英文标题】:Hibernate OpenSessionInViewFilter Prematurely Closing Sessions? 【发布时间】:2012-07-27 19:43:41 【问题描述】:

我有一个 Spring、Hibernate 和 Wicket 应用程序设置为从数据库中读取国际化的 json 内容项,并通过 api url 上的请求将它们传递出去。负责传递数据的代码库是为企业客户开发的整体网站结构的一小部分。

在超过 90% 的情况下,API 都可以正常运行,但客户端偶尔会遇到一个有趣的问题,该问题可能源于孤立的休眠会话。请求将通过 php 脚本失败并给出错误:

Warning: file_get_contents( http://client.net/api/attachment_lines?ce=false&language=en&region=na&ts=1341592326) [function.file-get-contents]: failed to open stream: Redirection limit reached, aborting in client_api->send_request() (line 38 of <sitepath>/api.class.php).

并且将在 tomcat 服务器日志中产生以下错误:

09:15:00,200 ERROR [RequestCycle] failed to lazily initialize a collection of role: com.client.data.AttachmentLineCode.attachmentSublineCodes, no session or session was closed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.client.data.AttachmentLineCode.attachmentSublineCodes, no session or session was closed

应用程序在 spring 中配置为使用 OpenSessionInViewFilter 和 @Transactional 注释设计模式,所以我不确定是什么导致了间歇性请求失败。除此之外,客户端表示该 api 将在问题发生后大约 15 分钟内继续失败,考虑到配置,这似乎真的很古怪。在 web.xml 中,这里是过滤器的声明:

<filter>
    <filter-name>openEntityManagerInView</filter-name>
    <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>openEntityManagerInView</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

在代码中,这里是通用 DAO 上的事务注释,由内容项 DAO 扩展:

@Transactional(noRollbackFor=javax.persistence.EntityNotFoundException.class, org.springframework.orm.ObjectRetrievalFailureException.class)
public class GenericDaoHibernate<T, PK extends Serializable> implements GenericDao<T, PK> 
    @Autowired
    private SessionFactory sessionFactory;

在通用 DAO 中,这里也是我检索和使用会话的地方:

protected Session getSession() 
    return sessionFactory.getCurrentSession();


protected Criteria createCacheableCriteria(Class<T> clazz) 
    Criteria criteria = createNonCacheableCriteria(clazz);
    criteria.setCacheable(true);
    criteria.setCacheMode(CacheMode.NORMAL);
    return criteria;


protected Criteria createCacheableCriteria(Class<?> clazz, String alias) 
    Criteria criteria = createNonCacheableCriteria(clazz, alias);
    criteria.setCacheable(true);
    criteria.setCacheMode(CacheMode.NORMAL);
    return criteria;


protected Criteria createNonCacheableCriteria(Class<?> clazz) 
    Session session = getSession();
    Criteria criteria = session.createCriteria(clazz);
    criteria.setCacheable(false);
    criteria.setCacheMode(CacheMode.IGNORE);
    return criteria;


protected Criteria createNonCacheableCriteria(Class<?> clazz, String alias) 
    Session session = getSession();
    Criteria criteria = session.createCriteria(clazz, alias);
    criteria.setCacheable(false);
    criteria.setCacheMode(CacheMode.IGNORE);
    return criteria;

是否有某种方式可以使会话在此设置中成为孤儿?休眠会话是否有某种内置超时可能导致此问题?缓存可能有问题?提前感谢您的帮助。

【问题讨论】:

您如何使用SessionFactory?通过HibernateTemplate?你能给我们一份它的代码草案吗? 添加了与我使用 SessionFactory 相关的附加代码。感谢您的回复! 【参考方案1】:

这里的解决方案与 Hibernate 或 Spring 无关,仅存在于我的错误中,没有注意到生产环境和我们的开发/登台之间的差异。生产环境实施了复杂的负载平衡策略,没有粘性会话。

事实证明,Wicket 的请求/响应周期涉及在 POST 之后缓存缓冲的响应。返回获取该响应的相应 GET 偶尔会抛出 302,因为负载平衡会将请求转发到没有缓存响应的服务器,并且代理对象将被遗忘。我为解决此问题而选择实现的相关代码位于我的 Application.java 中的 init() 下:

public class ClientApplication extends SpringWebApplication   
    ... 
    public void init() 
        ...
        getRequestCycleSettings().setRenderStrategy(IRequestCycleSettings.ONE_PASS_RENDER);

这会将 Wicket 的渲染策略配置更改为不缓冲响应。结果出现了一个问题,它允许经典的“刷新双重提交”问题。因此,这不一定是理想的解决方案,但客户端不想使用启用粘性会话的负载平衡,也不介意出现双重提交问题。

有关此问题的更多详细信息以及更雄辩/结构化的答案,请参阅:http://blog.comsysto.com/2011/04/08/lost-in-redirection-with-apache-wicket/

【讨论】:

以上是关于Hibernate OpenSessionInViewFilter 过早关闭会话?的主要内容,如果未能解决你的问题,请参考以下文章

Spring和Hibernate的注解整合 hibernate3和hibernate4/5的区别

hibernate.merge()方法怎么用

hibernate 异常 怎么解决

Hibernate之Hibernate环境配置

(转)Hibernate框架基础——Hibernate API及Hibernate主配置文件

Hibernate基础学习—Hibernate相关API介绍