Skip to content

Update implementations of PagingQueryProvider #4713

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions spring-batch-infrastructure/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,84 @@
<version>${derby.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql-connector-j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mysql</artifactId>
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>oracle-xe</artifactId>
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc10</artifactId>
<version>${oracle.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>${mariadb-java-client.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mariadb</artifactId>
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.ibm.db2</groupId>
<artifactId>jcc</artifactId>
<version>${db2.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>db2</artifactId>
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mssqlserver</artifactId>
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>${sqlserver.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2006-2021 the original author or authors.
* Copyright 2006-2024 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.
Expand Down Expand Up @@ -27,7 +27,7 @@
* @author Mahmoud Ben Hassine
* @since 2.0
*/
public class Db2PagingQueryProvider extends SqlWindowingPagingQueryProvider {
public class Db2PagingQueryProvider extends AbstractSqlPagingQueryProvider {

@Override
public String generateFirstPageQuery(int pageSize) {
Expand All @@ -44,11 +44,6 @@ public String generateRemainingPagesQuery(int pageSize) {
}
}

@Override
protected Object getSubQueryAlias() {
return "AS TMP_SUB ";
}

private String buildLimitClause(int pageSize) {
return new StringBuilder().append("FETCH FIRST ").append(pageSize).append(" ROWS ONLY").toString();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2024 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.
Expand All @@ -16,76 +16,37 @@

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

import java.sql.DatabaseMetaData;
import javax.sql.DataSource;

import org.springframework.batch.item.database.PagingQueryProvider;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.util.StringUtils;

/**
* Derby implementation of a {@link PagingQueryProvider} using standard SQL:2003 windowing
* functions. These features are supported starting with Apache Derby version 10.4.1.3.
* <p>
* As the OVER() function does not support the ORDER BY clause a sub query is instead used
* to order the results before the ROW_NUM restriction is applied
* Derby implementation of a {@link PagingQueryProvider} using database specific features.
*
* @author Thomas Risberg
* @author David Thexton
* @author Michael Minella
* @author Henning Pöttker
* @since 2.0
*/
public class DerbyPagingQueryProvider extends SqlWindowingPagingQueryProvider {

private static final String MINIMAL_DERBY_VERSION = "10.4.1.3";
public class DerbyPagingQueryProvider extends AbstractSqlPagingQueryProvider {

@Override
public void init(DataSource dataSource) throws Exception {
super.init(dataSource);
String version = JdbcUtils.extractDatabaseMetaData(dataSource, DatabaseMetaData::getDatabaseProductVersion);
if (!isDerbyVersionSupported(version)) {
throw new InvalidDataAccessResourceUsageException(
"Apache Derby version " + version + " is not supported by this class, Only version "
+ MINIMAL_DERBY_VERSION + " or later is supported");
}
}

// derby version numbering is M.m.f.p [ {alpha|beta} ] see
// https://db.apache.org/derby/papers/versionupgrade.html#Basic+Numbering+Scheme
private boolean isDerbyVersionSupported(String version) {
String[] minimalVersionParts = MINIMAL_DERBY_VERSION.split("\\.");
String[] versionParts = version.split("[\\. ]");
for (int i = 0; i < minimalVersionParts.length; i++) {
int minimalVersionPart = Integer.parseInt(minimalVersionParts[i]);
int versionPart = Integer.parseInt(versionParts[i]);
if (versionPart < minimalVersionPart) {
return false;
}
else if (versionPart > minimalVersionPart) {
return true;
}
}
return true;
public String generateFirstPageQuery(int pageSize) {
return SqlPagingQueryUtils.generateLimitSqlQuery(this, false, buildLimitClause(pageSize));
}

@Override
protected String getOrderedQueryAlias() {
return "TMP_ORDERED";
}

@Override
protected String getOverClause() {
return "";
}

@Override
protected String getOverSubstituteClauseStart() {
return " FROM (SELECT " + getSelectClause();
public String generateRemainingPagesQuery(int pageSize) {
if (StringUtils.hasText(getGroupClause())) {
return SqlPagingQueryUtils.generateLimitGroupedSqlQuery(this, buildLimitClause(pageSize));
}
else {
return SqlPagingQueryUtils.generateLimitSqlQuery(this, true, buildLimitClause(pageSize));
}
}

@Override
protected String getOverSubstituteClauseEnd() {
return " ) AS " + getOrderedQueryAlias();
private String buildLimitClause(int pageSize) {
return new StringBuilder("FETCH FIRST ").append(pageSize).append(" ROWS ONLY").toString();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
* @author Mahmoud Ben Hassine
* @since 2.0
*/
public class SqlServerPagingQueryProvider extends SqlWindowingPagingQueryProvider {
public class SqlServerPagingQueryProvider extends AbstractSqlPagingQueryProvider {

@Override
public String generateFirstPageQuery(int pageSize) {
Expand All @@ -45,11 +45,6 @@ public String generateRemainingPagesQuery(int pageSize) {
}
}

@Override
protected Object getSubQueryAlias() {
return "AS TMP_SUB ";
}

private String buildTopClause(int pageSize) {
return new StringBuilder().append("TOP ").append(pageSize).toString();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2024 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.
Expand All @@ -26,7 +26,9 @@
* @author Thomas Risberg
* @author Michael Minella
* @since 2.0
* @deprecated since 5.2.1 with no replacement. Scheduled for removal in 6.0.
*/
@Deprecated(forRemoval = true)
public class SqlWindowingPagingQueryProvider extends AbstractSqlPagingQueryProvider {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
* @author Mahmoud Ben Hassine
* @since 2.0
*/
public class SybasePagingQueryProvider extends SqlWindowingPagingQueryProvider {
public class SybasePagingQueryProvider extends AbstractSqlPagingQueryProvider {

@Override
public String generateFirstPageQuery(int pageSize) {
Expand All @@ -45,11 +45,6 @@ public String generateRemainingPagesQuery(int pageSize) {
}
}

@Override
protected Object getSubQueryAlias() {
return "";
}

private String buildTopClause(int pageSize) {
return new StringBuilder().append("TOP ").append(pageSize).toString();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright 2024 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.item.database.support;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;

import org.junit.jupiter.api.Test;
import org.springframework.batch.item.database.Order;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
* @author Henning Pöttker
*/
abstract class AbstractPagingQueryProviderIntegrationTests {

private final JdbcTemplate jdbcTemplate;

private final AbstractSqlPagingQueryProvider queryProvider;

AbstractPagingQueryProviderIntegrationTests(DataSource dataSource, AbstractSqlPagingQueryProvider queryProvider) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.queryProvider = queryProvider;
}

@Test
void testWithoutGrouping() {
queryProvider.setSelectClause("ID, STRING");
queryProvider.setFromClause("TEST_TABLE");
Map<String, Order> sortKeys = new HashMap<>();
sortKeys.put("ID", Order.ASCENDING);
queryProvider.setSortKeys(sortKeys);

List<Item> firstPage = jdbcTemplate.query(queryProvider.generateFirstPageQuery(2), MAPPER);
assertEquals(List.of(new Item(1, "Spring"), new Item(2, "Batch")), firstPage);

List<Item> secondPage = jdbcTemplate.query(queryProvider.generateRemainingPagesQuery(2), MAPPER, 2);
assertEquals(List.of(new Item(3, "Infrastructure")), secondPage);
}

@Test
void testWithGrouping() {
queryProvider.setSelectClause("STRING");
queryProvider.setFromClause("GROUPING_TEST_TABLE");
queryProvider.setGroupClause("STRING");
Map<String, Order> sortKeys = new HashMap<>();
sortKeys.put("STRING", Order.ASCENDING);
queryProvider.setSortKeys(sortKeys);

List<String> firstPage = jdbcTemplate.queryForList(queryProvider.generateFirstPageQuery(2), String.class);
assertEquals(List.of("Batch", "Infrastructure"), firstPage);

List<String> secondPage = jdbcTemplate.queryForList(queryProvider.generateRemainingPagesQuery(2), String.class,
"Infrastructure");
assertEquals(List.of("Spring"), secondPage);
}

private record Item(Integer id, String string) {
}

private static final RowMapper<Item> MAPPER = (rs, rowNum) -> new Item(rs.getInt("id"), rs.getString("string"));

}
Loading