假设您从数据存储中检索一组记录(例如:select * from MyClass,其中reserved ='false')。
我如何确保其他用户未设置保留仍为假?
我查看了交易文档,并对google的解决方案感到震惊,该解决方案是捕获异常并在循环中重试。
我缺少的任何解决方案 - 很难相信在这种环境中无法进行原子操作。
(顺便说一句 - 我可以在servlet中使用'syncronize',但我认为它无效,因为没有办法确保只有一个servlet对象的实例,不是吗?同样适用于静态变量解决方案)< / p>
关于如何解决的任何想法?
(这是谷歌解决方案:
http://code.google.com/appengine/docs/java/datastore/transactions.html#Entity_Groups
看看:
Key k = KeyFactory.createKey("Employee", "k12345");
Employee e = pm.getObjectById(Employee.class, k);
e.counter += 1;
pm.makePersistent(e);
'这需要一个事务,因为在此代码获取对象之后,但在保存修改后的对象之前,该值可能会被另一个用户更新。如果没有事务,用户的请求将在其他用户更新之前使用计数器的值,并且保存将覆盖新值。通过事务,应用程序将被告知其他用户的更新。如果在事务期间更新实体,则事务将失败并发生异常。应用程序可以重复该事务以使用新数据'
可怕的解决方案,不是吗?
答案 0 :(得分:4)
你是对的,你不能使用同步或静态变量。
您在App Engine环境中无法进行原子操作是不正确的。 (看看原子意味着什么here)当你做一个事务时,它是原子的 - 要么一切都发生了,要么什么都没发生。听起来你想要的是某种全局锁定机制。在RDBMS世界中,可能类似于“select for update”或将事务隔离级别设置为序列化事务。这些类型的选项中的任何一种都不具有可扩展性。或者如您所说,它们都是可怕的解决方案:)
如果你真的想要在app引擎中进行全局锁定,那么你可以做到这一点,但它会很丑陋并严重影响可扩展性。您需要做的就是创建某种CurrentUser实体,您可以在其中存储具有全局锁定的当前用户的用户名。在让用户执行任何操作之前,您需要首先检查没有用户已经列为CurrentUser,然后将该用户的密钥写入CurrentUser实体。检查和写入必须在交易中。这样,只有一个用户将成为“当前”,因此具有全局锁定。
答案 1 :(得分:0)
你的意思是这样的:
public void func(Data data2) {
String query = "select from " + objectA.class.getName()
+ " where reserved == false";
List<objectA> Table = (List<objectA>) pm.newQuery(
query).execute();
for (objectA row : Table)
{
Data data1 = row.getData1();
row.setWeight(JUtils.CalcWeight(data1, data2));
}
Collections.sort(Table, new objectA.SortByWeight());
int retries = 0;
int NUM_RETRIES = 10;
for (int i = 0; i < Table.size() ; i++)
{
retries++;
pm.currentTransaction().begin(); // <---- BEGIN
ObjectA obj = pm.getObjectById(Table.get(i).class, Table.get(i).getKey());
if (obj .getReserved() == false) // <--- CHECK if still reserved
obj.setReserved(true);
else
break;
try
{
pm.currentTransaction().commit();
break;
}
catch (JDOCanRetryException ex)
{
if (j == (NUM_RETRIES - 1))
{
throw ex;
}
i--; //so we retry again on the same object
}
}
}