From 9214f9abb2314fa76d91caa03533bafaf3979186 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Tue, 16 Jul 2024 16:01:03 +0200 Subject: [PATCH] Consider sanitised names when copying parameter sources. Closes #1565 --- .../convert/SqlIdentifierParameterSource.java | 2 +- ...SqlIdentifierParameterSourceUnitTests.java | 53 +++++++++++++++++++ ...dbcRepositoryEmbeddedIntegrationTests.java | 23 ++++++++ ...epositoryEmbeddedIntegrationTests-hsql.sql | 9 +++- 4 files changed, 85 insertions(+), 2 deletions(-) diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSource.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSource.java index 4dfcc39a58..2b131ac7a9 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSource.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSource.java @@ -73,7 +73,7 @@ void addAll(SqlIdentifierParameterSource others) { for (SqlIdentifier identifier : others.getIdentifiers()) { - String name = identifier.getReference(); + String name = BindParameterNameSanitizer.sanitize( identifier.getReference()); addValue(identifier, others.getValue(name), others.getSqlType(name)); } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSourceUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSourceUnitTests.java index cfa6b437bc..0c2db589b2 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSourceUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSourceUnitTests.java @@ -61,6 +61,25 @@ void addSingleValue() { }); } + @Test // GH-1565 + void addSingleUnsanitaryValue() { + + SqlIdentifierParameterSource parameters = new SqlIdentifierParameterSource(); + + parameters.addValue(SqlIdentifier.unquoted("ke.y"), 23); + + assertSoftly(softly -> { + + softly.assertThat(parameters.getParameterNames()).isEqualTo(new String[] { "key" }); + softly.assertThat(parameters.getValue("key")).isEqualTo(23); + softly.assertThat(parameters.hasValue("key")).isTrue(); + + softly.assertThat(parameters.getValue("ke.y")).isNull(); + softly.assertThat(parameters.hasValue("ke.y")).isFalse(); + softly.assertThat(parameters.getSqlType("ke.y")).isEqualTo(Integer.MIN_VALUE); + }); + } + @Test // DATAJDBC-386 void addSingleValueWithType() { @@ -114,4 +133,38 @@ void addOtherDatabaseObjectIdentifierParameterSource() { softly.assertThat(parameters.getSqlType("blah")).isEqualTo(Integer.MIN_VALUE); }); } + + @Test // DATAJDBC-386 + void addOtherDatabaseObjectIdentifierParameterSourceWithUnsanitaryValue() { + + SqlIdentifierParameterSource parameters = new SqlIdentifierParameterSource(); + parameters.addValue(SqlIdentifier.unquoted("key1"), 111, 11); + parameters.addValue(SqlIdentifier.unquoted("key2"), 111); + + SqlIdentifierParameterSource parameters2 = new SqlIdentifierParameterSource(); + parameters2.addValue(SqlIdentifier.unquoted("key.2"), 222, 22); + parameters2.addValue(SqlIdentifier.unquoted("key.3"), 222); + + parameters.addAll(parameters2); + + assertSoftly(softly -> { + + softly.assertThat(parameters.getParameterNames()).containsExactlyInAnyOrder("key1", "key2", "key3"); + softly.assertThat(parameters.getValue("key1")).isEqualTo(111); + softly.assertThat(parameters.hasValue("key1")).isTrue(); + softly.assertThat(parameters.getSqlType("key1")).isEqualTo(11); + + softly.assertThat(parameters.getValue("key2")).isEqualTo(222); + softly.assertThat(parameters.hasValue("key2")).isTrue(); + softly.assertThat(parameters.getSqlType("key2")).isEqualTo(22); + + softly.assertThat(parameters.getValue("key3")).isEqualTo(222); + softly.assertThat(parameters.hasValue("key3")).isTrue(); + softly.assertThat(parameters.getSqlType("key3")).isEqualTo(Integer.MIN_VALUE); + + softly.assertThat(parameters.getValue("blah")).isNull(); + softly.assertThat(parameters.hasValue("blah")).isFalse(); + softly.assertThat(parameters.getSqlType("blah")).isEqualTo(Integer.MIN_VALUE); + }); + } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedIntegrationTests.java index 95d457892d..23f4b2b5d3 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedIntegrationTests.java @@ -36,6 +36,7 @@ import org.springframework.data.relational.core.mapping.Embedded.OnEmpty; import org.springframework.data.relational.core.mapping.Table; import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.ListCrudRepository; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.test.jdbc.JdbcTestUtils; @@ -46,6 +47,7 @@ * @author Bastian Wilhelm * @author Christoph Strobl * @author Mikhail Polivakha + * @author Jens Schauder */ @IntegrationTest public class JdbcRepositoryEmbeddedIntegrationTests { @@ -69,12 +71,18 @@ WithDotColumnRepo withDotColumnRepo(JdbcRepositoryFactory factory) { return factory.getRepository(WithDotColumnRepo.class); } + @Bean + WithDotEmbeddedRepo withDotEmbeddedRepo(JdbcRepositoryFactory factory) { + return factory.getRepository(WithDotEmbeddedRepo.class); + } + } @Autowired NamedParameterJdbcTemplate template; @Autowired DummyEntityRepository repository; @Autowired PersonRepository personRepository; @Autowired WithDotColumnRepo withDotColumnRepo; + @Autowired WithDotEmbeddedRepo withDotEmbeddedRepo; @Test // DATAJDBC-111 void savesAnEntity() { @@ -270,6 +278,16 @@ void sortingWorksCorrectlyIfColumnHasDotInItsName() { Assertions.assertThat(fetchedPersons).containsExactly(saved.get(1), saved.get(0), saved.get(2)); } + @Test // GH-1565 + void saveAndLoadEmbeddedWithDottedPrefix() { + WithDotEmbedded entity = withDotEmbeddedRepo.save( + new WithDotEmbedded(null, new PersonContacts("jens@jens.de", "123456789"))); + + WithDotEmbedded reloaded = withDotEmbeddedRepo.findById(entity.id).orElseThrow(); + + assertThat(reloaded).isEqualTo(entity); + } + private static DummyEntity createDummyEntity() { DummyEntity entity = new DummyEntity(); @@ -304,6 +322,11 @@ interface WithDotColumnRepo record WithDotColumn(@Id Integer id, @Column("address.city") String address) { } + record WithDotEmbedded(@Id Integer id, @Embedded.Nullable(prefix = "prefix.") PersonContacts contact) { + } + + interface WithDotEmbeddedRepo extends ListCrudRepository {} + @Table("SORT_EMBEDDED_ENTITY") record Person(@Id Long id, String firstName, String address, @Embedded.Nullable PersonContacts personContacts) { } diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedIntegrationTests-hsql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedIntegrationTests-hsql.sql index b9bdb6c665..57730a5ce7 100644 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedIntegrationTests-hsql.sql +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedIntegrationTests-hsql.sql @@ -20,4 +20,11 @@ CREATE TABLE WITH_DOT_COLUMN ( id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, "address.city" VARCHAR(255) -); \ No newline at end of file +); + +CREATE TABLE WITH_DOT_EMBEDDED +( + ID BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, + "PREFIX.EMAIL" VARCHAR(255), + "PREFIX.PHONE_NUMBER" VARCHAR(255) +) \ No newline at end of file