В GAE у меня есть таблица, полная «одноразовых» - такие вещи, как «последний использованный порядковый номер» и тому подобное, которые на самом деле не попадают в другие таблицы.Это простой ключ String-key с парой String-value.
У меня есть некоторый код для захвата именованного целого числа и его увеличения, например, так:
@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...
Однако, когда я делаюэти вызовы:
int val1 = OneOff.getIntValueForKeyAndIncrement("someKey", 100);
int val2 = OneOff.getIntValueForKeyAndIncrement("someKey", 100);
int val3 = OneOff.getIntValueForKeyAndIncrement("someKey", 100);
Иногда я получаю желаемый прирост, а иногда я получаю одно и то же значение.Кажется, что мой доступ к БД работает асинхронно, когда я хотел бы заблокировать БД для этой конкретной транзакции.
Я думал, что
synchronized
public static
должен был сделать это для меня,но, очевидно, нет (вероятно, из-за нескольких запущенных экземпляров!)
В любом случае - как мне сделать то, что я хочу?(Я хочу заблокировать свою БД, пока получаю и обновляю это значение, чтобы сделать все это безопасным для параллелизма.)
Спасибо!
== РЕДАКТИРОВАТЬ ==
Я принял правильный ответ Роберта, поскольку транзакции были действительно тем, чего я хотел.Однако для полноты я добавил свой обновленный код ниже.Я думаю, что это правильно, хотя я не уверен насчет предложения if(oneOff==null)
(бит try-catch.)
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;
}