如何在AppEngine(GAE)中执行数据库锁定? [英] How to do a database lock in AppEngine (GAE)?
问题描述
在GAE中,我有一个充满一次性的表格,例如最后使用的序列号等,并不真正落入其他表格。这是一个简单的字符串键与字符串值对。
我有一些代码来获取一个命名的整数,并增加它,像这样:
@PersistenceCapable(detachable =true)
public class OneOff
{
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
@Persistent
private String dataKey;
@Persistent
private String value;
public OneOff(String kk,String vv)
{
this.dataKey = kk;
this.value = vv;
}
public static OneOff persistOneOff(String kk,String vv)
{
OneOff oneoff = new OneOff(kk,vv);
PersistenceManager pm = PMF.get()。getPersistenceManager();
try
{
pm.makePersistent(oneoff);
}
finally
{
pm.close();
}
return oneoff;
}
// snip ...
@SuppressWarnings(unchecked)
synchronized
public static int getIntValueForKeyAndIncrement(String kk ,int deFltValue)
{
int result = 0;
OneOff oneOff = null;
PersistenceManager pm = PMF.get()。getPersistenceManager();
查询查询= pm.newQuery(OneOff.class);
query.setFilter(datKey == kkParam);
query.declareParameters(String kkParam);
List< OneOff> oneOffs =(List< OneOff>)query.execute(kk);
int count = oneOffs.size();
if(count == 1)
{
oneOff = oneOffs.get(0);
result = Integer.parseInt(oneOff.value);
}
else if(count == 0)
{
oneOff = new OneOff(kk,default);
result = deFltValue;
}
else
{
//记录WTF错误。
}
//更新DB中的对象。
oneOff.value =+(result + 1);
try
{
pm.makePersistent(oneOff);
}
finally
{
pm.close();
}
返回结果;
}
// etc ...
调用:
int val1 = OneOff.getIntValueForKeyAndIncrement(someKey,100);
int val2 = OneOff.getIntValueForKeyAndIncrement(someKey,100);
int val3 = OneOff.getIntValueForKeyAndIncrement(someKey,100);
有时我得到所需的增量,有时我得到相同的值。看来我的数据库访问是异步运行,当我想锁定该特定事务的数据库。
我认为
同步
public static
应该为我做,但显然不是(可能是由于多个实例运行!)
无论如何,我怎么做想? (我想锁定我的数据库,而我得到和更新这个值,使整个事情的并发安全。)
谢谢!
== EDIT ==
我已经接受了罗伯特作为正确答案,我想要什么。但是,为了完整性,我在下面添加了我更新的代码。我认为这是正确的,虽然我不确定
if(oneOff == null)
子句(try-catch位)。 public static int getIntValueForKeyAndIncrement(String kk,int defltValue)
{
int result = 0;
Entity oneOff = null;
int retries = 3;
//使用Datastore事务
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
while(true)
{
com.google.appengine.api.datastore.Transaction txn = datastore.beginTransaction();
try
{
Key oneOffKey = KeyFactory.createKey(OneOff,kk);
oneOff = datastore.get(oneOffKey);
result = Integer.parseInt((String)oneOff.getProperty(value));
oneOff.setProperty(value,+(result + 1));
datastore.put(oneOff);
txn.commit();
break;
}
catch(EntityNotFoundException ex)
{
result = defltValue;
}
catch(ConcurrentModificationException ex)
{
if(--retries< 0)
{
throw ex;
}
}
if(oneOff == null)
{
try
{
Key oneOffKey = KeyFactory.createKey (OneOff,kk);
oneOff = new Entity(oneOffKey);
oneOff.setProperty(value,+(defltValue + 1));
datastore.put(txn,oneOff);
datastore.put(oneOff);
txn.commit();
break;
}
finally
{
if(txn.isActive())
{
txn.rollback();
}
}
}
else
{
if(txn.isActive())
{
txn.rollback );
}
}
}
return result;
}
在交易中。 App Engine的交易将防止两个更新互相覆盖,只要您的读取和写入都在一个事务中。请务必注意有关实体群组的讨论。
In GAE, I've got a table full of "one offs" -- things like "last-used sequence number" and the like that don't really fall into other tables. It's a simple String-key with String-value pair.
I've got some code to grab a named integer and increment it, like so:
@PersistenceCapable(detachable="true")
public class OneOff
{
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
@Persistent
private String dataKey;
@Persistent
private String value;
public OneOff(String kk, String vv)
{
this.dataKey = kk;
this.value = vv;
}
public static OneOff persistOneOff(String kk, String vv)
{
OneOff oneoff= new OneOff(kk, vv);
PersistenceManager pm = PMF.get().getPersistenceManager();
try
{
pm.makePersistent(oneoff);
}
finally
{
pm.close();
}
return oneoff;
}
// snip...
@SuppressWarnings("unchecked")
synchronized
public static int getIntValueForKeyAndIncrement(String kk, int deFltValue)
{
int result = 0;
OneOff oneOff = null;
PersistenceManager pm = PMF.get().getPersistenceManager();
Query query = pm.newQuery(OneOff.class);
query.setFilter("dataKey == kkParam");
query.declareParameters("String kkParam");
List<OneOff> oneOffs = (List<OneOff>) query.execute(kk);
int count = oneOffs.size();
if (count == 1)
{
oneOff = oneOffs.get(0);
result = Integer.parseInt(oneOff.value);
}
else if (count == 0)
{
oneOff = new OneOff(kk, "default");
result = deFltValue;
}
else
{
// Log WTF error.
}
// update object in DB.
oneOff.value = "" + (result+1);
try
{
pm.makePersistent(oneOff);
}
finally
{
pm.close();
}
return result;
}
// etc...
However, when I make these calls:
int val1 = OneOff.getIntValueForKeyAndIncrement("someKey", 100);
int val2 = OneOff.getIntValueForKeyAndIncrement("someKey", 100);
int val3 = OneOff.getIntValueForKeyAndIncrement("someKey", 100);
Sometimes I get the desired increment and sometimes I get the same value. It appears that my DB access is running asynchronously, when I'd like to lock the DB for this particular transaction.
I thought that
synchronized
public static
was supposed to do that for me, but apparently not (probably due to multiple instances running!)
At any rate -- how do I do the thing that I want? (I want to lock my DB while I get & update this value, to make the whole thing concurrency-safe.)
Thanks!
== EDIT ==
I have accepted Robert's as the correct answer, since transactions were, indeed, what I wanted. However, for completeness, I have added my updated code below. I think it's correct, although I'm not sure about the if(oneOff==null)
clause (the try-catch bit.)
public static int getIntValueForKeyAndIncrement(String kk, int defltValue)
{
int result = 0;
Entity oneOff = null;
int retries = 3;
// Using Datastore Transactions
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
while (true)
{
com.google.appengine.api.datastore.Transaction txn = datastore.beginTransaction();
try
{
Key oneOffKey = KeyFactory.createKey("OneOff", kk);
oneOff = datastore.get (oneOffKey);
result = Integer.parseInt((String) oneOff.getProperty("value"));
oneOff.setProperty("value", "" + (result+1));
datastore.put(oneOff);
txn.commit();
break;
}
catch (EntityNotFoundException ex)
{
result = defltValue;
}
catch (ConcurrentModificationException ex)
{
if (--retries < 0)
{
throw ex;
}
}
if (oneOff == null)
{
try
{
Key oneOffKey = KeyFactory.createKey("OneOff", kk);
oneOff = new Entity(oneOffKey);
oneOff.setProperty("value", "" + (defltValue+1));
datastore.put(txn, oneOff);
datastore.put(oneOff);
txn.commit();
break;
}
finally
{
if (txn.isActive())
{
txn.rollback();
}
}
}
else
{
if (txn.isActive())
{
txn.rollback();
}
}
}
return result;
}
You should be updating your values inside a transaction. App Engine's transactions will prevent two updates from overwriting each other as long as your read and write are within a single transaction. Be sure to pay attention to the discussion about entity groups.
这篇关于如何在AppEngine(GAE)中执行数据库锁定?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!