我有一个Spring Boot应用程序,它通过REST API获取请求。
收到这样的请求后,我为我的对象生成一个offerID。这不是数据库的主键,它只是我们与客户沟通的人类可读数字,而不是正常的数据库ID。
此优惠ID类似于" 16051801"我们在2016年5月18日收到的第一个请求。
将其作为String存储在数据库中,以选择当前的" max"用SELECT count(r) FROM Request as r WHERE r.offerId LIKE ?1%
之类的东西
我刚刚输入了当前日期" 160518"并获取存储在那里的请求数,并将计数+ 1作为下一个值。
正如您可能已经猜到的那样,这会带来一些问题。
一方面,这很好,因为我们现在没有那么多的流量在这个网站上。
但是,如果我们有两个请求在几乎同一时刻进入,我会得到类似于"竞争条件"我创造了两次相同的offerId,这并不酷。
我已经为此列添加了一个唯一约束,这会阻止创建具有相同值的多个条目,但在这种情况下,两个请求中的一个失败。
我已经尝试过类似的东西了:
try {
super.create(customerRequest);
} catch (DataIntegrityViolationException ex) {
if (ex.getCause() instanceof ConstraintViolationException) {
ConstraintViolationException constraintViolationException = (ConstraintViolationException) ex.getCause();
if (constraintViolationException.getCause().getMessage().contains("'UC_CUSTOMERREQUESTOFFERNUMBER_COL'")){
customerRequest.setOfferNumber(generateOfferNumber(customerRequest));
super.create(customerRequest);
}
}
}
但在这种情况下,我总是得到像Do not flush the session if an Exception occurs
基本上在这种情况下我能够处理并解决问题,但Spring / Hibernate阻止我这样做。
所以真正的问题是,处理这样一个"问题的最佳做法是什么?以一种非hacky的方式(我也尝试过使用Thread.sleep()的东西,感觉不是很好......)并且还可以扩展(应该也可以在将来使用多个应用程序实例)。
作为一个数据库,我们使用MySQL,据我所知,我们无法移动这个" ID-Generation"进入数据库。
答案 0 :(得分:2)
MySQL固有地没有序列的概念。 你最接近的是AUTO_INCREMENT。
对于MyISAM表,您需要一种简单的方法。只需在表格中保存订单的日期,并根据日期,auto_increment发生。
这就是MYSQL手册所说的:
"对于MyISAM表,您可以在多列索引中的辅助列上指定AUTO_INCREMENT。在这种情况下,AUTO_INCREMENT列的生成值计算为MAX(auto_increment_column)+ 1 WHERE prefix = given-prefix。当您想要将数据放入有序的组中时,这非常有用"
查看此链接以获取更多信息。 http://dev.mysql.com/doc/refman/5.7/en/example-auto-increment.html
答案 1 :(得分:1)
关于将ID生成逻辑移动到DB的最后想法,是的,您可以这样做。 您可以为非pk列创建序列。您可以在序列中使用逻辑,当发生任何插入时,将使用休眠自动调用。
MySQL和Hibernate都可以通过使用Sequence的概念解决您的问题。
我们有一个场景,我们必须使用“PK-ABCD-001”等格式生成自定义键,例如“545455-ABCD-008”。使用序列我们已经解决了这个问题。
答案 2 :(得分:1)
抛出几个想法:
在Oracle中,我们有" SEQUENCE"每次从中查询时都会为您提供一个新编号。虽然在MySql中没有直接替换,但应该可以通过使用带有自动增量列的虚拟表来模拟效果
我曾经写过一个小的存储过程来执行序列号生成(它负责日期前缀,每天自动重置序列等)。自治事务用于避免并发事务相互阻塞。 MySQL中的自主事务没有直接替换(再次),但是有一些技巧可以实现类似的结果。
编写一个执行"序列生成"的小型网络服务,并以您想要的任何方式公开它(SOAP,REST等)。鉴于逻辑的简单性,我认为你不需要它可扩展。将当前序列存储在DB中(以便可以恢复),并使序列生成逻辑具有自己的短事务。例如,您可以通过"缓存"来提高性能。序列号(例如,每次将DB中的数字增加100,您可以在不访问数据库的情况下返回100个序列号。)
答案 3 :(得分:1)
为了使id生成过程成为原子,如果只有一个集中式服务器,则可以在应用程序上下文中为后缀保存AtomicInteger字段。此字段将在每天的每个开始时初始化为0,每次新事务发生时,您都使用该方法:increamentAndGet ()
如果您在分布式环境(云)中使用多台服务器,我建议您寻找一种方法将其存储在其中一台服务器上,并直接从其他服务器接收它。例如,亚马逊的AWS有一个名为Redis的服务,它提供了一个缓存存储服务器,可以存储这样的ID,并以原子方式获取和增加它们。
答案 4 :(得分:1)
您使用什么隔离级别进行交易。对于此方案,您需要使用“SERIALIZABLE”事务隔离。 在您的交易中尝试以下操作。
@Transactional(isolation=Isolation.SERIALIZABLE)