我必须在XG交易中生成一个唯一的发票号,其中包含以下数据模型中的 3个实体组:
(toplevel)ContactRoot< - (祖先)< --- 联系:联系必须在交易期间更新为状态客户
(toplevel) CustomerSettings :保存要使用的下一个序列号; CustomerSettings只有一个实例,具有固定的静态ID;在交易期间,序列号必须增加+1
(toplevel)InvoiceRoot< - (祖先)< --- 发票:根据CustomerSettings中的序列号分配新的唯一发票号;
这是DAO实施的重要部分(删除了无关的业务规则检查等):
public void saveInvoice(final Invoice invoice) throws BusinessRuleException {
final Objectify ofy = ObjectifyService.factory().begin().cache(true);
ofy.transact(new Work<Void>() {
@Override
public Void run() {
CustomerSettings customerSettings = ofy.load()
.key(Key.create(CustomerSettings.class, CustomerSettings.ID)).safeGet();
Contact contact = ofy.load().key(createContactKey(invoice.getContactId()).safeGet();
contact.setContactType(ContactType.CLIENT);
ofy.save().entity(contact).now();
String invoiceNumber = generateSequence(ofy, customerSettings);
invoice.setInvoiceNumber(invoiceNumber);
ofy.save().entity(invoice).now();
return null;
}
});
}
用于生成下一个序列号的简化版本,其中下一个序列号将在下次调用时增加,而CustomerSettings必须在事务上更新(我已将此同步,但我认为这不是很有用) :
private synchronized String generateSequence(Objectify ofy, CustomerSettings settings) {
String ret = "";
int sequence = settings.getNextSequence();
settings.setNextSequence(sequence + 1);
ofy.save().entity(settings).now();
ret = "" + sequence;
return ret;
}
这是我的单元测试对于可变线程数:
的样子private void test(final int threadCount) throws InterruptedException, ExecutionException {
final Environment currentEnvironment = ApiProxy.getCurrentEnvironment();
Callable<String> task = new Callable<String>() {
@Override
public String call() {
ApiProxy.setEnvironmentForCurrentThread(currentEnvironment);
return generateInvoiceNumber();
}
};
List<Callable<String>> tasks = Collections.nCopies(threadCount, task);
ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
List<Future<String>> futures = executorService.invokeAll(tasks);
List<String> resultList = new ArrayList<String>(futures.size());
// Check for exceptions
for (Future<String> future : futures) {
// Throws an exception if an exception was thrown by the task.
resultList.add(future.get());
}
// Validate the IDs
Assert.assertEquals(futures.size(), threadCount);
List<String> expectedList = new ArrayList<String>(threadCount);
for (long i = 1; i <= threadCount; i++) {
expectedList.add("" + i);
}
Collections.sort(resultList);
Assert.assertEquals(expectedList, resultList);
}
@SuppressWarnings("unchecked")
private String generateInvoiceNumber() {
InvoiceDAO invoiceDAO = new InvoiceDAO();
Invoice invoice = ... create a valid invoice
invoiceDAO.saveInvoice(invoice);
log.info("generated invoice number : " + invoice.getInvoiceNumber());
return invoice.getInvoiceNumber();
}
例如,当我同时运行32个线程时:
@Test
public void test32() throws InterruptedException, ExecutionException {
test(32);
}
但后续线程未发现先前的交易增加了发票编号顺序。
结果如下:
junit.framework.AssertionFailedError:expected:&lt; [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18, 19,20,21,22,23,24, [25,26,27,28,29,30,31,32]&gt;但是:&lt; [1,1,1,1,1,1,1,1,1,1, 1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,2,1,2,2,2,2,2,2,2,2,2,3,3,3,3 3]&GT;
我已经浏览了几次文档,无法弄清楚为什么这不起作用?
如果您在交易中访问多个实体组,则 交易是一个XG交易。如果你只访问一个,它 不是。 5个EG的标准限制适用于所有交易。 objectify transactions documentation
我做错了什么?
答案 0 :(得分:1)
这段代码使代码不是事务性的:
最终Objectify ofy = 。ObjectifyService.factory()()开始高速缓冲存储器(真);
ofy.transact(new Work<Void>() { .... ofy.save().entity(settings).now(); ....
}
因为我重用了非事务性的objectify实例。要在事务处理中获取实例,您必须始终询问这样的实例:
ObjectifyService.ofy()
小组讨论here中的更多信息。
查看ObjectifyService
的实现,您可以看到新实例被推入/弹出堆栈;
除此之外,测试用例仍然没有运行..测试的最佳方法可能是发送http请求同时发送;