diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryCreator.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryCreator.java index cc28ff2f1..bb86ae89a 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryCreator.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryCreator.java @@ -16,9 +16,11 @@ package org.springframework.data.jdbc.repository.query; import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Set; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -60,6 +62,7 @@ import org.springframework.util.Assert; * @author Jens Schauder * @author Myeonghyeon Lee * @author Diego Krupitza + * @author Tomasz Bielecki * @since 2.0 */ class JdbcQueryCreator extends RelationalQueryCreator { @@ -235,7 +238,7 @@ class JdbcQueryCreator extends RelationalQueryCreator { private SelectBuilder.SelectJoin selectBuilder(Table table) { - List columnExpressions = new ArrayList<>(); + Set columnExpressions = new LinkedHashSet<>(); RelationalPersistentEntity entity = entityMetadata.getTableEntity(); SqlContext sqlContext = new SqlContext(entity); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQueryUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQueryUnitTests.java index a941d1830..921dac5fd 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQueryUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQueryUnitTests.java @@ -39,12 +39,14 @@ import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.relational.core.dialect.Escaper; import org.springframework.data.relational.core.dialect.H2Dialect; +import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Embedded; import org.springframework.data.relational.core.mapping.MappedCollection; import org.springframework.data.relational.core.mapping.Table; import org.springframework.data.relational.core.sql.LockMode; import org.springframework.data.relational.repository.Lock; import org.springframework.data.relational.repository.query.RelationalParametersParameterAccessor; +import org.springframework.data.repository.ListCrudRepository; import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; @@ -61,6 +63,7 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; * @author Jens Schauder * @author Myeonghyeon Lee * @author Diego Krupitza + * @author Tomasz Bielecki */ @ExtendWith(MockitoExtension.class) public class PartTreeJdbcQueryUnitTests { @@ -669,6 +672,17 @@ public class PartTreeJdbcQueryUnitTests { .isEqualTo("SELECT COUNT(*) FROM " + TABLE + " WHERE " + TABLE + ".\"FIRST_NAME\" = :first_name"); } + @Test + public void mappingMapKeyToChildShouldNotResultInDuplicateColumn() throws Exception { + Method method = ParentRepository.class.getMethod("findByName", String.class); + var queryMethod = new JdbcQueryMethod(method, new DefaultRepositoryMetadata(ParentRepository.class), + new SpelAwareProxyProjectionFactory(), new PropertiesBasedNamedQueries(new Properties()), mappingContext); + PartTreeJdbcQuery jdbcQuery = createQuery(queryMethod); + ParametrizedQuery query = jdbcQuery.createQuery(getAccessor(queryMethod, new Object[] { "John" }), returnedType); + + assertThat(query.getQuery()).containsOnlyOnce("\"children\".\"NICK_NAME\" AS \"children_NICK_NAME\""); + } + private PartTreeJdbcQuery createQuery(JdbcQueryMethod queryMethod) { return new PartTreeJdbcQuery(mappingContext, queryMethod, JdbcH2Dialect.INSTANCE, converter, mock(NamedParameterJdbcOperations.class), mock(RowMapper.class)); @@ -776,6 +790,19 @@ public class PartTreeJdbcQueryUnitTests { long countByFirstName(String name); } + @NoRepositoryBean + interface ParentRepository extends ListCrudRepository { + Parent findByName(String name); + } + + @Table("parent") + record Parent(@Id String name, @MappedCollection(idColumn = "NICK_NAME") Child children) { + } + + @Table("children") + record Child(@Column("NICK_NAME") String nickName, String name) { + } + @Table("users") static class User {