diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryUtils.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryUtils.java index 2ae66e4388..e25429652b 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryUtils.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlPagingQueryUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2024 the original author or authors. + * Copyright 2006-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,6 +32,7 @@ * @author Michael Minella * @author Mahmoud Ben Hassine * @author Taeik Lim + * @author Kyeonghoon Lee * @since 2.0 */ public abstract class SqlPagingQueryUtils { @@ -108,8 +109,8 @@ public static String generateLimitGroupedSqlQuery(AbstractSqlPagingQueryProvider buildGroupByClause(provider, sql); sql.append(") AS MAIN_QRY "); sql.append("WHERE "); - buildSortConditions(provider, sql); - sql.append(" ORDER BY ").append(buildSortClause(provider)); + buildSortConditionsWithoutTableAliases(provider, sql); + sql.append(" ORDER BY ").append(buildSortClause(provider.getSortKeysWithoutAliases())); sql.append(" ").append(limitClause); return sql.toString(); @@ -233,7 +234,7 @@ public static String generateRowNumSqlQuery(AbstractSqlPagingQueryProvider provi /** * Generates ORDER BY attributes based on the sort keys. - * @param provider the {@link AbstractSqlPagingQueryProvider} to be used for used for + * @param provider the {@link AbstractSqlPagingQueryProvider} to be used for * pagination. * @return a String that can be appended to an ORDER BY clause. */ @@ -269,6 +270,19 @@ public static String buildSortClause(Map sortKeys) { return builder.toString(); } + /** + * Appends the where conditions required to query for the subsequent pages, without + * using table aliases in sort keys. + * @param provider the {@link AbstractSqlPagingQueryProvider} to be used for + * pagination. + * @param sql {@link StringBuilder} containing the sql to be used for the query. + */ + public static void buildSortConditionsWithoutTableAliases(AbstractSqlPagingQueryProvider provider, + StringBuilder sql) { + List> keys = new ArrayList<>(provider.getSortKeysWithoutAliases().entrySet()); + buildDetailSortConditions(keys, provider, sql); + } + /** * Appends the where conditions required to query for the subsequent pages. * @param provider the {@link AbstractSqlPagingQueryProvider} to be used for @@ -277,6 +291,11 @@ public static String buildSortClause(Map sortKeys) { */ public static void buildSortConditions(AbstractSqlPagingQueryProvider provider, StringBuilder sql) { List> keys = new ArrayList<>(provider.getSortKeys().entrySet()); + buildDetailSortConditions(keys, provider, sql); + } + + private static void buildDetailSortConditions(List> keys, + AbstractSqlPagingQueryProvider provider, StringBuilder sql) { List clauses = new ArrayList<>(); for (int i = 0; i < keys.size(); i++) { diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/MariaDBPagingQueryProviderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/MariaDBPagingQueryProviderTests.java index 7921e6da74..53670e13d8 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/MariaDBPagingQueryProviderTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/MariaDBPagingQueryProviderTests.java @@ -16,6 +16,7 @@ package org.springframework.batch.item.database.support; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import org.junit.jupiter.api.Test; @@ -26,6 +27,7 @@ /** * @author Mahmoud Ben Hassine + * @author Kyeonghoon Lee */ class MariaDBPagingQueryProviderTests extends AbstractSqlPagingQueryProviderTests { @@ -67,6 +69,22 @@ void testGenerateRemainingPagesQueryWithGroupBy() { assertEquals(sql, s); } + @Test + void testGenerateRemainingPagesQueryWithGroupByWithAlias() { + pagingQueryProvider.setSelectClause("SELECT f.id, f.name, f.age"); + pagingQueryProvider.setFromClause("FROM foo f"); + pagingQueryProvider.setWhereClause("f.bar = 1"); + pagingQueryProvider.setGroupClause("f.id, f.dep"); + Map sortKeys = new LinkedHashMap<>(); + sortKeys.put("f.id", Order.ASCENDING); + pagingQueryProvider.setSortKeys(sortKeys); + + String sql = "SELECT * FROM (SELECT f.id, f.name, f.age FROM foo f WHERE f.bar = 1 GROUP BY f.id, f.dep) AS MAIN_QRY WHERE ((id > ?)) ORDER BY id ASC LIMIT " + + pageSize; + String s = pagingQueryProvider.generateRemainingPagesQuery(pageSize); + assertEquals(sql, s); + } + @Test void testFirstPageSqlWithAliases() { Map sorts = new HashMap<>(); diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/MySqlPagingQueryProviderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/MySqlPagingQueryProviderTests.java index 5c3280a5f9..9b4cc6930b 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/MySqlPagingQueryProviderTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/MySqlPagingQueryProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2022 the original author or authors. + * Copyright 2006-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.LinkedHashMap; import org.junit.jupiter.api.Test; @@ -27,6 +28,7 @@ /** * @author Thomas Risberg * @author Michael Minella + * @author Kyeonghoon Lee */ class MySqlPagingQueryProviderTests extends AbstractSqlPagingQueryProviderTests { @@ -68,6 +70,22 @@ void testGenerateRemainingPagesQueryWithGroupBy() { assertEquals(sql, s); } + @Test + void testGenerateRemainingPagesQueryWithGroupByWithAlias() { + pagingQueryProvider.setSelectClause("SELECT f.id, f.name, f.age"); + pagingQueryProvider.setFromClause("FROM foo f"); + pagingQueryProvider.setWhereClause("f.bar = 1"); + pagingQueryProvider.setGroupClause("f.id, f.dep"); + Map sortKeys = new LinkedHashMap<>(); + sortKeys.put("f.id", Order.ASCENDING); + pagingQueryProvider.setSortKeys(sortKeys); + + String sql = "SELECT * FROM (SELECT f.id, f.name, f.age FROM foo f WHERE f.bar = 1 GROUP BY f.id, f.dep) AS MAIN_QRY WHERE ((id > ?)) ORDER BY id ASC LIMIT " + + pageSize; + String s = pagingQueryProvider.generateRemainingPagesQuery(pageSize); + assertEquals(sql, s); + } + @Test void testFirstPageSqlWithAliases() { Map sorts = new HashMap<>(); diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/PostgresPagingQueryProviderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/PostgresPagingQueryProviderTests.java index 6ad8a6544b..cbe5675544 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/PostgresPagingQueryProviderTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/PostgresPagingQueryProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,15 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; +import org.springframework.batch.item.database.Order; + +import java.util.LinkedHashMap; +import java.util.Map; /** * @author Thomas Risberg * @author Michael Minella + * @author Kyeonghoon Lee */ class PostgresPagingQueryProviderTests extends AbstractSqlPagingQueryProviderTests { @@ -63,6 +68,22 @@ void testGenerateRemainingPagesQueryWithGroupBy() { assertEquals(sql, s); } + @Test + void testGenerateRemainingPagesQueryWithGroupByWithAlias() { + pagingQueryProvider.setSelectClause("SELECT f.id, f.name, f.age"); + pagingQueryProvider.setFromClause("FROM foo f"); + pagingQueryProvider.setWhereClause("f.bar = 1"); + pagingQueryProvider.setGroupClause("f.id, f.dep"); + Map sortKeys = new LinkedHashMap<>(); + sortKeys.put("f.id", Order.ASCENDING); + pagingQueryProvider.setSortKeys(sortKeys); + + String sql = "SELECT * FROM (SELECT f.id, f.name, f.age FROM foo f WHERE f.bar = 1 GROUP BY f.id, f.dep) AS MAIN_QRY WHERE ((id > ?)) ORDER BY id ASC LIMIT " + + pageSize; + String s = pagingQueryProvider.generateRemainingPagesQuery(pageSize); + assertEquals(sql, s); + } + @Override String getFirstPageSqlWithMultipleSortKeys() { return "SELECT id, name, age FROM foo WHERE bar = 1 ORDER BY name ASC, id DESC LIMIT 100"; diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlitePagingQueryProviderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlitePagingQueryProviderTests.java index 16cf9c3148..425c3ca121 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlitePagingQueryProviderTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlitePagingQueryProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,16 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; +import org.springframework.batch.item.database.Order; + +import java.util.LinkedHashMap; +import java.util.Map; /** * @author Thomas Risberg * @author Michael Minella * @author Luke Taylor + * @author Kyeonghoon Lee */ class SqlitePagingQueryProviderTests extends AbstractSqlPagingQueryProviderTests { @@ -64,6 +69,22 @@ void testGenerateRemainingPagesQueryWithGroupBy() { assertEquals(sql, s); } + @Test + void testGenerateRemainingPagesQueryWithGroupByWithAlias() { + pagingQueryProvider.setSelectClause("SELECT f.id, f.name, f.age"); + pagingQueryProvider.setFromClause("FROM foo f"); + pagingQueryProvider.setWhereClause("f.bar = 1"); + pagingQueryProvider.setGroupClause("dep"); + Map sortKeys = new LinkedHashMap<>(); + sortKeys.put("f.id", Order.ASCENDING); + pagingQueryProvider.setSortKeys(sortKeys); + + String sql = "SELECT * FROM (SELECT f.id, f.name, f.age FROM foo f WHERE f.bar = 1 GROUP BY dep) AS MAIN_QRY WHERE ((id > ?)) ORDER BY id ASC LIMIT " + + pageSize; + String s = pagingQueryProvider.generateRemainingPagesQuery(pageSize); + assertEquals(sql, s); + } + @Override String getFirstPageSqlWithMultipleSortKeys() { return "SELECT id, name, age FROM foo WHERE bar = 1 ORDER BY name ASC, id DESC LIMIT 100";