如果查询条件满足JPA和spring则插入行

Sod*_*ved 6 java mysql jpa mariadb spring-data-jpa

我认为这个问题类似于Database pessimistic locks with Spring data JPA (Hibernate under the hood),但我想我会单独问,因为不完全相同。

我在 mariadb 数据库之上有一个多线程/节点 springboot 应用程序,其表如下

CREATE TABLE job (
id      INT PRIMARY KEY AUTO_INCREMENT,
owner   VARCHAR(50),
status  VARCHAR(10) );
Run Code Online (Sandbox Code Playgroud)

拥有一个Job如您所期望的域类。

有一个JobRepository扩展的接口CrudRepository<Job,Integer>和一个服务类。

应用程序规则是,如果所有者和状态值集相同,则我们无法插入新作业。例如,如果这是老式的本机 sql,我只会:

START TRANSACTION;
INSERT INTO job (owner, status)
SELECT 'fred', 'init' FROM DUAL
WHERE NOT EXISTS
(   SELECT 1 FROM job
    WHERE owner = 'fred' AND status IN ('init', 'running')
);
COMMIT;
Run Code Online (Sandbox Code Playgroud)

但如何在 JPA/CrudRepository 中执行此操作。

我分为数据库操作。定义了一个存储库方法:

@Lock(LockModeType.READ)
long countByOwnerAndStatusIn(String owner, List<String> status);
Run Code Online (Sandbox Code Playgroud)

然后有一个类似的服务方法:

@Transactional
public Job createJob(Job job) {
    if (jobRepository.countByOwnerAndStatusIn(job.getOwner(), Job.PROGRESS_STATUS) == 0) {
        // Sleeps just to ensure conflicts
        Thread.sleep(1000);
        Job newJob = jobRepository.save(job);
        Thread.sleep(1000);
        return newJob
    }
    else {
        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

但这样我没有得到预期的效果。

READ 和 WRITE 的 LockModeType 允许创建重复项。

PESSIMISTIC_READ 和 PESSIMISTIC_WRITE 的 LockModeType 可能会导致死锁错误。

所以我想我会选择以下两种选择之一:

  • 有没有办法让 getINSERT...SELECT WHERE NOT EXISTS 进入 JPA/CrudRepository 方法?
  • 有没有办法让服务方法有效地将计数检查和插入包装在同一个锁中?

如果没有办法做到这一点,我想我会尝试访问底层jdbc连接并显式运行一个LOCK TABLE语句(或插入...选择,但我不喜欢这个想法,保留它JPA喜欢可能更好)。

希望我已经正确解释了自己。在此先感谢您的帮助。