From 390e46204e33914d83b2d76fb4ab76c5bb270b41 Mon Sep 17 00:00:00 2001 From: Burak KALAYCI Date: Tue, 11 Aug 2026 19:45:32 +0300 Subject: [PATCH 1/2] GH-11248: Make METADATA_VALUE NOT NULL in JdbcMetadataStore schemas Reject null metadata values at the schema level so putIfAbsent cannot misinterpret a null-valued row as a successful insert. Document the non-null contract (including metadata-store.adoc DDL) and cover it with focused tests. Signed-off-by: Burak KALAYCI --- .../jdbc/metadata/JdbcMetadataStore.java | 7 ++++++ .../integration/jdbc/schema-db2.sql | 2 +- .../integration/jdbc/schema-derby.sql | 2 +- .../integration/jdbc/schema-h2.sql | 2 +- .../integration/jdbc/schema-hsqldb.sql | 2 +- .../integration/jdbc/schema-mysql.sql | 2 +- .../integration/jdbc/schema-oracle.sql | 2 +- .../integration/jdbc/schema-postgresql.sql | 2 +- .../integration/jdbc/schema-sqlserver.sql | 2 +- .../integration/jdbc/schema-sybase.sql | 2 +- .../jdbc/metadata/JdbcMetadataStoreTests.java | 25 +++++++++++++++++++ .../ROOT/pages/jdbc/metadata-store.adoc | 5 +++- 12 files changed, 45 insertions(+), 10 deletions(-) diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/metadata/JdbcMetadataStore.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/metadata/JdbcMetadataStore.java index dce5e341c74..87d4f6cf5d7 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/metadata/JdbcMetadataStore.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/metadata/JdbcMetadataStore.java @@ -42,6 +42,12 @@ * org/springframework/integration/jdbc/schema-*.sql, * where * is the target database type. *

+ * The {@code METADATA_VALUE} column is {@code NOT NULL}. Values passed to + * {@link #put(String, String)} and {@link #putIfAbsent(String, String)} must be non-null; + * a {@code null} value cannot be stored and is rejected by the API. + * Existing databases created with older schema scripts should migrate the column to + * {@code NOT NULL} (and remove any rows with a {@code null} value) when upgrading. + *

* The transaction management is required to use this {@link ConcurrentMetadataStore}. *

* This class implements {@link SmartLifecycle} and calls @@ -54,6 +60,7 @@ * @author Artem Bilan * @author Gary Russell * @author Sanghun Lee + * @author Burak Kalayci * * @since 5.0 */ diff --git a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-db2.sql b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-db2.sql index cbb52ccb300..733b435248d 100644 --- a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-db2.sql +++ b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-db2.sql @@ -55,7 +55,7 @@ CREATE INDEX INT_CHANNEL_MSG_DELETE_IDX ON INT_CHANNEL_MESSAGE (REGION, GROUP_KE CREATE TABLE INT_METADATA_STORE ( METADATA_KEY VARCHAR(255) NOT NULL, - METADATA_VALUE VARCHAR(4000), + METADATA_VALUE VARCHAR(4000) NOT NULL, REGION VARCHAR(100) NOT NULL, constraint INT_METADATA_STORE_PK primary key (METADATA_KEY, REGION) ); diff --git a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-derby.sql b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-derby.sql index 8715e59fc40..cef4132f70c 100644 --- a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-derby.sql +++ b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-derby.sql @@ -55,7 +55,7 @@ CREATE INDEX INT_CHANNEL_MSG_DELETE_IDX ON INT_CHANNEL_MESSAGE (REGION, GROUP_KE CREATE TABLE INT_METADATA_STORE ( METADATA_KEY VARCHAR(255) NOT NULL, - METADATA_VALUE VARCHAR(4000), + METADATA_VALUE VARCHAR(4000) NOT NULL, REGION VARCHAR(100) NOT NULL, constraint INT_METADATA_STORE_PK primary key (METADATA_KEY, REGION) ); diff --git a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-h2.sql b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-h2.sql index 692c93fa4f8..d568ff432c9 100644 --- a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-h2.sql +++ b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-h2.sql @@ -55,7 +55,7 @@ CREATE INDEX INT_CHANNEL_MSG_DELETE_IDX ON INT_CHANNEL_MESSAGE (REGION, GROUP_KE CREATE TABLE INT_METADATA_STORE ( METADATA_KEY VARCHAR(255) NOT NULL, - METADATA_VALUE VARCHAR(4000), + METADATA_VALUE VARCHAR(4000) NOT NULL, REGION VARCHAR(100) NOT NULL, constraint INT_METADATA_STORE_PK primary key (METADATA_KEY, REGION) ); diff --git a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-hsqldb.sql b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-hsqldb.sql index 9ab3333e04f..c441f2e5b82 100644 --- a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-hsqldb.sql +++ b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-hsqldb.sql @@ -55,7 +55,7 @@ CREATE INDEX INT_CHANNEL_MSG_DELETE_IDX ON INT_CHANNEL_MESSAGE (REGION, GROUP_KE CREATE TABLE INT_METADATA_STORE ( METADATA_KEY VARCHAR(255) NOT NULL, - METADATA_VALUE VARCHAR(4000), + METADATA_VALUE VARCHAR(4000) NOT NULL, REGION VARCHAR(100) NOT NULL, constraint INT_METADATA_STORE_PK primary key (METADATA_KEY, REGION) ); diff --git a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-mysql.sql b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-mysql.sql index 6920f840e79..1c2f735ce69 100644 --- a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-mysql.sql +++ b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-mysql.sql @@ -55,7 +55,7 @@ CREATE INDEX INT_CHANNEL_MSG_DELETE_IDX ON INT_CHANNEL_MESSAGE (REGION, GROUP_KE CREATE TABLE INT_METADATA_STORE ( METADATA_KEY VARCHAR(255) NOT NULL, - METADATA_VALUE VARCHAR(4000), + METADATA_VALUE VARCHAR(4000) NOT NULL, REGION VARCHAR(100) NOT NULL, constraint INT_METADATA_STORE_PK primary key (METADATA_KEY, REGION) ) ENGINE=InnoDB; diff --git a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-oracle.sql b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-oracle.sql index 1945c597d1f..e78ba733006 100644 --- a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-oracle.sql +++ b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-oracle.sql @@ -54,7 +54,7 @@ CREATE INDEX INT_CHANNEL_MSG_DELETE_IDX ON INT_CHANNEL_MESSAGE (REGION, GROUP_KE CREATE TABLE INT_METADATA_STORE ( METADATA_KEY VARCHAR2(255) NOT NULL, - METADATA_VALUE VARCHAR2(4000), + METADATA_VALUE VARCHAR2(4000) NOT NULL, REGION VARCHAR2(100) NOT NULL, constraint INT_METADATA_STORE_PK primary key (METADATA_KEY, REGION) ); diff --git a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-postgresql.sql b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-postgresql.sql index 2b4ba9bf075..e74f7f8643d 100644 --- a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-postgresql.sql +++ b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-postgresql.sql @@ -55,7 +55,7 @@ CREATE INDEX INT_CHANNEL_MSG_DELETE_IDX ON INT_CHANNEL_MESSAGE (REGION, GROUP_KE CREATE TABLE INT_METADATA_STORE ( METADATA_KEY VARCHAR(255) NOT NULL, - METADATA_VALUE VARCHAR(4000), + METADATA_VALUE VARCHAR(4000) NOT NULL, REGION VARCHAR(100) NOT NULL, constraint INT_METADATA_STORE_PK primary key (METADATA_KEY, REGION) ); diff --git a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-sqlserver.sql b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-sqlserver.sql index bb6d93cfc9a..bb2a95efba4 100644 --- a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-sqlserver.sql +++ b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-sqlserver.sql @@ -55,7 +55,7 @@ CREATE INDEX INT_CHANNEL_MSG_DELETE_IDX ON INT_CHANNEL_MESSAGE (REGION, GROUP_KE CREATE TABLE INT_METADATA_STORE ( METADATA_KEY NVARCHAR(255) NOT NULL, - METADATA_VALUE NVARCHAR(4000), + METADATA_VALUE NVARCHAR(4000) NOT NULL, REGION NVARCHAR(100) NOT NULL, constraint INT_METADATA_STORE_PK primary key (METADATA_KEY, REGION) ); diff --git a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-sybase.sql b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-sybase.sql index 9096f08ced3..d5a22569c10 100644 --- a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-sybase.sql +++ b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/schema-sybase.sql @@ -55,7 +55,7 @@ CREATE INDEX INT_CHANNEL_MSG_DELETE_IDX ON INT_CHANNEL_MESSAGE (REGION, GROUP_KE CREATE TABLE INT_METADATA_STORE ( METADATA_KEY VARCHAR(255) NOT NULL, - METADATA_VALUE VARCHAR(4000), + METADATA_VALUE VARCHAR(4000) NOT NULL, REGION VARCHAR(100) NOT NULL, constraint INT_METADATA_STORE_PK primary key (METADATA_KEY, REGION) ) LOCK DATAROWS; diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/metadata/JdbcMetadataStoreTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/metadata/JdbcMetadataStoreTests.java index 7e52c4754d3..7e9b05403fa 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/metadata/JdbcMetadataStoreTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/metadata/JdbcMetadataStoreTests.java @@ -26,7 +26,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContextException; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.integration.test.util.TestUtils; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import org.springframework.transaction.annotation.Transactional; @@ -124,4 +126,27 @@ void noTableThrowsExceptionOnStart() { } } + @Test + void metadataValueColumnRejectsNull() { + JdbcTemplate jdbcTemplate = new JdbcTemplate(this.dataSource); + assertThatExceptionOfType(DataIntegrityViolationException.class) + .isThrownBy(() -> jdbcTemplate.update( + "INSERT INTO INT_METADATA_STORE(METADATA_KEY, METADATA_VALUE, REGION) VALUES (?, ?, ?)", + "nullValued", null, "DEFAULT")); + } + + @Test + void putIfAbsentRejectsNullValue() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> this.metadataStore.putIfAbsent("foo", null)) + .withMessageContaining("value"); + } + + @Test + void putRejectsNullValue() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> this.metadataStore.put("foo", null)) + .withMessageContaining("value"); + } + } diff --git a/src/reference/antora/modules/ROOT/pages/jdbc/metadata-store.adoc b/src/reference/antora/modules/ROOT/pages/jdbc/metadata-store.adoc index c22c9ff374b..ca9dd06488f 100644 --- a/src/reference/antora/modules/ROOT/pages/jdbc/metadata-store.adoc +++ b/src/reference/antora/modules/ROOT/pages/jdbc/metadata-store.adoc @@ -29,12 +29,15 @@ For example, the following listing shows the H2 DDL for the metadata table: ---- CREATE TABLE INT_METADATA_STORE ( METADATA_KEY VARCHAR(255) NOT NULL, - METADATA_VALUE VARCHAR(4000), + METADATA_VALUE VARCHAR(4000) NOT NULL, REGION VARCHAR(100) NOT NULL, constraint INT_METADATA_STORE_PK primary key (METADATA_KEY, REGION) ); ---- +`METADATA_VALUE` is non-null (since 7.2). +Existing databases created with older scripts should remove any null-valued rows and alter the column to `NOT NULL` when upgrading. + You can change the `INT_` prefix to match the target database design requirements. You can also configure `JdbcMetadataStore` to use the custom prefix. From 701b09bc26e8c1b8e58e9470129fd62d3faf89b3 Mon Sep 17 00:00:00 2001 From: Burak KALAYCI Date: Wed, 12 Aug 2026 00:10:11 +0300 Subject: [PATCH 2/2] GH-11248: Address review on docs and tests Move METADATA_VALUE NOT NULL note to whats-new, drop foo keys in new tests, and add author on JdbcMetadataStoreTests. Signed-off-by: Burak KALAYCI --- .../integration/jdbc/metadata/JdbcMetadataStoreTests.java | 5 +++-- .../antora/modules/ROOT/pages/jdbc/metadata-store.adoc | 3 --- src/reference/antora/modules/ROOT/pages/whats-new.adoc | 7 +++++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/metadata/JdbcMetadataStoreTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/metadata/JdbcMetadataStoreTests.java index 7e9b05403fa..e05e66d8381 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/metadata/JdbcMetadataStoreTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/metadata/JdbcMetadataStoreTests.java @@ -39,6 +39,7 @@ /** * @author Bojan Vukasovic * @author Artem Bilan + * @author Burak Kalayci * * @since 5.0 */ @@ -138,14 +139,14 @@ void metadataValueColumnRejectsNull() { @Test void putIfAbsentRejectsNullValue() { assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> this.metadataStore.putIfAbsent("foo", null)) + .isThrownBy(() -> this.metadataStore.putIfAbsent("testKey", null)) .withMessageContaining("value"); } @Test void putRejectsNullValue() { assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> this.metadataStore.put("foo", null)) + .isThrownBy(() -> this.metadataStore.put("testKey", null)) .withMessageContaining("value"); } diff --git a/src/reference/antora/modules/ROOT/pages/jdbc/metadata-store.adoc b/src/reference/antora/modules/ROOT/pages/jdbc/metadata-store.adoc index ca9dd06488f..f588b8a22fb 100644 --- a/src/reference/antora/modules/ROOT/pages/jdbc/metadata-store.adoc +++ b/src/reference/antora/modules/ROOT/pages/jdbc/metadata-store.adoc @@ -35,9 +35,6 @@ CREATE TABLE INT_METADATA_STORE ( ); ---- -`METADATA_VALUE` is non-null (since 7.2). -Existing databases created with older scripts should remove any null-valued rows and alter the column to `NOT NULL` when upgrading. - You can change the `INT_` prefix to match the target database design requirements. You can also configure `JdbcMetadataStore` to use the custom prefix. diff --git a/src/reference/antora/modules/ROOT/pages/whats-new.adoc b/src/reference/antora/modules/ROOT/pages/whats-new.adoc index 46d2c5b2b32..d9b8953fdfd 100644 --- a/src/reference/antora/modules/ROOT/pages/whats-new.adoc +++ b/src/reference/antora/modules/ROOT/pages/whats-new.adoc @@ -30,3 +30,10 @@ The `wait-for-confirm` option on the outbound channel adapter now also works wit When the message has no correlation data, the send is performed within the scope of a `RabbitTemplate.invoke()` operation and `waitForConfirmsOrDie()` is used. A warning is now logged when the option is set but the connection factory supports no publisher confirms at all. See xref:amqp/outbound-channel-adapter.adoc[] for more information. + +[[x7.2-jdbc-changes]] +=== JDBC Support Changes + +The `METADATA_VALUE` column in the `JdbcMetadataStore` schema scripts is now `NOT NULL`. +Existing databases created with older scripts should remove any null-valued rows and alter the column to `NOT NULL` when upgrading. +See xref:jdbc/metadata-store.adoc[] for more information.