如何通过外部更新保持你的休眠 1 级缓存一致?

Posted

技术标签:

【中文标题】如何通过外部更新保持你的休眠 1 级缓存一致?【英文标题】:How to keep your hibernate levell 1 cache consistent through external updates? 【发布时间】:2012-07-03 09:09:52 【问题描述】:

请原谅我这个问题,但到目前为止我无法通过我的研究找到任何解决方案。 (至少没有什么对我有帮助)

我的情况:

我正在将休眠用于 vaadin 网络服务 网络服务正在获取上传的文档,并通过休眠将它们保持不变地放在我的 mysql 数据库中 后台的计算服务(稍后可能在另一台服务器上的单独进程)对上传的文档执行计算任务,并将结果再次添加到数据库中 webservice 识别数据库中的分析结果并显示给用户

我的问题:

我的问题是一级休眠缓存 计算服务启动后,他不会对后来更新的文档进行任何计算,因为当他完成给定的搜索查询时,这些文档不会出现在他的缓存中。 我的 web 服务覆盖了计算服务器中的更改,因为它也不知道任何关于它的信息。

我知道休眠的 1 级缓存是造成这种行为的原因(顺便说一下,2 级缓存没有激活)。

到目前为止我已经尝试过什么

我读了很多书并尝试了几件事,例如在不同的地方调用 session.clear()。这经常给我“会话已关闭”异常并且对我没有帮助(尽管我读到有人提到这有助于解决他们的问题)

我试图确保会话最终总是关闭,但这并没有解决问题,而且我读到我不需要手动关闭会话,在我使用事务之后.commit() (我听说这是每个请求模式中的标准)

我尝试关闭我的 sessionfactory 并在每次我的计算服务需要搜索数据库时重新打开它。如果我也在我的 Web 服务上执行此操作,这部分工作并且可能全部工作,但这对我来说似乎不是一个很好的解决方案(也消耗内存,不是吗?)

我尝试添加

2

到我的 hibernate.cfg.xml,但这也只是部分起作用。使用这个我的计算服务完成了计算任务,但每次通过 Web 服务上传新数据时,他的更改都会再次被覆盖。因此检测到了新的上传,但是一旦新的上传到达,所有已经分析过的文档都会再次被分析。

我读到了一个名为“setForceCacheRefresh(boolean forceCacheRefresh)”的查询方法(例如,从这里http://www.dil.univ-mrs.fr/~massat/docs/hibernate-2/api/net/sf/hibernate/Query.html#setForceCacheRefresh%28boolean%29),但不知何故我在我的休眠版本中找不到这个方法。遗憾的是,描述听起来不错

我一直在思考我管理会话的方式是否不正确。我的 DAO 类有两个方法,在事务的每次开始和结束时都会调用它们。

public abstract class DAO 

/**
 * Returns the current hibernate session. Also takes care that there's
 * always an open hibernate transaction when needed.
 * 
 * @return Current hibernate session
 */
public static Session getSession() 
    Session currentSession = HibernateUtil.getSessionFactory()
            .getCurrentSession();
    if (!currentSession.getTransaction().isActive()) 
        currentSession.beginTransaction();
    

    return currentSession;


/**
 * Closes the current hibernate session, if there is one.
 */
public static void closeSession() 
    Session sess = HibernateUtil.getSessionFactory().getCurrentSession();
    if (sess.getTransaction().isActive()) 
        sess.getTransaction().commit();
    
    ThreadLocalSessionContext.unbind(HibernateUtil.getSessionFactory());



我也读过一些关于 Optimistic Locking with hibernate 的文章,但我不确定这是否能解决我的问题。我读到乐观锁定只会在缓存对象不是数据库中的对象时抛出异常。但是它是否也加载(刷新)缓存呢?

你是如何在你的休眠中处理这个问题的?你有什么建议我可以做什么,或者我什至在我的 DAO 课程中做错了什么?我对代码示例也很满意。再说一遍:我希望每个进程都知道另一个进程在数据库中所做的更改。 (如果可以的话,我什至会禁用第一个缓存,但因为这是不可能的)

非常感谢您的每一个回答并认为您正在与我分享

【问题讨论】:

org.hibernate.CacheMode#REFRESH 是更通用的刷新缓存值的方法。但它只影响二级缓存(顺便说一句,setForceCacheRefresh 方法也是如此)。您是否使用二级缓存?如果没有,听起来更像是您的会话运行时间过长(过时数据)。 感谢您的评论。正如我的标题和帖子中提到的,我只谈论一级缓存。二级缓存被禁用。 【参考方案1】:

我已经解决了我的问题。

在 Gergely Szilagyi 和他的回答的大力帮助下,第一部分可以解决:

附加对象实例仅存在于 Hibernate 的 Session 中。如果 您正确终止交易和会话,级别 1 缓存将被清除。您使用什么模式进行会话管理? 正在查看会话?

我问这个,因为你的问题在我看来就像一个 会话终止问题。

编辑:

我没有看到 Session.close() 或至少 Session.disconnect() 在您的 代码。据我所知 unbind() 不是自动的。

(感谢这篇精彩的帖子,如果我可以将几个答案标记为正确,我也会标记你的!)

他的回答让我想到了我的会话管理,我的一些问题通过这个得到了解决。

我的问题的第二部分,也是我主要问题中非常重要的部分是如何刷新一级缓存/使其保持一致。经过大量研究,我发现hibernate Session 类包含一个可以直接从数据库加载对象的方法。

给定一个要刷新的 Entity 对象实体,你的行为如下:

//Call session.evict if this object is currently in the session, otherwise not necessary
//It deletes the object from the session cache (If you don't call this method although this object is currenlty in the session an exception will be thrown
session.evict(entity);
//session.get acccesses the database directly and returns you the actual saved entity
Ent1 updatedEntitEnty = (Ent1) session.get(Ent1.class, entity.getPrimaryUniqueID());

现在您可以继续使用收到的更新实体。

session.get(...) 是这里最重要的方法,因为它直接访问数据库并忽略缓存。给定的返回对象是保存在数据库中的最新对象。

注意:还有一个 session.load(...) 方法,在这种情况下没有帮助。 load(..) 访问缓存并从那里检索对象,而 get(..) 则正确。

所以如果你想从数据库中检索对象,不要使用 session.load(...)!

我希望这个答案有一天可以帮助其他人,如果您有任何问题,请与我联系。

【讨论】:

您在加载/获取时绝对不正确。两者都将查看两个级别的缓存。 load() 只允许返回未初始化的代理,而 get() 不允许。这是它们之间的唯一区别。 好吧,我发现了几个方面(例如 mkyong.com/hibernate/… ),其中声明 get 将直接访问数据库。我做了一些小测试,通过休眠插入一些值,在后台手动更改它们,然后用 get 重新读取对象,这对我来说效果很好。对我来说,这似乎是直接从数据库中读取的值(这是我之前的问题)。无论如何,因为这让我可以读出当前状态,它解决了我的问题,所以我可以接受这个作为我的答案。 好吧,我发现有几个网站声称世界末日即将来临.. ;) 但是我向您保证 get 和 load 都可以查看两个级别的缓存。您必须记住,这些答案也是为后代而存在的,我不希望下一个人得到错误的想法。 那么继续提供一些证明我的答案是错误的。并不是说我不接受任何不同。它是关于告诉什么是错的,而不仅仅是讨论你是一个多么专业的专家。为您的陈述添加一些证据,即此方法不能用于在特定情况下使缓存保持一致,或者只是保持沉默。因为你在这里所做的并没有真正的帮助。顺便说一句:如果我是 hibernate 的首席开发人员,我可能会注意让论坛中的人获得帮助,并且我也会对那里的垃圾邮件机器人采取一些措施。 再说一次,我不担心你。您似乎愿意接受某个随机人的互联网博客作为福音。那是你的特权。我担心其他人会追随并相信你指责我所做的事情(即在没有证据的情况下声称某事)。我选择在 IRC 和最近在这里帮助人们; Hibernate 论坛不是我选择的论坛。作为一个愿意花时间帮助别人的人(在我自己的时间,免费),我可以选择在哪里做这件事;奇怪的概念吧?【参考方案2】:

附加对象实例仅存在于 Hibernate 中的 Session 中。如果您正确终止事务和会话,一级缓存将被清除。您使用什么模式进行会话管理?正在查看会话?

我问这个,因为你的问题在我看来像是一个会话终止问题。

编辑:

我在您的代码中没有看到 Session.close() 或至少 Session.disconnect()。据我所知 unbind() 不是自动的。

【讨论】:

非常感谢您的回答,它让我重新考虑了我的会话管理,而且您是对的。始终关闭每个打开的会话是如此(!)重要,因为它还确保您获得最新的数据库更改。可悲的是,它并没有帮助我解决最后一个问题,但它仍然让我修复了它的一部分!非常感谢!

以上是关于如何通过外部更新保持你的休眠 1 级缓存一致?的主要内容,如果未能解决你的问题,请参考以下文章

如果有人从后端手动更新数据库,如何更新休眠缓存?

如何保证solr跟数据库的数据一致性

亿级流量高并发场景下,如何解决一致性问题?

UML-线程标示法

如何保证数据库缓存的最终一致性?

GraphQL:Apollo 客户端缓存如何工作以保持与服务器的数据一致性?