From 25504b2a3ccac958009b6e33d56187d4b2405d0a Mon Sep 17 00:00:00 2001 From: dsyer Date: Wed, 23 Dec 2009 12:27:13 +0000 Subject: [PATCH] BATCH-1460: merged changes from trunk --- .../support/DerbyPagingQueryProvider.java | 11 ++++ .../support/OraclePagingQueryProvider.java | 16 +++++- .../database/support/SqlPagingQueryUtils.java | 21 ++++++- .../SqlWindowingPagingQueryProvider.java | 44 +++++++++------ .../DerbyPagingQueryProviderTests.java | 56 ++++++++++++++----- .../OraclePagingQueryProviderTests.java | 4 +- 6 files changed, 113 insertions(+), 39 deletions(-) diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/DerbyPagingQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/DerbyPagingQueryProvider.java index d9c769aa1..1878032a6 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/DerbyPagingQueryProvider.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/DerbyPagingQueryProvider.java @@ -39,4 +39,15 @@ public class DerbyPagingQueryProvider extends SqlWindowingPagingQueryProvider { throw new InvalidDataAccessResourceUsageException("Apache Derby version " + version + " is not supported by this class, Only version 10.4.1.3 or later is supported"); } } + + @Override + protected Object getSubQueryAlias() { + return "AS TMP_SUB "; + } + + @Override + protected String getOverClause() { + return ""; + } + } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/OraclePagingQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/OraclePagingQueryProvider.java index df45606ab..e8eb4f86f 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/OraclePagingQueryProvider.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/OraclePagingQueryProvider.java @@ -17,13 +17,14 @@ package org.springframework.batch.item.database.support; /** - * Oracle implementation of a {@link org.springframework.batch.item.database.PagingQueryProvider} using + * Oracle implementation of a + * {@link org.springframework.batch.item.database.PagingQueryProvider} using * database specific features. - * + * * @author Thomas Risberg * @since 2.0 */ -public class OraclePagingQueryProvider extends SqlWindowingPagingQueryProvider { +public class OraclePagingQueryProvider extends AbstractSqlPagingQueryProvider { @Override public String generateFirstPageQuery(int pageSize) { @@ -36,6 +37,15 @@ public class OraclePagingQueryProvider extends SqlWindowingPagingQueryProvider { return SqlPagingQueryUtils.generateRowNumSqlQuery(this, true, buildRowNumClause(pageSize)); } + @Override + public String generateJumpToItemQuery(int itemIndex, int pageSize) { + int page = itemIndex / pageSize; + int offset = (page * pageSize); + offset = offset==0 ? 1 : offset; + return SqlPagingQueryUtils.generateRowNumSqlQuery(this, this.getSortKey() + " AS SORT_KEY", false, + "ROWNUM = " + offset); + } + private String buildRowNumClause(int pageSize) { return new StringBuilder().append("ROWNUM <= ").append(pageSize).toString(); } 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 f59d63839..763424d27 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 @@ -76,15 +76,32 @@ public class SqlPagingQueryUtils { * * @param provider {@link AbstractSqlPagingQueryProvider} providing the * implementation specifics - * @param remainingPageQuery is this query for the ramining pages (true) as + * @param remainingPageQuery is this query for the remaining pages (true) as * opposed to the first page (false) * @param rowNumClause the implementation specific row num clause to be used * @return the generated query */ public static String generateRowNumSqlQuery(AbstractSqlPagingQueryProvider provider, boolean remainingPageQuery, String rowNumClause) { + + return generateRowNumSqlQuery(provider, provider.getSelectClause(), remainingPageQuery, rowNumClause); + + } + + /** + * Generate SQL query string using a ROW_NUM condition + * + * @param provider {@link AbstractSqlPagingQueryProvider} providing the + * implementation specifics + * @param remainingPageQuery is this query for the remaining pages (true) as + * opposed to the first page (false) + * @param rowNumClause the implementation specific row num clause to be used + * @return the generated query + */ + public static String generateRowNumSqlQuery(AbstractSqlPagingQueryProvider provider, String selectClause, boolean remainingPageQuery, + String rowNumClause) { StringBuilder sql = new StringBuilder(); - sql.append("SELECT * FROM (SELECT ").append(provider.getSelectClause()); + sql.append("SELECT * FROM (SELECT ").append(selectClause); sql.append(" FROM ").append(provider.getFromClause()); buildWhereClause(provider, remainingPageQuery, sql); sql.append(" ORDER BY ").append(provider.getSortKey()); diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlWindowingPagingQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlWindowingPagingQueryProvider.java index b885e84a2..670579c89 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlWindowingPagingQueryProvider.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlWindowingPagingQueryProvider.java @@ -17,9 +17,10 @@ package org.springframework.batch.item.database.support; /** - * Generic Paging Query Provider using standard SQL:2003 windowing functions. These features are supported by - * DB2, Oracle, SQL Server 2005, Sybase and Apache Derby version 10.4.1.3 - * + * Generic Paging Query Provider using standard SQL:2003 windowing functions. + * These features are supported by DB2, Oracle, SQL Server 2005, Sybase and + * Apache Derby version 10.4.1.3 + * * @author Thomas Risberg * @since 2.0 */ @@ -30,22 +31,25 @@ public class SqlWindowingPagingQueryProvider extends AbstractSqlPagingQueryProvi StringBuilder sql = new StringBuilder(); sql.append("SELECT * FROM ( "); sql.append("SELECT ").append(getSelectClause()).append(", "); - sql.append("ROW_NUMBER() OVER (ORDER BY ").append(getSortKey()); - buildAscendingClause(sql); + sql.append("ROW_NUMBER() OVER (").append(getOverClause()); sql.append(") AS ROW_NUMBER"); - sql.append(" FROM ").append(getFromClause()).append(getWhereClause() == null ? "" : " WHERE " + getWhereClause()); - sql.append(") WHERE ROW_NUMBER <= ").append(pageSize); + sql.append(" FROM ").append(getFromClause()).append( + getWhereClause() == null ? "" : " WHERE " + getWhereClause()); + sql.append(") ").append(getSubQueryAlias()).append("WHERE ROW_NUMBER <= ").append(pageSize); return sql.toString(); } + protected Object getSubQueryAlias() { + return ""; + } + @Override public String generateRemainingPagesQuery(int pageSize) { StringBuilder sql = new StringBuilder(); sql.append("SELECT * FROM ( "); sql.append("SELECT ").append(getSelectClause()).append(", "); - sql.append("ROW_NUMBER() OVER (ORDER BY ").append(getSortKey()); - buildAscendingClause(sql); + sql.append("ROW_NUMBER() OVER (").append(getOverClause()); sql.append(") AS ROW_NUMBER"); sql.append(" FROM ").append(getFromClause()); sql.append(" WHERE "); @@ -61,7 +65,7 @@ public class SqlWindowingPagingQueryProvider extends AbstractSqlPagingQueryProvi sql.append(" < "); } sql.append(getSortKeyPlaceHolder()); - sql.append(") WHERE ROW_NUMBER <= ").append(pageSize); + sql.append(") ").append(getSubQueryAlias()).append("WHERE ROW_NUMBER <= ").append(pageSize); return sql.toString(); } @@ -70,28 +74,32 @@ public class SqlWindowingPagingQueryProvider extends AbstractSqlPagingQueryProvi public String generateJumpToItemQuery(int itemIndex, int pageSize) { int page = itemIndex / pageSize; int lastRowNum = (page * pageSize); - if (lastRowNum<=0) { + if (lastRowNum <= 0) { lastRowNum = 1; } StringBuilder sql = new StringBuilder(); sql.append("SELECT SORT_KEY FROM ( "); sql.append("SELECT ").append(getSortKey()).append(" AS SORT_KEY, "); - sql.append("ROW_NUMBER() OVER (ORDER BY ").append(getSortKey()); - buildAscendingClause(sql); + sql.append("ROW_NUMBER() OVER (").append(getOverClause()); sql.append(") AS ROW_NUMBER"); - sql.append(" FROM ").append(getFromClause()).append(getWhereClause() == null ? "" : " WHERE " + getWhereClause()); - sql.append(") WHERE ROW_NUMBER = ").append(lastRowNum); + sql.append(" FROM ").append(getFromClause()).append( + getWhereClause() == null ? "" : " WHERE " + getWhereClause()); + sql.append(") ").append(getSubQueryAlias()).append("WHERE ROW_NUMBER = ").append(lastRowNum); return sql.toString(); } - private void buildAscendingClause(StringBuilder sql) { + protected String getOverClause() { + return "ORDER BY " + getSortKey() + " " + getAscendingClause(); + } + + private String getAscendingClause() { if (isAscending()) { - sql.append(" ASC"); + return "ASC"; } else { - sql.append(" DESC"); + return "DESC"; } } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/DerbyPagingQueryProviderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/DerbyPagingQueryProviderTests.java index 98b47cab8..1e7420645 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/DerbyPagingQueryProviderTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/DerbyPagingQueryProviderTests.java @@ -15,19 +15,21 @@ */ package org.springframework.batch.item.database.support; -import static org.junit.Assert.fail; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; -import org.junit.Test; -import org.junit.Assert; -import org.springframework.dao.InvalidDataAccessResourceUsageException; +import java.sql.Connection; +import java.sql.DatabaseMetaData; import javax.sql.DataSource; -import java.sql.DatabaseMetaData; -import java.sql.Connection; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.dao.InvalidDataAccessResourceUsageException; /** * @author Thomas Risberg @@ -69,7 +71,8 @@ public class DerbyPagingQueryProviderTests extends AbstractSqlPagingQueryProvide try { pagingQueryProvider.init(ds); fail(); - } catch (InvalidDataAccessResourceUsageException e) { + } + catch (InvalidDataAccessResourceUsageException e) { // expected } verify(ds); @@ -80,30 +83,55 @@ public class DerbyPagingQueryProviderTests extends AbstractSqlPagingQueryProvide @Test @Override public void testGenerateFirstPageQuery() { - String sql = "SELECT * FROM ( SELECT id, name, age, ROW_NUMBER() OVER (ORDER BY id ASC) AS ROW_NUMBER FROM foo WHERE bar = 1) WHERE ROW_NUMBER <= 100"; + String sql = "SELECT * FROM ( SELECT id, name, age, ROW_NUMBER() OVER () AS ROW_NUMBER FROM foo WHERE bar = 1) AS TMP_SUB WHERE ROW_NUMBER <= 100"; String s = pagingQueryProvider.generateFirstPageQuery(pageSize); Assert.assertEquals("", sql, s); } - @Test @Override + @Test + @Override public void testGenerateRemainingPagesQuery() { - String sql = "SELECT * FROM ( SELECT id, name, age, ROW_NUMBER() OVER (ORDER BY id ASC) AS ROW_NUMBER FROM foo WHERE bar = 1 AND id > ?) WHERE ROW_NUMBER <= 100"; + String sql = "SELECT * FROM ( SELECT id, name, age, ROW_NUMBER() OVER () AS ROW_NUMBER FROM foo WHERE bar = 1 AND id > ?) AS TMP_SUB WHERE ROW_NUMBER <= 100"; String s = pagingQueryProvider.generateRemainingPagesQuery(pageSize); Assert.assertEquals("", sql, s); } - @Test @Override + @Test + @Override public void testGenerateJumpToItemQuery() { - String sql = "SELECT SORT_KEY FROM ( SELECT id AS SORT_KEY, ROW_NUMBER() OVER (ORDER BY id ASC) AS ROW_NUMBER FROM foo WHERE bar = 1) WHERE ROW_NUMBER = 100"; + String sql = "SELECT SORT_KEY FROM ( SELECT id AS SORT_KEY, ROW_NUMBER() OVER () AS ROW_NUMBER FROM foo WHERE bar = 1) AS TMP_SUB WHERE ROW_NUMBER = 100"; String s = pagingQueryProvider.generateJumpToItemQuery(145, pageSize); Assert.assertEquals("", sql, s); } - @Test @Override + @Test + @Override public void testGenerateJumpToItemQueryForFirstPage() { - String sql = "SELECT SORT_KEY FROM ( SELECT id AS SORT_KEY, ROW_NUMBER() OVER (ORDER BY id ASC) AS ROW_NUMBER FROM foo WHERE bar = 1) WHERE ROW_NUMBER = 1"; + String sql = "SELECT SORT_KEY FROM ( SELECT id AS SORT_KEY, ROW_NUMBER() OVER () AS ROW_NUMBER FROM foo WHERE bar = 1) AS TMP_SUB WHERE ROW_NUMBER = 1"; String s = pagingQueryProvider.generateJumpToItemQuery(45, pageSize); Assert.assertEquals("", sql, s); } + /** + * Derby doesn't allow order by in the sub select (so it's pretty useless in + * general for paging) + */ + @Test + @Override + public void testQueryContainsSortKey() { + String s = pagingQueryProvider.generateFirstPageQuery(pageSize).toLowerCase(); + assertFalse("Wrong query: " + s, s.contains("id asc")); + } + + /** + * Derby doesn't allow order by in the sub select + */ + @Test + @Override + public void testQueryContainsSortKeyDesc() { + pagingQueryProvider.setAscending(false); + String s = pagingQueryProvider.generateFirstPageQuery(pageSize).toLowerCase(); + assertFalse("Wrong query: " + s, s.contains("id desc")); + } + } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/OraclePagingQueryProviderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/OraclePagingQueryProviderTests.java index 628de16b6..6983d2504 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/OraclePagingQueryProviderTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/OraclePagingQueryProviderTests.java @@ -33,14 +33,14 @@ public class OraclePagingQueryProviderTests extends AbstractSqlPagingQueryProvid @Test @Override public void testGenerateJumpToItemQuery() { - String sql = "SELECT SORT_KEY FROM ( SELECT id AS SORT_KEY, ROW_NUMBER() OVER (ORDER BY id ASC) AS ROW_NUMBER FROM foo WHERE bar = 1) WHERE ROW_NUMBER = 100"; + String sql = "SELECT * FROM (SELECT id AS SORT_KEY FROM foo WHERE bar = 1 ORDER BY id ASC) WHERE ROWNUM = 100"; String s = pagingQueryProvider.generateJumpToItemQuery(145, pageSize); Assert.assertEquals("", sql, s); } @Test @Override public void testGenerateJumpToItemQueryForFirstPage() { - String sql = "SELECT SORT_KEY FROM ( SELECT id AS SORT_KEY, ROW_NUMBER() OVER (ORDER BY id ASC) AS ROW_NUMBER FROM foo WHERE bar = 1) WHERE ROW_NUMBER = 1"; + String sql = "SELECT * FROM (SELECT id AS SORT_KEY FROM foo WHERE bar = 1 ORDER BY id ASC) WHERE ROWNUM = 1"; String s = pagingQueryProvider.generateJumpToItemQuery(45, pageSize); Assert.assertEquals("", sql, s); }