【问题标题】:Session management using Hibernate in a *multi-threaded* Swing application在 * 多线程 * Swing 应用程序中使用 Hibernate 进行会话管理
【发布时间】:2010-12-12 11:24:42
【问题描述】:

我目前正在处理我的一个(相当大的)宠物项目,一个 Swing 应用程序,它本质上需要是多线程的。几乎所有的用户交互都可能通过互联网从一些远程服务器获取数据,因为我既不控制这些服务器也不控制互联网本身,因此长响应时间是不可避免的。 Swing UI 显然无法在 EDT 忙碌时重新绘制自身,因此所有远程服务器调用都需要由后台线程执行。

我的问题:

后台线程获取的数据通过来自本地(内存中)数据库的数据“丰富”(远程服务器返回本地数据库中数据的 ID/引用)。这些数据后来最终被传递到 EDT,在那里它成为视图模型的一部分。某些实体此时尚未完全初始化(启用延迟获取),因此用户可能会触发延迟获取,例如在 JTable 中滚动。由于休眠会话已经关闭,这将触发 LazyInitializationException。我不知道用户何时会触发延迟获取,因此按需创建会话/附加分离的对象在这里不起作用。

我通过以下方式“解决”了这个问题:

  • 为整个应用程序使用单个(同步,因为 Session 实例不是线程安全的)Session
  • 完全禁用延迟获取

虽然这可行,但应用程序的性能受到很大影响(有时几乎无法使用)。速度变慢主要是由于每个查询现在要获取大量对象。

我目前正在考虑将应用程序的设计更改为“每线程会话”并将非 EDT 线程获取的所有实体迁移到 EDT 线程的 Session(类似于 this posting on the Hibernate forums)。

旁注:任何与数据库更新相关的问题都不适用,因为所有数据库实体都是只读的(参考数据)。

关于在这种情况下如何使用 Hibernate with 延迟加载的任何其他想法?

【问题讨论】:

    标签: java multithreading hibernate swing


    【解决方案1】:

    你可以看看Ebean ORM。它是无会话和延迟加载的。这并没有回答您的问题,但确实提出了替代方案。

    我知道 Ebean 已经内置了对异步查询执行的支持,这可能对您的场景也很有趣。

    也许值得一看。

    • 罗伯。

    【讨论】:

    • 您好 Rob,感谢您的链接 - 我假设您是首席开发人员? ;-) 无论如何,听起来很有希望......我想我会在有空的时候尝试一下。
    【解决方案2】:

    有两个不同的问题,应该分开解决:

    1. 在 Swing 应用程序中处理休眠会话。让我推荐我自己的文章,关于这个问题:http://blog.schauderhaft.de/2008/09/28/hibernate-sessions-in-two-tier-rich-client-applications/

    基本思想是每个帧都有一个会话,不包括使用生成帧会话的模态帧。这并不容易,但它确实有效。这意味着,您将不再获得任何 LLE。

    1. 如何让您的 GUI 线程与后端分离。

    我建议将休眠对象严格保留在它们源自的后端线程上。仅将包装器对象提供给 ETD。如果要求这些包装器对象提供值,它们会创建一个请求,该请求将传递给后端线程,最终将返回该值。

    我设想了三种包装器实现:

    异步:请求该值,并在该值可用时得到通知。它会立即返回一些虚拟值。在收到通知时,它将触发 PropertyChange 事件 i.O.通知 GUI “更改”值(从未知值更改为实际值)。

    同步:请求值并等待它可用。

    定时:两者混合,等待一小段时间 (0.01) 秒,然后返回。与异步版本相比,这将避免大量更改事件。

    作为这些包装器的基础,推荐 JGoodies 绑定库的 ValueModel:http://www.jgoodies.com/downloads/libraries.html

    显然,您需要注意仅对实际加载的值执行任何操作,但由于您不打算进行更新,因此这应该不是什么大问题。

    让我以警告结束:我想了很多,但从未真正尝试过,所以要小心。

    【讨论】:

    • 不适用于共享引用,但可以用于复制引用
    • 我不确定您所说的共享参考是什么意思。但我可能同意 :) Hibernate 对象及其会话需要限制在明确定义的范围内,并且两个会话的范围不能重叠。如果您需要将对象从一个范围移动到另一个范围,则必须在目标会话中重新加载它。
    【解决方案3】:

    不要在数据 API 中公开 Session 本身。您仍然可以懒惰地执行此操作,只需确保每次都从 'data' 线程完成补水。您可以使用一个块(不幸的是,可运行或某种命令类可能是 Java 可以为您做的最好的),它由执行来自“数据”线程的异步加载的代码包装。当您在 UI 代码中时,(当然是在 UI 线程上)字段是由数据服务发布的某种“数据准备就绪”事件。然后,您可以从 UI 中使用的事件中获取数据。

    【讨论】:

    • 也许我没有正确理解您的答案(英语不是我的第一语言),但是当视图模型 (=EDT) 尝试延迟获取数据时,这是否仍会触发 LazyInitException(因为 EDT !=“数据”线程)?无论如何,如果解析服务器的响应将在 EDT 上执行,而不是由其他线程执行,那么您的建议将适用于我的应用程序,因为它目前正在完成..hmmmm...我必须考虑一下;) 感谢您接受你的时间!
    • 他的回答假定您在数据线程中有一个未关闭的会话。然而,这会受到 L1 缓存不断增长的问题的影响,除非您非常讲究在不再需要时将项目从会话中分离出来。
    • 我知道“不断增长的会话”问题,但目前这远远超过了“延迟获取禁用”问题;)我是否还需要两个会话(一个用于数据线程,一个用于 EDT)以便在视图中进行延迟获取?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-25
    • 2015-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多