Hibernate + Swing

Tor*_*dek 12 java swing hibernate

什么是最佳做法?

  • 整个应用程序的单个会话
  • 每个窗口一个会话
  • 每个线程一个会话,并随机分离所有内容
  • 别的什么?

我用Google搜索,并没有达成共识.每个人都说了些什么,然后把它拿回来.我发现围绕着愚蠢的会议......

那么,这笔交易是什么?

raz*_*nha 6

也许每个事件处理的会话是正确的方法.在Web应用程序中,我们通常为每个请求创建一个会话(使用所有这些OpenSessionInView的东西等).如果我们仔细思考,Web应用程序中的每个请求都是应用程序中的不同交互,并且在Swing应用程序中,每个被触发的事件都是应用程序中的不同交互.如果我们应用我们在Swing应用程序中的Web应用程序中应用的相同原则,那么我们应该为每个事件处理创建一个会话.

但我们必须确定使用Hibernate是否是最佳选择...您正在访问的数据库是本地数据库还是远程数据库?其他应用程序共享同一个数据库吗?您应该考虑创建EJB\HTTP后端,而不是让Swing应用程序直接访问数据库.使用Hibernate表明你有一个不那么简单的数据库,所以我认为你应该考虑创建一个EJB\HTTP后端.


Kim*_*m L 6

我在企业级的Rich Internet Application中使用了Hibernate,这种技术类似于很多hibernate + swing组合.我花了很多时间研究在3层应用程序中使用hibernate的不同设计模式.

Hibernate并不完全适用于这种应用程序,因此没有太多关于此主题的信息.我尝试了不同的设计模式,但大多数会导致内存泄漏,会话中附加和取消附加对象的问题,事务等问题等.最终的解决方案是使用每个请求会话模式.基本上,在您从UI逻辑到业务逻辑的每个请求中,您都会创建一个新的hibernate会话.然后,您的业务逻辑执行您希望它执行的任何操作,并在结束业务逻辑执行之前,刷新并关闭打开的会话.这样就不会有任何内存泄漏,对象将被正确连接并从会话中解除附加.

这不是一个完美的解决方案,因为您将遇到一些问题,例如延迟加载和事务.我将简要解释这些问题以及如何解决这些问题.

事务
因为您在每个请求之后终止了hibernate会话,所以您不能拥有超出一个请求的事务.这可能有些问题,例如,假设您要在同一事务中存储10个对象.您无法为每个对象单独发出保存请求,因为事务已终止.因此,您需要创建一个方法,该方法将对象列表作为输入,并将所有这些对象保存在同一事务中.如果事务失败,则回滚所有对象.

延迟加载
您的对象的延迟加载将无法工作,因为它们很可能未附加到会话(即,如果您在会话终止后延迟加载某些内容).要使延迟加载工作,您需要将对象重新附加到会话.我想出了一个解决方法,这在创建新的实体对象时有点棘手,但在日常开发中效果很好.我们的想法是为一个延迟加载的字段设置重复的getter和setter.一对是私人的,另一对是公开的.这个想法是私有getter/setter对是hibernate内部使用的,而公共getter和setter是瞬态的并且被开发人员使用.因此,当开发人员调用公共getter时,getter会检查字段是否已加载,如果没有,则将对象附加到会话,加载字段并关闭会话.Voilá,一切正常,开发人员从未注意到任何事情.这是一个小代码,可以给你一个想法:


@Entity
public class Foo {
  List<Bar> bars = new ArrayList<Bar>();

  @OneToMany
  private List<Bar> getBarsInternal() {
    return bars;
  }

  private void setBarsInternal(List<Bar> bars) {
    this.bars = bars;
  }

  @Transient
  public List<Bar> getBars() {
     // pseudo code
     if(check if bar is still lazy loaded) {
       // the field is still lazy loaded
       // attach the field to a session an initialize it
       // once the field is initialized, it can be returned
    }
    return getBarsInternal();       
  }

  public void setBars(List<Bar> bars) {
    setBarsInternal(bars);
  }
}
Run Code Online (Sandbox Code Playgroud)


Sur*_*rya 5

我们从每个应用程序的一个会话开始,我列出了下面的动机和问题,

顾名思义,整个用户与应用程序的交互将由一个单独的长生活会话组成,所有(主要)与数据库的通信将通过此会话.您的"工作单元"现在将包含用户与应用程序的所有交互...这意味着您的"应用程序事务"是应用程序的整个生命周期.

为什么?1.执行此操作的最大驱动程序似乎是能够使用hibernate的lazyintialization功能,例如,当选择下拉组合框时,显示Book信息的对话框可以动态填充作者信息.

  1. 由于对基础数据的所有修改都是通过这个单个长时间运行的会话完成的,因此您可以在方便的位置明确划分事务边界.

  2. 利用会话缓存,而无需每次从数据库加载/卸载对象.

如何:

如果您在GUI线程中执行所有主要工作(几乎所有Java GUI系统都是单事件线程,您可以使用tweak线程本地模式从GUI线程获取主会话.

问题:

上述优势可能会描绘出一幅美好的画面,但是在没有太多预见的情况下使用长时间运行的会话可能会导致一些严重的问

  1. 您的应用程序可能会遇到"膨胀的会话缓存"现象.随着会话缓存变得越来越大,每个应用程序变得越来越慢,因为hibernate必须遍历会话缓存中的所有对象以进行每个查询(具有大量会话)这可能会减慢你的速度,如果会话变大,刷新它将花费越来越多的时间,即使没有变化).如果允许会话变得很大,那么上面的3个优势可能会成为一个缺点(由于休眠事务后写).在某些情况下可能有用的解决方法可能是将query.setReadOnly(true),session.setReadOnly()用于将只读对象加载到会话中的查询/实体.
  2. 手动"逐出"以保持会话一致,您可能在应用程序的各个位置出于各种原因使用sql查询来执行更新/删除,在这种情况下,您必须进行手动逐出以防止会话被破坏.
  3. 如果您计划从"其他会话"访问数据,则事务性后写可能会妨碍您(取决于您管理长时间运行的会话的方式).
  4. 当hibernate正在执行其延迟加载魔法时,用户可能会遇到GUI冻结.
  5. 经典延迟加载n + 1查询问题(参见下面的链接)
  6. 过时的数据问题.如果数据由某些后台作业或线程更新而不使用主会话..您的主会话将不会更新.

我仍然认为如果你有一个一致的驱逐策略,例如在订单窗口关闭时驱逐订单图,这可能是一个很好的策略.

我们切换到每个窗口的会话,它有自己的问题,如管理窗口间窗口通信(例如,价格窗口的更改可能使订单窗口数据失效).可能想看看Christian Bauer使用这种方法的摇摆例子.

我猜,和其他一切一样,没有一种正确的方法可以做到这一点,你如何管理会话取决于应用程序的布局方式,例如,每个窗口是否可以是一个独立的工作单元,它不会干扰其他工作单元,然后每个窗口的会话可能是一个更好的方法.