Skip to content

Commit fac3ca1

Browse files
committed
Add version to delete sql for optimistic locking
Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
1 parent 08c4cb1 commit fac3ca1

File tree

4 files changed

+37
-8
lines changed

4 files changed

+37
-8
lines changed

spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/jdbc/JdbcJobExecutionDao.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
* @author Dimitrios Liapis
7878
* @author Philippe Marschall
7979
* @author Jinwoo Bae
80+
* @author Yanming Zhou
8081
*/
8182
public class JdbcJobExecutionDao extends AbstractJdbcBatchMetadataDao implements JobExecutionDao, InitializingBean {
8283

@@ -149,7 +150,7 @@ SELECT COUNT(*)
149150

150151
private static final String DELETE_JOB_EXECUTION = """
151152
DELETE FROM %PREFIX%JOB_EXECUTION
152-
WHERE JOB_EXECUTION_ID = ?
153+
WHERE JOB_EXECUTION_ID = ? AND VERSION = ?
153154
""";
154155

155156
private static final String DELETE_JOB_EXECUTION_PARAMETERS = """
@@ -398,7 +399,13 @@ public void synchronizeStatus(JobExecution jobExecution) {
398399
*/
399400
@Override
400401
public void deleteJobExecution(JobExecution jobExecution) {
401-
getJdbcTemplate().update(getQuery(DELETE_JOB_EXECUTION), jobExecution.getId());
402+
int count = getJdbcTemplate().update(getQuery(DELETE_JOB_EXECUTION), jobExecution.getId(),
403+
jobExecution.getVersion());
404+
405+
if (count == 0) {
406+
throw new OptimisticLockingFailureException("Attempt to delete job execution id=" + jobExecution.getId()
407+
+ " with wrong version (" + jobExecution.getVersion() + ")");
408+
}
402409
}
403410

404411
/**

spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/jdbc/JdbcJobInstanceDao.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.springframework.beans.factory.InitializingBean;
3434
import org.springframework.dao.DataAccessException;
3535
import org.springframework.dao.EmptyResultDataAccessException;
36+
import org.springframework.dao.OptimisticLockingFailureException;
3637
import org.springframework.jdbc.core.ResultSetExtractor;
3738
import org.springframework.jdbc.core.RowMapper;
3839
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
@@ -55,6 +56,7 @@
5556
* @author Will Schipp
5657
* @author Mahmoud Ben Hassine
5758
* @author Parikshit Dutta
59+
* @author Yanming Zhou
5860
*/
5961
public class JdbcJobInstanceDao extends AbstractJdbcBatchMetadataDao implements JobInstanceDao, InitializingBean {
6062

@@ -120,7 +122,7 @@ SELECT COUNT(*)
120122

121123
private static final String DELETE_JOB_INSTANCE = """
122124
DELETE FROM %PREFIX%JOB_INSTANCE
123-
WHERE JOB_INSTANCE_ID = ?
125+
WHERE JOB_INSTANCE_ID = ? AND VERSION = ?
124126
""";
125127

126128
private DataFieldMaxValueIncrementer jobInstanceIncrementer;
@@ -281,7 +283,13 @@ public long getJobInstanceCount(@Nullable String jobName) throws NoSuchJobExcept
281283
*/
282284
@Override
283285
public void deleteJobInstance(JobInstance jobInstance) {
284-
getJdbcTemplate().update(getQuery(DELETE_JOB_INSTANCE), jobInstance.getId());
286+
int count = getJdbcTemplate().update(getQuery(DELETE_JOB_INSTANCE), jobInstance.getId(),
287+
jobInstance.getVersion());
288+
289+
if (count == 0) {
290+
throw new OptimisticLockingFailureException("Attempt to delete job instance id=" + jobInstance.getId()
291+
+ " with wrong version (" + jobInstance.getVersion() + ")");
292+
}
285293
}
286294

287295
/**

spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/jdbc/JdbcStepExecutionDao.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
* @author Mahmoud Ben Hassine
6969
* @author Baris Cubukcuoglu
7070
* @author Minsoo Kim
71+
* @author Yanming Zhou
7172
* @see StepExecutionDao
7273
*/
7374
public class JdbcStepExecutionDao extends AbstractJdbcBatchMetadataDao implements StepExecutionDao, InitializingBean {
@@ -116,7 +117,7 @@ SELECT COUNT(*)
116117

117118
private static final String DELETE_STEP_EXECUTION = """
118119
DELETE FROM %PREFIX%STEP_EXECUTION
119-
WHERE STEP_EXECUTION_ID = ?
120+
WHERE STEP_EXECUTION_ID = ? and VERSION = ?
120121
""";
121122

122123
private static final Comparator<StepExecution> BY_CREATE_TIME_DESC_ID_DESC = Comparator
@@ -380,7 +381,13 @@ public long countStepExecutions(JobInstance jobInstance, String stepName) {
380381
*/
381382
@Override
382383
public void deleteStepExecution(StepExecution stepExecution) {
383-
getJdbcTemplate().update(getQuery(DELETE_STEP_EXECUTION), stepExecution.getId());
384+
int count = getJdbcTemplate().update(getQuery(DELETE_STEP_EXECUTION), stepExecution.getId(),
385+
stepExecution.getVersion());
386+
387+
if (count == 0) {
388+
throw new OptimisticLockingFailureException("Attempt to delete step execution id=" + stepExecution.getId()
389+
+ " with wrong version (" + stepExecution.getVersion() + ")");
390+
}
384391
}
385392

386393
private static class StepExecutionRowMapper implements RowMapper<StepExecution> {

spring-batch-test/src/main/java/org/springframework/batch/test/JobRepositoryTestUtils.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2023 the original author or authors.
2+
* Copyright 2006-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@
3030
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
3131
import org.springframework.batch.core.repository.JobRepository;
3232
import org.springframework.batch.core.repository.JobRestartException;
33+
import org.springframework.dao.OptimisticLockingFailureException;
3334
import org.springframework.lang.Nullable;
3435

3536
/**
@@ -39,6 +40,7 @@
3940
*
4041
* @author Dave Syer
4142
* @author Mahmoud Ben Hassine
43+
* @author Yanming Zhou
4244
*/
4345
public class JobRepositoryTestUtils {
4446

@@ -136,7 +138,12 @@ public void removeJobExecutions(Collection<JobExecution> jobExecutions) {
136138
removeJobExecution(jobExecution);
137139
}
138140
for (JobExecution jobExecution : jobExecutions) {
139-
this.jobRepository.deleteJobInstance(jobExecution.getJobInstance());
141+
try {
142+
this.jobRepository.deleteJobInstance(jobExecution.getJobInstance());
143+
}
144+
catch (OptimisticLockingFailureException ignore) {
145+
// same job instance may be already deleted
146+
}
140147
}
141148
}
142149

0 commit comments

Comments
 (0)