31

我有一个看起来像这样的错误:

无法初始化代理 - 没有会话

我正在使用 java、hibernate 和 spring。尝试生成 PDF 文档时出现此错误,我正在按照后续步骤动态生成它并存储在数据库中。

  1. 我通过 POST 方法向应用程序发送了一个请求。这会即时生成 PDF 并向用户显示。

  2. 就在该请求之后,我发送了另一个请求,但通过 ajax 请求。这将生成相同的 PDF,但会将其保存在数据库中。

该错误表明由于“无法初始化代理 - 无会话”错误而无法执行查询。

我做错了什么,从同一个用户会话中调用了两次相同的方法吗?会不会是会话在两个请求完成之前就关闭了?

希望有人可以帮助我了解正在发生的事情。

4

1 回答 1

76

您的问题是休眠会话仅适用于一个请求。它在请求开始时打开并在结束时关闭。你猜对了:Hibernate 会话在两个请求完成之前就关闭了。

究竟发生了什么?您的实体对象在两个请求期间都存在。如何?它们存储在 HTTP 会话中(这是一个不同的东西,称为会话)你没有提供关于你正在使用的框架的太多信息,所以我不能给你更多的细节,但可以肯定的是你正在使用的框架以某种方式将您的实体保留在 HTTP 会话中。这就是框架使您可以轻松地为多个请求使用相同对象的方式。

当第二个请求的处理开始时,代码试图访问由 hibernate 延迟初始化的某个实体(通常是集合的元素)。该实体未附加到休眠会话,因此休眠无法在读取休眠代理之前对其进行初始化。您应该在 ajax 请求处理开始时打开一个会话并将您的实体重新附加到它。

编辑:

我将尝试简要解释幕后发生的事情。所有 java web 框架都有一个或多个处理请求的 servlet。servlet 通过创建一个最终将产生响应 (HttpResponse) 的新线程来处理每个请求 (HttpRequest)。处理每个请求的方法在这个线程内执行。

在请求处理开始时,您的应用程序应分配处理所需的资源(事务、休眠会话等)。在处理周期结束时,这些资源被释放(事务被提交,休眠会话被关闭,JDBC 连接被释放等)。这些资源的生命周期可以由您的框架管理,也可以由您的代码完成。

为了支持 HTTP 等无状态协议中的应用程序状态,我们有 HttpSession 对象。我们(或框架)将在同一客户端的不同请求周期之间保持相关的信息放在 HttpSession 上。

在处理第一个请求期间,hibernate 从数据库中(懒惰地)读取一个实体。由于延迟初始化,该对象结构的某些部分是休眠代理对象。这些对象与创建它们的休眠会话相关联。

当您尝试处理第二个请求时,框架会在 HttpSession 对象中从上一个请求中找到实体。然后它试图从一个被延迟初始化的子实体访问一个属性,现在它是一个休眠代理对象。休眠代理对象是对真实对象的模仿,当有人试图访问其属性之一时,它将要求其休眠会话用数据库中的信息填充它。这就是您的休眠代理正在尝试做的事情。但是它的会话在之前的请求处理结束时关闭了,所以现在它没有一个休眠会话可以用来补充水分(充满真实信息)。

请注意,您可能已经在第二个请求开始时打开了休眠会话,但它不知道包含代理对象的实体,因为该实体已由不同的休眠会话读取。您应该将实体重新附加到新的休眠会话。

关于如何重新附加分离的实体有很多讨论,但目前最简单的方法是session.update(entity).

希望能帮助到你。

于 2013-05-25T19:12:41.100 回答