diff --git a/spring-batch-core-tests/pom.xml b/spring-batch-core-tests/pom.xml index d1dab9180..bed360b3b 100644 --- a/spring-batch-core-tests/pom.xml +++ b/spring-batch-core-tests/pom.xml @@ -108,7 +108,6 @@ commons-lang commons-lang - test org.apache.derby diff --git a/spring-batch-infrastructure/pom.xml b/spring-batch-infrastructure/pom.xml index e9899a6af..64a06307e 100644 --- a/spring-batch-infrastructure/pom.xml +++ b/spring-batch-infrastructure/pom.xml @@ -109,6 +109,11 @@ h2 test + + org.apache.derby + derby + test + org.hibernate hibernate-core 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); } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/DatabaseTypeIntegrationTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/DatabaseTypeIntegrationTests.java index eca7e389f..5c6baec0d 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/DatabaseTypeIntegrationTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/DatabaseTypeIntegrationTests.java @@ -22,18 +22,26 @@ import javax.sql.DataSource; import org.junit.Test; - /** * @author Dave Syer - * + * */ public class DatabaseTypeIntegrationTests { - + @Test public void testH2() throws Exception { - DataSource dataSource = DatabaseTypeTestUtils.getDataSource(org.h2.Driver.class, "jdbc:h2:file:target/data/sample"); + DataSource dataSource = DatabaseTypeTestUtils.getDataSource(org.h2.Driver.class, + "jdbc:h2:file:target/data/sample"); assertEquals(DatabaseType.H2, DatabaseType.fromMetaData(dataSource)); dataSource.getConnection(); } + @Test + public void testDerby() throws Exception { + DataSource dataSource = DatabaseTypeTestUtils.getDataSource(org.apache.derby.jdbc.EmbeddedDriver.class, + "jdbc:derby:derby-home/test;create=true", "sa", ""); + assertEquals(DatabaseType.DERBY, DatabaseType.fromMetaData(dataSource)); + dataSource.getConnection(); + } + } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/DatabaseTypeTestUtils.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/DatabaseTypeTestUtils.java index fc04c9546..dc12f27a6 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/DatabaseTypeTestUtils.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/DatabaseTypeTestUtils.java @@ -32,12 +32,18 @@ import org.apache.commons.dbcp.BasicDataSource; */ public class DatabaseTypeTestUtils { - public static DataSource getDataSource(Class driver, String url) throws Exception { + public static DataSource getDataSource(Class driver, String url, String username, String password) throws Exception { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName(driver.getName()); dataSource.setUrl(url); + dataSource.setUsername(username); + dataSource.setPassword(password); return dataSource; } + + public static DataSource getDataSource(Class driver, String url) throws Exception { + return getDataSource(driver, url, null, null); + } public static DataSource getMockDataSource() throws Exception { return getMockDataSource(DatabaseType.HSQL.getProductName()); diff --git a/spring-batch-samples/src/main/resources/batch-derby.properties b/spring-batch-samples/src/main/resources/batch-derby.properties index 9270541d1..380004cd1 100644 --- a/spring-batch-samples/src/main/resources/batch-derby.properties +++ b/spring-batch-samples/src/main/resources/batch-derby.properties @@ -4,15 +4,13 @@ batch.jdbc.driver=org.apache.derby.jdbc.EmbeddedDriver batch.jdbc.url=jdbc:derby:derby-home/test;create=true batch.jdbc.user=sa batch.jdbc.password= -batch.schema= -batch.jndi.name= -batch.naming.factory.initial= -batch.naming.provider.url= -batch.schema.script=/org/springframework/batch/core/schema-derby.sql +batch.jdbc.testWhileIdle=false +batch.jdbc.validationQuery= +batch.drop.script=classpath:/org/springframework/batch/core/schema-drop-derby.sql +batch.schema.script=classpath:/org/springframework/batch/core/schema-derby.sql batch.business.schema.script=business-schema-derby.sql +batch.data.source.init=true batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.DerbyMaxValueIncrementer -batch.lob.handler.class=org.springframework.jdbc.support.lob.DefaultLobHandler - -# Bean Properties for override -# when not using sequences: -incrementerParent.columnName=ID +batch.database.incrementer.parent=columnIncrementerParent +batch.grid.size=2 +batch.verify.cursor.position=false \ No newline at end of file diff --git a/spring-batch-samples/src/main/resources/batch-hsql.properties b/spring-batch-samples/src/main/resources/batch-hsql.properties index 362846da7..87d2d3919 100644 --- a/spring-batch-samples/src/main/resources/batch-hsql.properties +++ b/spring-batch-samples/src/main/resources/batch-hsql.properties @@ -15,3 +15,4 @@ batch.data.source.init=true batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.HsqlMaxValueIncrementer batch.database.incrementer.parent=columnIncrementerParent batch.grid.size=2 +batch.verify.cursor.position=true diff --git a/spring-batch-samples/src/main/resources/batch-mysql.properties b/spring-batch-samples/src/main/resources/batch-mysql.properties index 59bc4a9a4..4fe7a243e 100644 --- a/spring-batch-samples/src/main/resources/batch-mysql.properties +++ b/spring-batch-samples/src/main/resources/batch-mysql.properties @@ -13,3 +13,4 @@ batch.data.source.init=true batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.MySQLMaxValueIncrementer batch.database.incrementer.parent=columnIncrementerParent batch.grid.size=50 +batch.verify.cursor.position=true diff --git a/spring-batch-samples/src/main/resources/data-source-context.xml b/spring-batch-samples/src/main/resources/data-source-context.xml index ae90208ce..2399ec377 100644 --- a/spring-batch-samples/src/main/resources/data-source-context.xml +++ b/spring-batch-samples/src/main/resources/data-source-context.xml @@ -28,16 +28,17 @@ - + + - + @@ -49,7 +50,7 @@ - + diff --git a/spring-batch-samples/src/main/resources/jobs/iosample/jdbcPaging.xml b/spring-batch-samples/src/main/resources/jobs/iosample/jdbcPaging.xml index 271062e12..3647fa413 100644 --- a/spring-batch-samples/src/main/resources/jobs/iosample/jdbcPaging.xml +++ b/spring-batch-samples/src/main/resources/jobs/iosample/jdbcPaging.xml @@ -11,6 +11,7 @@ +