java按唯一名称查找实体,如果找不到,则创建,填充,然后保存。由于并发性,两个事务保存了两次
我偶然发现了一个问题,我们无法通过锁定策略和事务隔离来解决这个问题。我们有一个应用程序,它将消息推送到活动MQ。这个队列有一个常量侦听器
我们在这个消息中有一个状态报告,应该更新或创建它(如果没有)。报表是按唯一名称(“applicationId”)查询的,但这不是主键,也没有唯一约束
报告随后被填充/更新并保存到数据库中
如果找不到实体,我无法立即保存/持久化实体;如果找不到匹配的报告,我无法填充创建的实体。我必须坚持在用数据填充报告后保存报告。这是因为外部系统将每隔5分钟左右提取一次基础表,然后将其删除
问题是:我在几毫秒内收到了3条相同applicationId的传入消息。在前两条传入消息之后,数据库中有两个报告具有相同的applicationId,第三个更新请求失败,因为find查询需要一个结果
这是代码的摘录:
@Override
public Report saveReportinDB(APPLICATION application)
{
// check if already exists by key applicationId
Report report = this.reportRepository.findOneByApplicationID(application.getAppId());
if (report == null)
{
report = new Report();
}
/*this takes longer, so the next application is comming
in and does not find a report with the same applicationId
within the db because the report which is filled
at the moment is not yet persisted*/
fillReportWithData(application, report);
report = ReportRepository.save(report);
有没有办法解决这个问题
# 1 楼答案
在“查找是否使用了应用程序id”和“完成保存应用程序id的工作”之间运行的代码需要是原子代码,即位于两台服务器共用的锁内
就我个人而言,我会尝试让数据库服务于该功能(通过在“挂起”状态下写入应用程序记录),但如果你做不到这一点,那么在应用程序id上键入一个单独的锁记录似乎是合适的