Skip to content

Commit 8f1b24a

Browse files
hpoettkerfmbenhassine
authored andcommitted
Update implementations of PagingQueryProvider
Replaces the implementation of `DerbyPagingQueryProvider` with that corresponding to DB2 and adds an integration test that failed with the previous implementation. Deprecates `SqlWindowingPagingQueryProvider` for removal, which was effectively only used by `DerbyPagingQueryProvider`. Resolves #4733
1 parent e86bd65 commit 8f1b24a

File tree

9 files changed

+151
-151
lines changed

9 files changed

+151
-151
lines changed

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/Db2PagingQueryProvider.java

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2021 the original author or authors.
2+
* Copyright 2006-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@
2727
* @author Mahmoud Ben Hassine
2828
* @since 2.0
2929
*/
30-
public class Db2PagingQueryProvider extends SqlWindowingPagingQueryProvider {
30+
public class Db2PagingQueryProvider extends AbstractSqlPagingQueryProvider {
3131

3232
@Override
3333
public String generateFirstPageQuery(int pageSize) {
@@ -44,11 +44,6 @@ public String generateRemainingPagesQuery(int pageSize) {
4444
}
4545
}
4646

47-
@Override
48-
protected Object getSubQueryAlias() {
49-
return "AS TMP_SUB ";
50-
}
51-
5247
private String buildLimitClause(int pageSize) {
5348
return new StringBuilder().append("FETCH FIRST ").append(pageSize).append(" ROWS ONLY").toString();
5449
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2023 the original author or authors.
2+
* Copyright 2006-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,76 +16,37 @@
1616

1717
package org.springframework.batch.item.database.support;
1818

19-
import java.sql.DatabaseMetaData;
20-
import javax.sql.DataSource;
21-
2219
import org.springframework.batch.item.database.PagingQueryProvider;
23-
import org.springframework.dao.InvalidDataAccessResourceUsageException;
24-
import org.springframework.jdbc.support.JdbcUtils;
20+
import org.springframework.util.StringUtils;
2521

2622
/**
27-
* Derby implementation of a {@link PagingQueryProvider} using standard SQL:2003 windowing
28-
* functions. These features are supported starting with Apache Derby version 10.4.1.3.
29-
* <p>
30-
* As the OVER() function does not support the ORDER BY clause a sub query is instead used
31-
* to order the results before the ROW_NUM restriction is applied
23+
* Derby implementation of a {@link PagingQueryProvider} using database specific features.
3224
*
3325
* @author Thomas Risberg
3426
* @author David Thexton
3527
* @author Michael Minella
28+
* @author Henning Pöttker
3629
* @since 2.0
3730
*/
38-
public class DerbyPagingQueryProvider extends SqlWindowingPagingQueryProvider {
39-
40-
private static final String MINIMAL_DERBY_VERSION = "10.4.1.3";
31+
public class DerbyPagingQueryProvider extends AbstractSqlPagingQueryProvider {
4132

4233
@Override
43-
public void init(DataSource dataSource) throws Exception {
44-
super.init(dataSource);
45-
String version = JdbcUtils.extractDatabaseMetaData(dataSource, DatabaseMetaData::getDatabaseProductVersion);
46-
if (!isDerbyVersionSupported(version)) {
47-
throw new InvalidDataAccessResourceUsageException(
48-
"Apache Derby version " + version + " is not supported by this class, Only version "
49-
+ MINIMAL_DERBY_VERSION + " or later is supported");
50-
}
51-
}
52-
53-
// derby version numbering is M.m.f.p [ {alpha|beta} ] see
54-
// https://db.apache.org/derby/papers/versionupgrade.html#Basic+Numbering+Scheme
55-
private boolean isDerbyVersionSupported(String version) {
56-
String[] minimalVersionParts = MINIMAL_DERBY_VERSION.split("\\.");
57-
String[] versionParts = version.split("[\\. ]");
58-
for (int i = 0; i < minimalVersionParts.length; i++) {
59-
int minimalVersionPart = Integer.parseInt(minimalVersionParts[i]);
60-
int versionPart = Integer.parseInt(versionParts[i]);
61-
if (versionPart < minimalVersionPart) {
62-
return false;
63-
}
64-
else if (versionPart > minimalVersionPart) {
65-
return true;
66-
}
67-
}
68-
return true;
34+
public String generateFirstPageQuery(int pageSize) {
35+
return SqlPagingQueryUtils.generateLimitSqlQuery(this, false, buildLimitClause(pageSize));
6936
}
7037

7138
@Override
72-
protected String getOrderedQueryAlias() {
73-
return "TMP_ORDERED";
74-
}
75-
76-
@Override
77-
protected String getOverClause() {
78-
return "";
79-
}
80-
81-
@Override
82-
protected String getOverSubstituteClauseStart() {
83-
return " FROM (SELECT " + getSelectClause();
39+
public String generateRemainingPagesQuery(int pageSize) {
40+
if (StringUtils.hasText(getGroupClause())) {
41+
return SqlPagingQueryUtils.generateLimitGroupedSqlQuery(this, buildLimitClause(pageSize));
42+
}
43+
else {
44+
return SqlPagingQueryUtils.generateLimitSqlQuery(this, true, buildLimitClause(pageSize));
45+
}
8446
}
8547

86-
@Override
87-
protected String getOverSubstituteClauseEnd() {
88-
return " ) AS " + getOrderedQueryAlias();
48+
private String buildLimitClause(int pageSize) {
49+
return new StringBuilder("FETCH FIRST ").append(pageSize).append(" ROWS ONLY").toString();
8950
}
9051

9152
}

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlServerPagingQueryProvider.java

+1-6
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
* @author Mahmoud Ben Hassine
2929
* @since 2.0
3030
*/
31-
public class SqlServerPagingQueryProvider extends SqlWindowingPagingQueryProvider {
31+
public class SqlServerPagingQueryProvider extends AbstractSqlPagingQueryProvider {
3232

3333
@Override
3434
public String generateFirstPageQuery(int pageSize) {
@@ -45,11 +45,6 @@ public String generateRemainingPagesQuery(int pageSize) {
4545
}
4646
}
4747

48-
@Override
49-
protected Object getSubQueryAlias() {
50-
return "AS TMP_SUB ";
51-
}
52-
5348
private String buildTopClause(int pageSize) {
5449
return new StringBuilder().append("TOP ").append(pageSize).toString();
5550
}

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlWindowingPagingQueryProvider.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2023 the original author or authors.
2+
* Copyright 2006-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,7 +26,9 @@
2626
* @author Thomas Risberg
2727
* @author Michael Minella
2828
* @since 2.0
29+
* @deprecated since 5.2.1 with no replacement. Scheduled for removal in 6.0.
2930
*/
31+
@Deprecated(forRemoval = true)
3032
public class SqlWindowingPagingQueryProvider extends AbstractSqlPagingQueryProvider {
3133

3234
@Override

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SybasePagingQueryProvider.java

+1-6
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
* @author Mahmoud Ben Hassine
2929
* @since 2.0
3030
*/
31-
public class SybasePagingQueryProvider extends SqlWindowingPagingQueryProvider {
31+
public class SybasePagingQueryProvider extends AbstractSqlPagingQueryProvider {
3232

3333
@Override
3434
public String generateFirstPageQuery(int pageSize) {
@@ -45,11 +45,6 @@ public String generateRemainingPagesQuery(int pageSize) {
4545
}
4646
}
4747

48-
@Override
49-
protected Object getSubQueryAlias() {
50-
return "";
51-
}
52-
5348
private String buildTopClause(int pageSize) {
5449
return new StringBuilder().append("TOP ").append(pageSize).toString();
5550
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.batch.item.database.support;
17+
18+
import java.util.HashMap;
19+
import java.util.List;
20+
import java.util.Map;
21+
22+
import org.junit.jupiter.api.AfterAll;
23+
import org.junit.jupiter.api.BeforeAll;
24+
import org.junit.jupiter.api.Test;
25+
import org.springframework.batch.item.database.Order;
26+
import org.springframework.jdbc.core.JdbcTemplate;
27+
import org.springframework.jdbc.core.RowMapper;
28+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
29+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
30+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
31+
32+
import static org.junit.jupiter.api.Assertions.assertEquals;
33+
34+
/**
35+
* @author Henning Pöttker
36+
*/
37+
class DerbyPagingQueryProviderIntegrationTests {
38+
39+
private static EmbeddedDatabase embeddedDatabase;
40+
41+
private static JdbcTemplate jdbcTemplate;
42+
43+
@BeforeAll
44+
static void setUp() {
45+
embeddedDatabase = new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.DERBY)
46+
.addScript("/org/springframework/batch/item/database/support/query-provider-fixture.sql")
47+
.generateUniqueName(true)
48+
.build();
49+
jdbcTemplate = new JdbcTemplate(embeddedDatabase);
50+
}
51+
52+
@AfterAll
53+
static void tearDown() {
54+
if (embeddedDatabase != null) {
55+
embeddedDatabase.shutdown();
56+
}
57+
}
58+
59+
@Test
60+
void testWithoutGrouping() {
61+
var queryProvider = new DerbyPagingQueryProvider();
62+
queryProvider.setSelectClause("ID, STRING");
63+
queryProvider.setFromClause("TEST_TABLE");
64+
Map<String, Order> sortKeys = new HashMap<>();
65+
sortKeys.put("ID", Order.ASCENDING);
66+
queryProvider.setSortKeys(sortKeys);
67+
68+
List<Item> firstPage = jdbcTemplate.query(queryProvider.generateFirstPageQuery(2), MAPPER);
69+
assertEquals(List.of(new Item(1, "Spring"), new Item(2, "Batch")), firstPage);
70+
71+
List<Item> secondPage = jdbcTemplate.query(queryProvider.generateRemainingPagesQuery(2), MAPPER, 2);
72+
assertEquals(List.of(new Item(3, "Infrastructure")), secondPage);
73+
}
74+
75+
@Test
76+
void testWithGrouping() {
77+
var queryProvider = new DerbyPagingQueryProvider();
78+
queryProvider.setSelectClause("STRING");
79+
queryProvider.setFromClause("GROUPING_TEST_TABLE");
80+
queryProvider.setGroupClause("STRING");
81+
Map<String, Order> sortKeys = new HashMap<>();
82+
sortKeys.put("STRING", Order.ASCENDING);
83+
queryProvider.setSortKeys(sortKeys);
84+
85+
List<String> firstPage = jdbcTemplate.queryForList(queryProvider.generateFirstPageQuery(2), String.class);
86+
assertEquals(List.of("Batch", "Infrastructure"), firstPage);
87+
88+
List<String> secondPage = jdbcTemplate.queryForList(queryProvider.generateRemainingPagesQuery(2), String.class,
89+
"Infrastructure");
90+
assertEquals(List.of("Spring"), secondPage);
91+
}
92+
93+
private record Item(Integer id, String string) {
94+
}
95+
96+
private static final RowMapper<Item> MAPPER = (rs, rowNum) -> new Item(rs.getInt("id"), rs.getString("string"));
97+
98+
}

0 commit comments

Comments
 (0)