Skip to content

Commit 9f1c66f

Browse files
artembilangaryrussell
authored andcommitted
GH-3282: Fix JdbcMetadataStore for DuplicateKeyEx (#3285)
* GH-3282: Fix JdbcMetadataStore for DuplicateKeyEx Fixes: #3282 The `INSERT INTO ... SELECT ... FROM ... HAVING` may fail with `DuplicateKeyException` in between transactions. * Catch `DuplicateKeyException` from in the `tryToPutIfAbsent()` and return `0` as a fact of not inserted to let other logic to work as expected. There is no test coverage for this since it is almost impossible to reproduce such a race condition on DB **Cherry-pick to 5.3.x & 5.2.x** * * Remove misleading message in the comment sentence
1 parent 619c5bf commit 9f1c66f

File tree

2 files changed

+29
-22
lines changed

2 files changed

+29
-22
lines changed

spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/metadata/JdbcMetadataStore.java

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import javax.sql.DataSource;
2020

2121
import org.springframework.beans.factory.InitializingBean;
22+
import org.springframework.dao.DuplicateKeyException;
2223
import org.springframework.dao.EmptyResultDataAccessException;
2324
import org.springframework.integration.metadata.ConcurrentMetadataStore;
2425
import org.springframework.jdbc.core.JdbcOperations;
@@ -59,16 +60,20 @@ public class JdbcMetadataStore implements ConcurrentMetadataStore, InitializingB
5960

6061
private String getValueQuery = "SELECT METADATA_VALUE FROM %sMETADATA_STORE WHERE METADATA_KEY=? AND REGION=?";
6162

62-
private String getValueForUpdateQuery = "SELECT METADATA_VALUE FROM %sMETADATA_STORE WHERE METADATA_KEY=? AND REGION=? %s";
63+
private String getValueForUpdateQuery =
64+
"SELECT METADATA_VALUE FROM %sMETADATA_STORE WHERE METADATA_KEY=? AND REGION=? %s";
6365

64-
private String replaceValueQuery = "UPDATE %sMETADATA_STORE SET METADATA_VALUE=? WHERE METADATA_KEY=? AND METADATA_VALUE=? AND REGION=?";
66+
private String replaceValueQuery =
67+
"UPDATE %sMETADATA_STORE SET METADATA_VALUE=? WHERE METADATA_KEY=? AND METADATA_VALUE=? AND REGION=?";
6568

66-
private String replaceValueByKeyQuery = "UPDATE %sMETADATA_STORE SET METADATA_VALUE=? WHERE METADATA_KEY=? AND REGION=?";
69+
private String replaceValueByKeyQuery =
70+
"UPDATE %sMETADATA_STORE SET METADATA_VALUE=? WHERE METADATA_KEY=? AND REGION=?";
6771

6872
private String removeValueQuery = "DELETE FROM %sMETADATA_STORE WHERE METADATA_KEY=? AND REGION=?";
6973

70-
private String putIfAbsentValueQuery = "INSERT INTO %sMETADATA_STORE(METADATA_KEY, METADATA_VALUE, REGION) "
71-
+ "SELECT ?, ?, ? FROM %sMETADATA_STORE WHERE METADATA_KEY=? AND REGION=? HAVING COUNT(*)=0";
74+
private String putIfAbsentValueQuery =
75+
"INSERT INTO %sMETADATA_STORE(METADATA_KEY, METADATA_VALUE, REGION) "
76+
+ "SELECT ?, ?, ? FROM %sMETADATA_STORE WHERE METADATA_KEY=? AND REGION=? HAVING COUNT(*)=0";
7277

7378
/**
7479
* Instantiate a {@link JdbcMetadataStore} using provided dataSource {@link DataSource}.
@@ -142,7 +147,7 @@ public String putIfAbsent(String key, String value) {
142147
//try to insert if does not exists
143148
int affectedRows = tryToPutIfAbsent(key, value);
144149
if (affectedRows > 0) {
145-
//it was not in the table, so we have just inserted it
150+
//it was not in the table, so we have just inserted
146151
return null;
147152
}
148153
else {
@@ -158,14 +163,19 @@ public String putIfAbsent(String key, String value) {
158163
}
159164

160165
private int tryToPutIfAbsent(String key, String value) {
161-
return this.jdbcTemplate.update(this.putIfAbsentValueQuery,
162-
ps -> {
163-
ps.setString(1, key);
164-
ps.setString(2, value);
165-
ps.setString(3, this.region);
166-
ps.setString(4, key);
167-
ps.setString(5, this.region);
168-
});
166+
try {
167+
return this.jdbcTemplate.update(this.putIfAbsentValueQuery,
168+
ps -> {
169+
ps.setString(1, key);
170+
ps.setString(2, value);
171+
ps.setString(3, this.region);
172+
ps.setString(4, key);
173+
ps.setString(5, this.region);
174+
});
175+
}
176+
catch (DuplicateKeyException ex) {
177+
return 0;
178+
}
169179
}
170180

171181
@Override

spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/metadata/JdbcMetadataStoreTests.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,12 @@
2020

2121
import javax.sql.DataSource;
2222

23-
import org.junit.Before;
24-
import org.junit.Test;
25-
import org.junit.runner.RunWith;
23+
import org.junit.jupiter.api.BeforeEach;
24+
import org.junit.jupiter.api.Test;
2625

2726
import org.springframework.beans.factory.annotation.Autowired;
2827
import org.springframework.test.annotation.DirtiesContext;
29-
import org.springframework.test.context.ContextConfiguration;
30-
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
28+
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
3129
import org.springframework.transaction.annotation.Transactional;
3230

3331
/**
@@ -36,8 +34,7 @@
3634
*
3735
* @since 5.0
3836
*/
39-
@ContextConfiguration
40-
@RunWith(SpringJUnit4ClassRunner.class)
37+
@SpringJUnitConfig
4138
@DirtiesContext // close at the end after class
4239
@Transactional
4340
public class JdbcMetadataStoreTests {
@@ -48,7 +45,7 @@ public class JdbcMetadataStoreTests {
4845

4946
private JdbcMetadataStore metadataStore;
5047

51-
@Before
48+
@BeforeEach
5249
public void init() {
5350
metadataStore = new JdbcMetadataStore(dataSource);
5451
metadataStore.afterPropertiesSet();

0 commit comments

Comments
 (0)