From 230422fd192b4d13d3b55d093020d9a997f416d7 Mon Sep 17 00:00:00 2001
From: Yanming Zhou <zhouyanming@gmail.com>
Date: Wed, 26 Mar 2025 15:58:05 +0800
Subject: [PATCH] Improve update sql for optimistic locking

The version to be updated could be computed at server side instead of client side, it will save one parameter of prepared statement.

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
---
 .../batch/core/repository/dao/JdbcJobExecutionDao.java |  8 ++++----
 .../core/repository/dao/JdbcStepExecutionDao.java      | 10 +++++-----
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java
index 9ec0a9e2d8..df456b50f5 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java
@@ -74,6 +74,7 @@
  * @author Dimitrios Liapis
  * @author Philippe Marschall
  * @author Jinwoo Bae
+ * @author Yanming Zhou
  */
 public class JdbcJobExecutionDao extends AbstractJdbcBatchMetadataDao implements JobExecutionDao, InitializingBean {
 
@@ -98,7 +99,7 @@ SELECT COUNT(*)
 
 	private static final String UPDATE_JOB_EXECUTION = """
 			UPDATE %PREFIX%JOB_EXECUTION
-			SET START_TIME = ?, END_TIME = ?,  STATUS = ?, EXIT_CODE = ?, EXIT_MESSAGE = ?, VERSION = ?, CREATE_TIME = ?, LAST_UPDATED = ?
+			SET START_TIME = ?, END_TIME = ?,  STATUS = ?, EXIT_CODE = ?, EXIT_MESSAGE = ?, VERSION = VERSION + 1, CREATE_TIME = ?, LAST_UPDATED = ?
 			WHERE JOB_EXECUTION_ID = ? AND VERSION = ?
 			""";
 
@@ -284,7 +285,6 @@ public void updateJobExecution(JobExecution jobExecution) {
 
 		this.lock.lock();
 		try {
-			Integer version = jobExecution.getVersion() + 1;
 
 			String exitDescription = jobExecution.getExitStatus().getExitDescription();
 			if (exitDescription != null && exitDescription.length() > exitMessageLength) {
@@ -301,7 +301,7 @@ public void updateJobExecution(JobExecution jobExecution) {
 			Timestamp lastUpdated = jobExecution.getLastUpdated() == null ? null
 					: Timestamp.valueOf(jobExecution.getLastUpdated());
 			Object[] parameters = new Object[] { startTime, endTime, jobExecution.getStatus().toString(),
-					jobExecution.getExitStatus().getExitCode(), exitDescription, version, createTime, lastUpdated,
+					jobExecution.getExitStatus().getExitCode(), exitDescription, createTime, lastUpdated,
 					jobExecution.getId(), jobExecution.getVersion() };
 
 			// Check if given JobExecution's Id already exists, if none is found
@@ -315,7 +315,7 @@ public void updateJobExecution(JobExecution jobExecution) {
 
 			int count = getJdbcTemplate().update(getQuery(UPDATE_JOB_EXECUTION), parameters,
 					new int[] { Types.TIMESTAMP, Types.TIMESTAMP, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
-							Types.INTEGER, Types.TIMESTAMP, Types.TIMESTAMP, Types.BIGINT, Types.INTEGER });
+							Types.TIMESTAMP, Types.TIMESTAMP, Types.BIGINT, Types.INTEGER });
 
 			// Avoid concurrent modifications...
 			if (count == 0) {
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcStepExecutionDao.java b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcStepExecutionDao.java
index b1e46e0c23..62a75673f5 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcStepExecutionDao.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcStepExecutionDao.java
@@ -66,6 +66,7 @@
  * @author Mahmoud Ben Hassine
  * @author Baris Cubukcuoglu
  * @author Minsoo Kim
+ * @author Yanming Zhou
  * @see StepExecutionDao
  */
 public class JdbcStepExecutionDao extends AbstractJdbcBatchMetadataDao implements StepExecutionDao, InitializingBean {
@@ -79,7 +80,7 @@ public class JdbcStepExecutionDao extends AbstractJdbcBatchMetadataDao implement
 
 	private static final String UPDATE_STEP_EXECUTION = """
 			UPDATE %PREFIX%STEP_EXECUTION
-			SET START_TIME = ?, END_TIME = ?, STATUS = ?, COMMIT_COUNT = ?, READ_COUNT = ?, FILTER_COUNT = ?, WRITE_COUNT = ?, EXIT_CODE = ?, EXIT_MESSAGE = ?, VERSION = ?, READ_SKIP_COUNT = ?, PROCESS_SKIP_COUNT = ?, WRITE_SKIP_COUNT = ?, ROLLBACK_COUNT = ?, LAST_UPDATED = ?
+			SET START_TIME = ?, END_TIME = ?, STATUS = ?, COMMIT_COUNT = ?, READ_COUNT = ?, FILTER_COUNT = ?, WRITE_COUNT = ?, EXIT_CODE = ?, EXIT_MESSAGE = ?, VERSION = VERSION + 1, READ_SKIP_COUNT = ?, PROCESS_SKIP_COUNT = ?, WRITE_SKIP_COUNT = ?, ROLLBACK_COUNT = ?, LAST_UPDATED = ?
 			WHERE STEP_EXECUTION_ID = ? AND VERSION = ?
 			""";
 
@@ -267,7 +268,6 @@ public void updateStepExecution(StepExecution stepExecution) {
 		this.lock.lock();
 		try {
 
-			Integer version = stepExecution.getVersion() + 1;
 			Timestamp startTime = stepExecution.getStartTime() == null ? null
 					: Timestamp.valueOf(stepExecution.getStartTime());
 			Timestamp endTime = stepExecution.getEndTime() == null ? null
@@ -277,13 +277,13 @@ public void updateStepExecution(StepExecution stepExecution) {
 			Object[] parameters = new Object[] { startTime, endTime, stepExecution.getStatus().toString(),
 					stepExecution.getCommitCount(), stepExecution.getReadCount(), stepExecution.getFilterCount(),
 					stepExecution.getWriteCount(), stepExecution.getExitStatus().getExitCode(), exitDescription,
-					version, stepExecution.getReadSkipCount(), stepExecution.getProcessSkipCount(),
+					stepExecution.getReadSkipCount(), stepExecution.getProcessSkipCount(),
 					stepExecution.getWriteSkipCount(), stepExecution.getRollbackCount(), lastUpdated,
 					stepExecution.getId(), stepExecution.getVersion() };
 			int count = getJdbcTemplate().update(getQuery(UPDATE_STEP_EXECUTION), parameters,
 					new int[] { Types.TIMESTAMP, Types.TIMESTAMP, Types.VARCHAR, Types.BIGINT, Types.BIGINT,
-							Types.BIGINT, Types.BIGINT, Types.VARCHAR, Types.VARCHAR, Types.INTEGER, Types.BIGINT,
-							Types.BIGINT, Types.BIGINT, Types.BIGINT, Types.TIMESTAMP, Types.BIGINT, Types.INTEGER });
+							Types.BIGINT, Types.BIGINT, Types.VARCHAR, Types.VARCHAR, Types.BIGINT, Types.BIGINT,
+							Types.BIGINT, Types.BIGINT, Types.TIMESTAMP, Types.BIGINT, Types.INTEGER });
 
 			// Avoid concurrent modifications...
 			if (count == 0) {