5

我们已经为我们的 J2EE 应用程序创建了一个缓存层。在本例中,我们使用 Ehcache。这带来了一些挑战。

让我们举这个例子。

OrderItem orderitem = cache.getOrderItemByID("id");
OrderItem old_orderitem = cache.getOrderItemID("id");

orderitem.setStatus(1);
old_orderitem.setStatus(2);

如果我们不小心,对这些对象中的任何一个所做的任何更改都会影响另一个(它们引用同一个对象)。将 orderitem 保存回数据库将使其具有 status=2

我们如何以最好的方式解决这个问题?

我们尝试为每个对象创建一个 .copyObject() 方法。它只是创建一个新对象并设置所有值。但这似乎不是一个好的解决方案。

这个例子只是为了说明。代码远比这复杂,但结果是一样的。

************************更新 15.07.2010 ************************ ******************************

在 EHCache 2 中有一些选项可以打开 copyRead() 和 copyWrite()。这解决了我所有的问题:)

4

4 回答 4

6

这是可变状态的问题。它不仅是缓存,而且任何时候您都可以对同一个对象有多个引用并且该对象是可变的。例如:

HashMap map = new HashMap();
map.put("one", new OrderItem());
OrderItem one = map.get("one");
OrderItem two = map.get("one");
one.setStatus(1);
two.setStatus(2);

会有完全相同的问题。当您有一个并发环境时,这变得更加复杂。解决此问题的一种方法是仅具有不可变对象。这样,如果您想要一个具有不同状态的对象,您将不得不创建一个新对象。它还使并发编程更容易。

您考虑复制对象是正确的。您的选择是:

每个人都有自己的长处和短处,哪一个效果最好取决于您的环境。

于 2010-01-07T17:47:49.963 回答
1

听起来您想要缓存表示对象的数据(例如,数据库查询的结果)而不是对象本身(例如,您可能希望在其他地方检索的用户的会话数据)。

如果是这种情况,您的自定义缓存层(围绕 ehcache)需要在用户发出请求时从缓存数据创建一个对象 - 这每次都会为您提供一个唯一的对象,并且您不会受到对象干扰。

于 2010-01-07T17:31:04.040 回答
0

当您通过 id (getOrderItemById) 从缓存中检索时,我假设这意味着“获取由 id=唯一标识的对象?”。在您的第二个摘录中,您拥有(或正在尝试拥有?)2 个不同的对象,因此看起来您正在尝试在代码中执行 2 个相互矛盾的事情。

您可以强制执行唯一性-Id="ID" 始终是同一个对象。如果你设置了状态然后重置它,这意味着同一个对象有新的状态。如果 id 也匹配,您可能希望扩展 .equals 方法以返回 true。

您还可以使用“脏”标志来标记已从(事务性?)数据存储中更改的对象。

制作副本也不是解决此问题的好方法,尽管尚不清楚让 2 个对象以相同的 id 运行意味着什么。也许应该使用 null id 创建副本以指示它尚不存在于缓存中?

您的应用程序的正确行为是什么?

于 2010-01-07T17:34:55.890 回答
0

Ehcache 通过显式锁定 API 提供对锁定键的支持。您可以使用读写锁锁定缓存中的键。这不会锁定缓存中的值,因此如果程序员如此决定,对象仍然可以更改。

这可能会也可能不会解决您提到的问题,这取决于您如何看待它。如果程序员愿意自律,并且仅将读取获取的对象用于读取目的,并且在获取对象时修改+更新缓存,并且键上有写锁定,那么这应该可行。

然而,正如 jamie mccrindle 所提到的,可变状态问题并没有消失。

参考:http ://ehcache.org/documentation/explicitlocking.html

于 2011-07-21T09:05:52.150 回答