【问题标题】:Problems with Spring and Hibernate SessionFactory: Domain object scope restricted to sessionSpring 和 Hibernate SessionFactory 的问题:域对象范围仅限于会话
【发布时间】:2012-05-14 22:13:12
【问题描述】:

我一直在我的 Spring/Hibernate 应用程序中使用会话工厂(注入 DAO 对象的单例 Bean),我正在使用服务层架构,但我遇到了以下问题:

每当我从数据库中获取域对象时,它都会使用休眠会话工厂提供的新会话。在多次请求同一行的情况下,这会导致具有同一域对象的多个实例。 (如果使用单个会话,它将返回指向同一引用的多个对象)因此,对其中一个域对象所做的任何更改都不会被表示同一行的其他域对象考虑在内。

我正在开发一个具有多个视图的 SWING 应用程序,并且我从不同位置(和查询)获得相同的数据库行,因此我需要获取指向同一实例的域对象。

那么我的问题是,是否可以使用 SessionFactory 来实现这一点?如果没有,对我的整个应用程序使用单个会话是否是一种好习惯?在这种情况下,我应该如何以及在哪里声明这个会话? (应该是像sessionFactory一样注入DAO对象的bean吗?)

提前感谢您的帮助

【问题讨论】:

    标签: spring hibernate session concurrency sessionfactory


    【解决方案1】:

    您正在寻找Open Session in View Pattern。本质上,您希望在应用程序启动时将 Session 绑定到您的线程,并在应用程序的整个生命周期中使用相同的 Session。您可以通过创建一个像这样保持会话的单例实用程序类来做到这一点(请注意,我的示例使用EntityManager 而不是Session,但您的代码将基本相同):

      private static EntityManager        entityManager;
    
      public static synchronized void setupEntityManager() {
        if (entityManager == null) { 
          entityManager = entityManagerFactory.createEntityManager();
        } 
    
        if (!TransactionSynchronizationManager.hasResource(entityManagerFactory)) {
          TransactionSynchronizationManager.bindResource(entityManagerFactory, new EntityManagerHolder(entityManager));
        }
      }
    
      public static synchronized void tearDownEntityManager() {
        if (entityManager != null) { 
          if (entityManager.isOpen()) { 
            entityManager.close();
          } 
    
          if (TransactionSynchronizationManager.hasResource(entityManagerFactory)) { 
            TransactionSynchronizationManager.unbindResource(entityManagerFactory);
          } 
    
          if (entityManagerFactory.isOpen()) { 
            entityManagerFactory.close();
          } 
        } 
      }
    

    请注意,Open Session in View 模式存在固有风险。例如,我在 cmets 中注意到您打算在应用程序中使用线程。会话不是线程安全的。因此,您必须确保您没有尝试以线程方式访问数据库。*

    您还必须更加了解您的集合获取策略。在打开会话和延迟加载的情况下,您总是有可能对数据库施加过多的负载。

    *我以前在 NetBeans 应用程序中使用过这种方法,我知道它使用线程处理某些任务。我们从来没有遇到过任何问题,但您需要注意风险,其中有很多。

    编辑

    根据您的情况,也可以从 Session 中驱逐您的域对象并缓存分离的对象以供以后使用。此策略要求您的域对象不要经常更新,否则您的应用程序会变得不必要地复杂。

    【讨论】:

    • 在视图中打开会话用于解决“延迟加载”问题。但是问题的作者对“延迟加载”没有问题(至少现在还没有)。还要检查这个SO-question
    • @VadimPonomarev Open Session in View 恰好也解决了 OP 的问题,即确保他的所有视图都可以访问相同的域对象,即相同的会话。问题是同一个问题。我也知道 OSIV 的问题。链接问题中列出的两个问题与 Web 应用程序不同。我在回答中提到了第三个(获取策略/数据库负载)。
    【解决方案2】:

    Spring 中的 Hibernate 会话(我将其称为 h-session)通常绑定到线程(请参阅 HibernateTransactionManager 的 JavaDoc),因此每个线程获取一次 h-session。

    如果您在一个 h-session 上多次调用“get”或“load”,则用于检索相同对象的一级缓存(h-session 缓存 - 始终打开)。但是这个缓存不适用于查询。

    此外,您不应该忘记与事务隔离相关的问题。在大多数应用程序中,使用“已提交读”隔离级别。而这种隔离级别受称为“不可重复读取”的现象影响。基本上,如果您多次查询该行,您可以在一个事务中收到同一行的多个版本(因为可以在另一个事务中的查询之间更新该行)。

    因此,您不应该在一个 h-session/transaction 中多次查询相同的数据。

    【讨论】:

    • 嗨瓦迪姆,感谢您的回答。问题是我有多个视图(线程)请求数据库中的同一行。每当他们这样做时,他们都会调用自己调用 DAO 层的服务层。 (服务和 DAO 类是 bean 单例,由 Spring 注入)DAO 层然后打开一个新的 h-session,(通过会话工厂)从数据库中检索行,然后关闭 h-session。然后我从不同的视图(线程)访问同一个数据库行,我最终得到同一个数据库行的多个实例,而不是对同一行的多个引用。
    • 当使用单个会话时,每当我查询数据库行时,如果之前已经请求过域对象,它会返回相同的域对象实例。我也需要使用不同的会话。 (通过会话工厂)如果不可能,我需要从应用程序的开始到结束使用相同的会话,我不知道该怎么做。会话可以是注入 DAO 类的单例吗?然后我必须在发生任何事情之前在我的主类中打开它,并在调用 DAO 方法时让 spring 注入它?提前致谢
    • 多视图是什么意思?几个窗口?如果数据在其中一个窗口中更新,您想立即在窗口之间同步数据吗?
    • 实际上,Spring 工作流设计用于在应用服务器上同时在多个线程中服务多个请求。如果您有单线程应用程序,您可以直接使用 Hibernate(打开会话、处理事务)。
    • 但是,引用 Hibernate 文档中有关 Session 的内容:The lifecycle of a Session is bounded by the beginning and end of a logical transaction.
    猜你喜欢
    • 2012-03-21
    • 2019-01-04
    • 2014-06-14
    • 1970-01-01
    • 2017-10-16
    • 2015-12-10
    • 2012-03-20
    • 2011-09-07
    • 2010-12-29
    相关资源
    最近更新 更多