PostgreSQl正确锁定新实体的创建

问题描述

我有两个并发事务,它们检查是否存在适当的PostgreSQL表记录,如果不存在,请尝试插入一个新的事务。

我有以下Spring Data存储库方法:

@Lock(LockModeType.PESSIMISTIC_WRITE)
TaskApplication findByUserAndTask(User user,Task task);

您可能会看到我在其中添加了@Lock(LockModeType.PESSIMISTIC_WRITE)。在我的服务方法内部,我检查实体是否存在,如果不存在,请创建一个新实体:

@Transactional
public TaskApplication createIfNotExists(User user,Task task) {
    TaskApplication taskApplication = taskApplicationRepository.findByUserAndTask(user,task);
    if (taskApplication == null) {
        taskApplication = create(user,task);
    }
}

我还在tasks_applications (user_id,task_id)字段上添加了唯一约束。

ALTER TABLE public.task_applications 
ADD CONSTRAINT "task_applications-user_id_task_id_unique" 
UNIQUE (user_id,task_id)

自动创建相应的唯一索引:

CREATE UNIQUE INDEX "task_applications-user_id_task_id_unique" 
ON public.task_applications 
USING btree (user_id,task_id)

不幸的是,如果两个并发事务具有相同的user_idtask_id,则第二个事务总是失败,并具有以下异常:

Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "task_applications-user_id_task_id_unique"
  Key (user_id,task_id)=(1,1) already exists.

我在做什么错了,以及如何解决它,以便能够在我的服务方法中处理这种情况?

已更新

我不明白为什么以下方法在提交或回滚第一个事务之前不会阻止第二个事务的执行:

TaskApplication taskApplication = taskApplicationRepository.findByUserAndTask(user,task);

我不确定PostgreSQL,但是通常它应该基于唯一索引在没有记录的情况下阻止执行。

如何实现?

更新2

在执行过程中生成的SQL命令的顺序:

select * from task_applications taskapplic0_ where taskapplic0_.user_id=? and taskapplic0_.task_id=? for update of taskapplic0_
insert into task_applications values (?,...)

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)