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 cc28ff2f18..bb86ae89a9 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 @@ * @author Jens Schauder * @author Myeonghyeon Lee * @author Diego Krupitza + * @author Tomasz Bielecki * @since 2.0 */ class JdbcQueryCreator extends RelationalQueryCreator { @@ -235,7 +238,7 @@ SelectBuilder.SelectLimitOffset createSelectClause(RelationalPersistentEntity 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 27f4a47c29..f23cb60806 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 @@ -38,12 +38,14 @@ 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; @@ -60,6 +62,7 @@ * @author Jens Schauder * @author Myeonghyeon Lee * @author Diego Krupitza + * @author Tomasz Bielecki */ @ExtendWith(MockitoExtension.class) public class PartTreeJdbcQueryUnitTests { @@ -668,6 +671,17 @@ public void createsQueryForCountProjection() throws Exception { .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, H2Dialect.INSTANCE, converter, mock(NamedParameterJdbcOperations.class), mock(RowMapper.class)); @@ -775,6 +789,19 @@ interface UserRepository extends Repository { 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 {