Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Spring Java JPA:如何跨HTTP请求保持会话活动?_Spring_Caching_Jpa_Httprequest - Fatal编程技术网

Spring Java JPA:如何跨HTTP请求保持会话活动?

Spring Java JPA:如何跨HTTP请求保持会话活动?,spring,caching,jpa,httprequest,Spring,Caching,Jpa,Httprequest,在web应用程序中,我使用JPA实体将我的域对象持久化(和检索)到(和从)底层数据库。 这些JPA实体在web应用程序的整个运行时间内都保持在内存缓存结构中的“热”(想想Map) 因此,我向我的web应用程序发出请求,从存储库加载一个实体。该实体被放入内存缓存结构中。在这个请求的整个生命周期中,我可以愉快地访问这个实体的任何字段。在第一次请求期间,也可以将关系惰性地加载到其他实体,即使在我的视图中也可以:我成功地使用了视图中的打开会话模式(通过Spring的OpenEntityManagerVi

在web应用程序中,我使用JPA实体将我的域对象持久化(和检索)到(和从)底层数据库。 这些JPA实体在web应用程序的整个运行时间内都保持在内存缓存结构中的“热”(想想
Map

因此,我向我的web应用程序发出请求,从存储库加载一个实体。该实体被放入内存缓存结构中。在这个请求的整个生命周期中,我可以愉快地访问这个实体的任何字段。在第一次请求期间,也可以将关系惰性地加载到其他实体,即使在我的
视图中也可以:我成功地使用了视图中的打开会话模式(通过Spring的
OpenEntityManagerViewInterceptor

第一个请求已结束

我正在对我的web应用程序执行下一个请求。此请求请求另一个实体此实体已在内存缓存结构中,因此从那里加载。从这个实体中,我尝试访问一个字段,该字段应该延迟加载到其他实体的关系。不幸的是,这导致了令人讨厌的
org.hibernate.LazyInitializationException:无法初始化代理-无会话(我使用hibernate作为我的底层JPA实现)

据我所知,这个异常源于这样一个事实,即在第一个请求结束后,JPA/Hibernate已经结束了任何JPA会话,但我的内存缓存结构中的实体仍然期望这些会话中的任何一个存在;当下一个请求触发延迟加载实体的机制时,延迟加载机制找不到任何不再存在的会话

我的问题有什么解决方案

  • 解决方案之一是在第二个请求开始时使用
    session.update()
    将实体重新连接到会话
  • 另一个解决方案是在Hibernate中使用,而不是您自己的解决方案。它应该比任何自主开发的缓存机制更可靠

  • 基本上,您不能在HTTP请求之间保持会话活动,因为这意味着在请求之间保持事务打开

    我认为唯一的解决方案(除了检测自己加载了什么和没有加载什么)是在将实体放入缓存之前获取整个实体。我想你不应该把部分加载的对象放在缓存中。如果不想第一次加载整个对象,可以对对象关系使用单独的缓存


    如果您想要,您也可以考虑启用亚当的建议的Hibernate缓存,但我认为它不会与HealdLoad字段很好地工作。

    < p>因为您的对象与当前会话分离,所以您得到了这个异常。在评估此对象之前,必须将其重新附着到当前会话

    session.update(object);
    

    您可以阅读详细信息

    能否确保在请求结束之前加载所需的所有内容,例如在缓存之前复制/克隆到新对象?一般来说,从性能的角度来看,最好保持数据库事务尽可能短。@tofarr在我的域模型中,JPA entity类指向其他实体。所以基本上,假设我的实体类是
    Person
    ,方法是
    aPerson.getFriends()
    返回一个
    ;域模型实例成为网络图。我需要在连续的HTTP请求中从
    Person
    跳到
    Person
    。如果我加载了所有可能的朋友,以及朋友的朋友和朋友的朋友的朋友和(…),那么我需要加载数据库中的每个
    Person
    ,以确保我可以访问所有数据。在JPA上下文中,“会话”和“事务”是两个独立的概念,不是吗?据我所知,JPA会话可以保持活动状态,而不会影响事务语义。所以我想说“这意味着在请求之间保持事务开放”的说法并不总是正确的。。。。关于预取技术:我的实体是非常递归的(想想
    person.getFriends().getFriends()…
    ,所以有一个大的实体图)。因此,急切地获取所有实体“以备不时之需”可能意味着需要加载成千上万的对象。没错,会话更像是与数据库的连接,实际上可以跨越多个事务。由于实体管理器无法再知道数据库对象的状态,因此我不能确切地确定关闭事务后托管对象会发生什么情况。但请注意,会话不是线程安全的。Hibernate的二级缓存值得一试,它通常应该透明地处理延迟加载的集合。但是我不确定是否可以缓存关系本身(我最近尝试了一个简单的OneToMany,但我无法缓存)
    会话。更新(…)
    似乎是特定于Hibernate的。但是我使用的是JPA,因此我需要相应的JPA机制来实现这一点。现在还出现了我应该在何时何地重新附加实体的问题-最好是在抛出
    lazyiinitializationexception
    的时间和地点?(这可能可以在catch块中的有问题的entity方法内部完成)。最后,我需要找到“那个JPA
    session
    manager”,以便调用
    session.update(this)如何使用Spring将“该JPA
    会话
    管理器”注入我的实体?好的,在我刚才评论的基础上,我成功地使用建议的“会话更新”解决方案解决了问题:。。。1.将有问题的实体注入
    EntityManagerFactory emf
    。。。2.在有问题的实体内部,在每次访问可能延迟加载的字段时,用try-catch块包围该字段。。。3. <代码>公共对象problematicMethod(){try{…}catch(LazyInitializationException e){JpaFacebookUser th