我正在使用Java和Spring Boot开发REST API来管理购买和客户。在我的MySQL数据库中,我有一个表Purchase
,其中包含一个存储唯一ticketId
的列。它不是主键。
当添加新购买时(通过执行PUT
请求),我根据请求中提供的数据创建新购买,获取最大ticketId
,将其递增1并存储在数据库中。主键是自动递增的。
这是我的代码:
@Transactional
public boolean saveNewPurchase(PurchaseDTO data) {
Purchase p = createPurchaseFromData(data);
Long idTicket = purchaseDao.getMaxIdTicket();
p.setIdTicket(idTicket + 1);
save(p);
}
这里是否存在并发问题?假设两个PUT
请求并行执行此方法,是否可以检索相同的最大idTicket
,从而在保存第二次购买时违反唯一idTicket
约束?
如果是这样,我该如何解决?是否可以使方法synchronized
解决问题?
感谢。
答案 0 :(得分:1)
是的,它肯定容易出现并发问题。一个建议是使用AtomicInteger将计数器保留在内存中,这样你就不会最终从DB获得当前的maxId,这可能会导致竞争条件。因此,当应用程序启动时,通过查询数据库,它可以将maxId存储在内存中。这是强大的,即使在任何崩溃的情况下,它总是可以从DB读取信息。
这在分布式环境中不起作用,在这种情况下,有另一个专用于存储计数器的表,是JamieB在评论中建议的更好的方法。
答案 1 :(得分:1)
是的,这里存在并发问题。两个不同的主题可以获得相同的maxIdTicket
,然后使用相同的ticketId
保存购买。
我在这里看到3个解决方案:
AtomicInteger
将计数器保留在内存中AUTO_INCREMENT
,每次需要一个计数器值时插入/获取一行。使用其他RDBMS,您可以使用序列,但我很确定MySQL中没有序列。前2个解决方案在分布式环境中不起作用,所以我会做第三个。