Hibernate OpenSessionInViewFilter过早关闭会话?

时间:2012-07-27 19:43:41

标签: java spring hibernate orm wicket

我有一个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;
}

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

1 个答案:

答案 0 :(得分:1)

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

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

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/