From 77065c3ea6c8a54cfc5a7721f80f66f7c2277c8b Mon Sep 17 00:00:00 2001 From: trisberg Date: Sun, 31 Aug 2008 02:59:36 +0000 Subject: [PATCH] BATCH-760: added parameter support to all PagingItemReaders --- .../database/AbstractPagingItemReader.java | 1 + .../item/database/IbatisPagingItemReader.java | 16 ++- .../item/database/JdbcCursorItemReader.java | 2 +- .../item/database/JdbcPagingItemReader.java | 105 +++++++++++++--- .../item/database/JpaPagingItemReader.java | 18 ++- .../AbstractSqlPagingQueryProvider.java | 41 +++++++ .../support/Db2PagingQueryProvider.java | 8 +- .../support/HsqlPagingQueryProvider.java | 8 +- .../database/support/JdbcParameterUtils.java | 115 ++++++++++++++++++ .../support/MySqlPagingQueryProvider.java | 8 +- .../support/OraclePagingQueryProvider.java | 16 ++- .../database/support/PagingQueryProvider.java | 13 ++ .../support/PostgresPagingQueryProvider.java | 8 +- .../support/SqlServerPagingQueryProvider.java | 8 +- .../SqlWindowingPagingQueryProvider.java | 9 +- .../support/SybasePagingQueryProvider.java | 8 +- ...bstractPagingItemReaderParameterTests.java | 55 +++++++++ .../IbatisPagingItemReaderParameterTests.java | 45 +++++++ ...PagingItemReaderClassicParameterTests.java | 52 ++++++++ ...bcPagingItemReaderNamedParameterTests.java | 50 ++++++++ .../JpaPagingItemReaderParameterTests.java | 40 ++++++ .../support/Db2PagingQueryProviderTests.java | 2 +- .../DerbyPagingQueryProviderTests.java | 2 +- .../support/HsqlPagingQueryProviderTests.java | 2 +- .../support/JdbcParameterUtilsTests.java | 60 +++++++++ .../MySqlPagingQueryProviderTests.java | 2 +- .../OraclePagingQueryProviderTests.java | 4 +- .../PostgresPagingQueryProviderTests.java | 2 +- .../SqlServerPagingQueryProviderTests.java | 2 +- .../SqlWindowingPagingQueryProviderTests.java | 2 +- .../SybasePagingQueryProviderTests.java | 2 +- ...PagingItemReaderParameterTests-context.xml | 26 ++++ ...PagingItemReaderParameterTests-context.xml | 36 ++++++ .../batch/item/database/ibatis-foo.xml | 4 + 34 files changed, 725 insertions(+), 47 deletions(-) create mode 100644 spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/JdbcParameterUtils.java create mode 100644 spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/AbstractPagingItemReaderParameterTests.java create mode 100644 spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/IbatisPagingItemReaderParameterTests.java create mode 100644 spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JdbcPagingItemReaderClassicParameterTests.java create mode 100644 spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JdbcPagingItemReaderNamedParameterTests.java create mode 100644 spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JpaPagingItemReaderParameterTests.java create mode 100644 spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/JdbcParameterUtilsTests.java create mode 100644 spring-batch-infrastructure/src/test/resources/org/springframework/batch/item/database/JdbcPagingItemReaderParameterTests-context.xml create mode 100644 spring-batch-infrastructure/src/test/resources/org/springframework/batch/item/database/JpaPagingItemReaderParameterTests-context.xml diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/AbstractPagingItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/AbstractPagingItemReader.java index 18ba8f8a8..5683b2d24 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/AbstractPagingItemReader.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/AbstractPagingItemReader.java @@ -8,6 +8,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.List; +import java.util.Map; /** * Abstract {@link org.springframework.batch.item.ItemReader} for to extend when reading database records in a paging diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/IbatisPagingItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/IbatisPagingItemReader.java index 567f2226b..44f49cf22 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/IbatisPagingItemReader.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/IbatisPagingItemReader.java @@ -36,6 +36,8 @@ public class IbatisPagingItemReader extends AbstractPagingItemReader { private SqlMapClientTemplate sqlMapClientTemplate; + private Map parameterValues; + public IbatisPagingItemReader() { setName(ClassUtils.getShortName(IbatisPagingItemReader.class)); } @@ -48,6 +50,16 @@ public class IbatisPagingItemReader extends AbstractPagingItemReader { this.queryId = queryId; } + /** + * The parameter values to be used for the query execution. + * + * @param parameterValues the values keyed by the parameter named used in the query string. + */ + public void setParameterValues(Map parameterValues) { + this.parameterValues = parameterValues; + } + + /** * Check mandatory properties. * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() @@ -62,8 +74,10 @@ public class IbatisPagingItemReader extends AbstractPagingItemReader { @Override @SuppressWarnings("unchecked") protected void doReadPage() { - //TODO: add support for parameter map Map parameters = new HashMap(); + if (parameterValues != null) { + parameters.putAll(parameterValues); + } results = sqlMapClientTemplate.queryForList(queryId, parameters, (page * pageSize), pageSize); } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcCursorItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcCursorItemReader.java index 368c41d9e..d9bf04699 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcCursorItemReader.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcCursorItemReader.java @@ -232,7 +232,7 @@ public class JdbcCursorItemReader extends AbstractItemReaderItemStream imp * Throw a SQLWarningException if we're not ignoring warnings, else log the * warnings (at debug level). * - * @param warning the warnings object from the current statement. May be + * @param warnings the warnings object from the current statement. May be * null, in which case this method does nothing. * * @see org.springframework.jdbc.SQLWarningException diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcPagingItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcPagingItemReader.java index e16191584..68c5cf260 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcPagingItemReader.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JdbcPagingItemReader.java @@ -27,6 +27,11 @@ import org.springframework.jdbc.core.simple.ParameterizedRowMapper; import javax.sql.DataSource; import java.util.ArrayList; +import java.util.Map; +import java.util.List; +import java.util.LinkedHashMap; +import java.util.SortedMap; +import java.util.TreeMap; import java.sql.ResultSet; import java.sql.SQLException; @@ -56,6 +61,8 @@ public class JdbcPagingItemReader extends AbstractPagingItemReader impleme private PagingQueryProvider queryProvider; + private Map parameterValues; + private SimpleJdbcTemplate simpleJdbcTemplate; private ParameterizedRowMapper parameterizedRowMapper; @@ -87,6 +94,18 @@ public class JdbcPagingItemReader extends AbstractPagingItemReader impleme this.parameterizedRowMapper = parameterizedRowMapper; } + /** + * The parameter values to be used for the query execution. If you use named parameters then the + * key should be the name used in the query clause. If you use "?" placeholders then the key should be + * the relative index that the parameter appears in the query string built using the select, from and + * where cluases specified. + * + * @param parameterValues the values keyed by the parameter named/index used in the query string. + */ + public void setParameterValues(Map parameterValues) { + this.parameterValues = parameterValues; + } + /** * Check mandatory properties. * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() @@ -105,7 +124,6 @@ public class JdbcPagingItemReader extends AbstractPagingItemReader impleme @Override protected void doReadPage() { - //TODO: add support for parameter map if (results == null) { results = new ArrayList(); @@ -118,26 +136,43 @@ public class JdbcPagingItemReader extends AbstractPagingItemReader impleme if (logger.isDebugEnabled()) { logger.debug("SQL used for reading first page: [" + firstPageSql + "]"); } - simpleJdbcTemplate.getJdbcOperations().query(firstPageSql, - new RowCallbackHandler() { - public void processRow(ResultSet rs) throws SQLException { - startAfterValue = rs.getObject(1); - results.add(parameterizedRowMapper.mapRow(rs, results.size())); - } - }); + if (parameterValues != null && parameterValues.size() > 0) { + if (this.queryProvider.isUsingNamedParameters()) { + simpleJdbcTemplate.getNamedParameterJdbcOperations().query(firstPageSql, + getParameterMap(parameterValues, null), + new RowCallbackHandler() { + public void processRow(ResultSet rs) throws SQLException { + startAfterValue = rs.getObject(1); + results.add(parameterizedRowMapper.mapRow(rs, results.size())); + } + }); + } + else { + simpleJdbcTemplate.getJdbcOperations().query(firstPageSql, + getParameterList(parameterValues, null).toArray(), + new PagingRowCallbackHandler()); + } + } + else { + simpleJdbcTemplate.getJdbcOperations().query(firstPageSql, + new PagingRowCallbackHandler()); + } + } else { if (logger.isDebugEnabled()) { logger.debug("SQL used for reading remaining pages: [" + remainingPagesSql + "]"); } - simpleJdbcTemplate.getJdbcOperations().query(remainingPagesSql, - new Object[] {startAfterValue}, - new RowCallbackHandler() { - public void processRow(ResultSet rs) throws SQLException { - startAfterValue = rs.getObject(1); - results.add(parameterizedRowMapper.mapRow(rs, results.size())); - } - }); + if (this.queryProvider.isUsingNamedParameters()) { + simpleJdbcTemplate.getNamedParameterJdbcOperations().query(remainingPagesSql, + getParameterMap(parameterValues, startAfterValue), + new PagingRowCallbackHandler()); + } + else { + simpleJdbcTemplate.getJdbcOperations().query(remainingPagesSql, + getParameterList(parameterValues, startAfterValue).toArray(), + new PagingRowCallbackHandler()); + } } } @@ -165,4 +200,42 @@ public class JdbcPagingItemReader extends AbstractPagingItemReader impleme } + private Map getParameterMap(Map values, Object sortKeyValue) { + Map parameterMap = new LinkedHashMap(); + if (values != null) { + parameterMap.putAll(values); + } + if (sortKeyValue != null) { + parameterMap.put("_sortKey", sortKeyValue); + } + if (logger.isDebugEnabled()) { + logger.debug("Using parameterMap:" + parameterMap); + } + System.out.println(); + return parameterMap; + } + + private List getParameterList(Map values, Object sortKeyValue) { + SortedMap sm = new TreeMap(); + if (values != null) { + sm.putAll(values); + } + List parameterList = new ArrayList(); + parameterList.addAll(sm.values()); + if (sortKeyValue != null) { + parameterList.add(sortKeyValue); + } + if (logger.isDebugEnabled()) { + logger.debug("Using parameterList:" + parameterList); + } + return parameterList; + } + + private class PagingRowCallbackHandler implements RowCallbackHandler { + public void processRow(ResultSet rs) throws SQLException { + startAfterValue = rs.getObject(1); + results.add(parameterizedRowMapper.mapRow(rs, results.size())); + } + } + } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JpaPagingItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JpaPagingItemReader.java index 4a4191636..b6251e9ca 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JpaPagingItemReader.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/JpaPagingItemReader.java @@ -62,6 +62,8 @@ public class JpaPagingItemReader extends AbstractPagingItemReader { private String queryString; + private Map parameterValues; + public JpaPagingItemReader() { setName(ClassUtils.getShortName(JpaPagingItemReader.class)); } @@ -70,6 +72,15 @@ public class JpaPagingItemReader extends AbstractPagingItemReader { this.entityManagerFactory = entityManagerFactory; } + /** + * The parameter values to be used for the query execution. + * + * @param parameterValues the values keyed by the parameter named used in the query string. + */ + public void setParameterValues(Map parameterValues) { + this.parameterValues = parameterValues; + } + public void afterPropertiesSet() throws Exception { super.afterPropertiesSet(); Assert.notNull(entityManagerFactory); @@ -86,7 +97,6 @@ public class JpaPagingItemReader extends AbstractPagingItemReader { @Override @SuppressWarnings("unchecked") protected void doReadPage() { - //TODO: add support for parameter map EntityManager entityManager = entityManagerFactory.createEntityManager(jpaPropertyMap); @@ -101,6 +111,12 @@ public class JpaPagingItemReader extends AbstractPagingItemReader { .setFirstResult(page * pageSize) .setMaxResults(pageSize); + if (parameterValues != null) { + for (Map.Entry me : parameterValues.entrySet()) { + query.setParameter(me.getKey(), me.getValue()); + } + } + results = query.getResultList(); entityManager.flush(); diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/AbstractSqlPagingQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/AbstractSqlPagingQueryProvider.java index f2b729cf8..254204b89 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/AbstractSqlPagingQueryProvider.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/AbstractSqlPagingQueryProvider.java @@ -18,6 +18,10 @@ package org.springframework.batch.item.database.support; import javax.sql.DataSource; import org.springframework.util.Assert; +import org.springframework.dao.InvalidDataAccessApiUsageException; + +import java.util.List; +import java.util.ArrayList; /** * Abstract SQL Paging Query Provider to serve as a base class for all provided SQL paging query providers. @@ -43,6 +47,11 @@ public abstract class AbstractSqlPagingQueryProvider implements PagingQueryProvi private String sortKey; + private int parameterCount; + + private boolean usingNamedParameters; + + /** * @param selectClause SELECT clause part of SQL query string */ @@ -124,6 +133,24 @@ public abstract class AbstractSqlPagingQueryProvider implements PagingQueryProvi return sortKey; } + + public int getParameterCount() { + return parameterCount; + } + + public boolean isUsingNamedParameters() { + return usingNamedParameters; + } + + /** + * + * @return place holder for sortKey. Will vary depending on whethernamed parameters or traditional placeholders + * are used in query strings. + */ + protected String getSortKeyPlaceHolder() { + return usingNamedParameters ? ":_sortKey" : "?"; + } + /** * Check mandatory properties. * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() @@ -133,6 +160,20 @@ public abstract class AbstractSqlPagingQueryProvider implements PagingQueryProvi Assert.hasLength(selectClause, "selectClause must be specified"); Assert.hasLength(fromClause, "fromClause must be specified"); Assert.hasLength(sortKey, "sortKey must be specified"); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ").append(selectClause); + sql.append(" FROM ").append(fromClause); + if (whereClause != null) { + sql.append(" WHERE ").append(whereClause); + } + List namedParameters = new ArrayList(); + parameterCount = JdbcParameterUtils.countParameterPlaceholders(sql.toString(), namedParameters); + if (namedParameters.size() > 0) { + if (parameterCount != namedParameters.size()) { + throw new InvalidDataAccessApiUsageException("You can't use both named parameters and classic \"?\" placeholders: " + sql); + } + usingNamedParameters = true; + } } public abstract String generateFirstPageQuery(int pageSize); diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/Db2PagingQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/Db2PagingQueryProvider.java index 8ebd2a831..37265212a 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/Db2PagingQueryProvider.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/Db2PagingQueryProvider.java @@ -26,8 +26,12 @@ public class Db2PagingQueryProvider extends SqlWindowingPagingQueryProvider { StringBuilder sql = new StringBuilder(); sql.append("SELECT ").append(getSelectClause()); sql.append(" FROM ").append(getFromClause()); - sql.append(" WHERE ").append(getSortKey()).append(" > ?"); - sql.append(getWhereClause() == null ? "" : " AND " + getWhereClause()); + sql.append(" WHERE "); + if (getWhereClause() != null) { + sql.append(getWhereClause()); + sql.append(" AND "); + } + sql.append(getSortKey()).append(" > ").append(getSortKeyPlaceHolder()); sql.append(" ORDER BY ").append(getSortKey()).append(" ASC"); sql.append(" FETCH FIRST ").append(pageSize).append(" ROWS ONLY"); diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/HsqlPagingQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/HsqlPagingQueryProvider.java index 2b34c3206..980f64f5d 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/HsqlPagingQueryProvider.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/HsqlPagingQueryProvider.java @@ -39,8 +39,12 @@ public class HsqlPagingQueryProvider extends AbstractSqlPagingQueryProvider { StringBuilder sql = new StringBuilder(); sql.append("SELECT ").append("TOP ").append(pageSize).append(" ").append(getSelectClause()); sql.append(" FROM ").append(getFromClause()); - sql.append(" WHERE ").append(getSortKey()).append(" > ?"); - sql.append(getWhereClause() == null ? "" : " AND " + getWhereClause()); + sql.append(" WHERE "); + if (getWhereClause() != null) { + sql.append(getWhereClause()); + sql.append(" AND "); + } + sql.append(getSortKey()).append(" > ").append(getSortKeyPlaceHolder()); sql.append(" ORDER BY ").append(getSortKey()).append(" ASC"); return sql.toString(); diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/JdbcParameterUtils.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/JdbcParameterUtils.java new file mode 100644 index 000000000..b37dd3a81 --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/JdbcParameterUtils.java @@ -0,0 +1,115 @@ +/* + * Copyright 2002-2008 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 + * + * http://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.Map; +import java.util.HashMap; +import java.util.List; + +/** + * Helper methods for SQL statement parameter parsing. + * + * Only intended for internal use. + * + * @author Thomas Risberg + * @author Juergen Hoeller + * @since 2.0 + */ +public class JdbcParameterUtils { + + /** + * Count the occurrences of the character placeholder in an SQL string + * sql. The character placeholder is not counted if it appears + * within a literal, that is, surrounded by single or double quotes. This method will + * count traditional placeholders in the form of a question mark ('?') as well as + * named parameters indicated with a leading ':' or '&'. + * + * The code for this method is taken from an early version of the + * {@link org.springframework.jdbc.core.namedparam.NamedParameterUtils} + * class. That method was later removed after some refactoring, but the code + * is useful here for the Spring Batch project. The code has been altered to better + * suite the batch processing requirements. + * + * @param sql String to search in. Returns 0 if the given String is null. + */ + public static int countParameterPlaceholders(String sql, List namedParameterHolder ) { + if (sql == null) { + return 0; + } + + char[] statement = sql.toCharArray(); + boolean withinQuotes = false; + Map namedParameters = new HashMap(); + char currentQuote = '-'; + int parameterCount = 0; + int i = 0; + while (i < statement.length) { + if (withinQuotes) { + if (statement[i] == currentQuote) { + withinQuotes = false; + currentQuote = '-'; + } + } + else { + if (statement[i] == '"' || statement[i] == '\'') { + withinQuotes = true; + currentQuote = statement[i]; + } + else { + if (statement[i] == ':' || statement[i] == '&') { + int j = i + 1; + StringBuilder parameter = new StringBuilder(); + while (j < statement.length && parameterNameContinues(statement, j)) { + parameter.append(statement[j]); + j++; + } + if (j - i > 1) { + if (!namedParameters.containsKey(parameter.toString())) { + parameterCount++; + namedParameters.put(parameter.toString(), parameter); + i = j - 1; + } + } + } + else { + if (statement[i] == '?') { + parameterCount++; + } + } + } + } + i++; + } + if (namedParameterHolder != null) { + namedParameterHolder.addAll(namedParameters.keySet()); + } + return parameterCount; + } + + /** + * Determine whether a parameter name continues at the current position, + * that is, does not end delimited by any whitespace character yet. + * @param statement the SQL statement + * @param pos the position within the statement + */ + private static boolean parameterNameContinues(char[] statement, int pos) { + return (statement[pos] != ' ' && statement[pos] != ',' && statement[pos] != ')' && + statement[pos] != '"' && statement[pos] != '\'' && statement[pos] != '|' && + statement[pos] != ';' && statement[pos] != '\n' && statement[pos] != '\r'); + } + +} diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/MySqlPagingQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/MySqlPagingQueryProvider.java index 3a47ec90f..1505efde8 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/MySqlPagingQueryProvider.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/MySqlPagingQueryProvider.java @@ -40,8 +40,12 @@ public class MySqlPagingQueryProvider extends AbstractSqlPagingQueryProvider { StringBuilder sql = new StringBuilder(); sql.append("SELECT ").append(getSelectClause()); sql.append(" FROM ").append(getFromClause()); - sql.append(" WHERE ").append(getSortKey()).append(" > ?"); - sql.append(getWhereClause() == null ? "" : " AND " + getWhereClause()); + sql.append(" WHERE "); + if (getWhereClause() != null) { + sql.append(getWhereClause()); + sql.append(" AND "); + } + sql.append(getSortKey()).append(" > ").append(getSortKeyPlaceHolder()); sql.append(" ORDER BY ").append(getSortKey()).append(" ASC"); sql.append(" LIMIT ").append(pageSize); 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 5c78c433d..97b54530b 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 @@ -14,8 +14,12 @@ public class OraclePagingQueryProvider extends SqlWindowingPagingQueryProvider { StringBuilder sql = new StringBuilder(); sql.append("SELECT ").append(getSelectClause()); sql.append(" FROM ").append(getFromClause()); - sql.append(" WHERE ROWNUM <= ").append(pageSize); - sql.append(getWhereClause() == null ? "" : " AND " + getWhereClause()); + sql.append(" WHERE "); + if (getWhereClause() != null) { + sql.append(getWhereClause()); + sql.append(" AND "); + } + sql.append("ROWNUM <= ").append(pageSize); sql.append(" ORDER BY ").append(getSortKey()).append(" ASC"); return sql.toString(); @@ -26,9 +30,13 @@ public class OraclePagingQueryProvider extends SqlWindowingPagingQueryProvider { StringBuilder sql = new StringBuilder(); sql.append("SELECT ").append(getSelectClause()); sql.append(" FROM ").append(getFromClause()); - sql.append(" WHERE ").append(getSortKey()).append(" > ?"); + sql.append(" WHERE "); + if (getWhereClause() != null) { + sql.append(getWhereClause()); + sql.append(" AND "); + } + sql.append(getSortKey()).append(" > ").append(getSortKeyPlaceHolder()); sql.append(" AND ROWNUM <= ").append(pageSize); - sql.append(getWhereClause() == null ? "" : " AND " + getWhereClause()); sql.append(" ORDER BY ").append(getSortKey()).append(" ASC"); return sql.toString(); diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/PagingQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/PagingQueryProvider.java index 245e6ae32..3f868e0dd 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/PagingQueryProvider.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/PagingQueryProvider.java @@ -61,4 +61,17 @@ public interface PagingQueryProvider { */ String generateJumpToItemQuery(int itemIndex, int pageSize); + /** + * The number of parameters that are declared in the query + * @return number of parameters + */ + int getParameterCount(); + + /** + * Indicate whether the generated queries use named parameter syntax. + * + * @return true if named parameter syntax is used + */ + boolean isUsingNamedParameters(); + } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/PostgresPagingQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/PostgresPagingQueryProvider.java index 96a60abf9..804ca3a10 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/PostgresPagingQueryProvider.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/PostgresPagingQueryProvider.java @@ -25,8 +25,12 @@ public class PostgresPagingQueryProvider extends AbstractSqlPagingQueryProvider StringBuilder sql = new StringBuilder(); sql.append("SELECT ").append(getSelectClause()); sql.append(" FROM ").append(getFromClause()); - sql.append(" WHERE ").append(getSortKey()).append(" > ?"); - sql.append(getWhereClause() == null ? "" : " AND " + getWhereClause()); + sql.append(" WHERE "); + if (getWhereClause() != null) { + sql.append(getWhereClause()); + sql.append(" AND "); + } + sql.append(getSortKey()).append(" > ").append(getSortKeyPlaceHolder()); sql.append(" ORDER BY ").append(getSortKey()).append(" ASC"); sql.append(" LIMIT ").append(pageSize); diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlServerPagingQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlServerPagingQueryProvider.java index 02acc7cd3..51e88fc60 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlServerPagingQueryProvider.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SqlServerPagingQueryProvider.java @@ -25,8 +25,12 @@ public class SqlServerPagingQueryProvider extends SqlWindowingPagingQueryProvide StringBuilder sql = new StringBuilder(); sql.append("SELECT ").append("TOP ").append(pageSize).append(" ").append(getSelectClause()); sql.append(" FROM ").append(getFromClause()); - sql.append(" WHERE ").append(getSortKey()).append(" > ?"); - sql.append(getWhereClause() == null ? "" : " AND " + getWhereClause()); + sql.append(" WHERE "); + if (getWhereClause() != null) { + sql.append(getWhereClause()); + sql.append(" AND "); + } + sql.append(getSortKey()).append(" > ").append(getSortKeyPlaceHolder()); sql.append(" ORDER BY ").append(getSortKey()).append(" ASC"); return sql.toString(); 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 686cb9475..03a06825d 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 @@ -42,8 +42,13 @@ public class SqlWindowingPagingQueryProvider extends AbstractSqlPagingQueryProvi sql.append("SELECT * FROM ( "); sql.append("SELECT ").append(getSelectClause()).append(", "); sql.append("ROW_NUMBER() OVER (ORDER BY ").append(getSortKey()).append(" ASC) AS ROW_NUMBER"); - sql.append(" FROM ").append(getFromClause()).append(" WHERE ").append(getSortKey()).append(" > ?"); - sql.append(getWhereClause() == null ? "" : " AND " + getWhereClause()); + sql.append(" FROM ").append(getFromClause()); + sql.append(" WHERE "); + if (getWhereClause() != null) { + sql.append(getWhereClause()); + sql.append(" AND "); + } + sql.append(getSortKey()).append(" > ").append(getSortKeyPlaceHolder()); sql.append(") WHERE ROW_NUMBER <= ").append(pageSize); return sql.toString(); diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SybasePagingQueryProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SybasePagingQueryProvider.java index 24bcf39eb..075dcaaa3 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SybasePagingQueryProvider.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/support/SybasePagingQueryProvider.java @@ -25,8 +25,12 @@ public class SybasePagingQueryProvider extends SqlWindowingPagingQueryProvider { StringBuilder sql = new StringBuilder(); sql.append("SELECT ").append("TOP ").append(pageSize).append(" ").append(getSelectClause()); sql.append(" FROM ").append(getFromClause()); - sql.append(" WHERE ").append(getSortKey()).append(" > ?"); - sql.append(getWhereClause() == null ? "" : " AND " + getWhereClause()); + sql.append(" WHERE "); + if (getWhereClause() != null) { + sql.append(getWhereClause()); + sql.append(" AND "); + } + sql.append(getSortKey()).append(" > ").append(getSortKeyPlaceHolder()); sql.append(" ORDER BY ").append(getSortKey()).append(" ASC"); return sql.toString(); diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/AbstractPagingItemReaderParameterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/AbstractPagingItemReaderParameterTests.java new file mode 100644 index 000000000..a2b593885 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/AbstractPagingItemReaderParameterTests.java @@ -0,0 +1,55 @@ +package org.springframework.batch.item.database; + +import org.junit.Before; +import org.junit.After; +import org.junit.Test; +import org.junit.Assert; +import org.junit.runner.RunWith; +import org.springframework.batch.item.ItemStream; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.ExecutionContext; +import org.springframework.batch.item.sample.Foo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.ContextConfiguration; + +import javax.sql.DataSource; + +/** + * @author trisberg + */ +public abstract class AbstractPagingItemReaderParameterTests { + protected ItemReader tested; + protected ExecutionContext executionContext = new ExecutionContext(); + @Autowired + protected DataSource dataSource; + + @Before + public void setUp() throws Exception { + tested = getItemReader(); + ((ItemStream)tested).open(executionContext); + } + + @After + public void tearDown() { + ((ItemStream)tested).close(executionContext); + } + + @Test + public void testRead() throws Exception { + + Foo foo3 = tested.read(); + Assert.assertEquals(3, foo3.getValue()); + + Foo foo4 = tested.read(); + Assert.assertEquals(4, foo4.getValue()); + + Foo foo5 = tested.read(); + Assert.assertEquals(5, foo5.getValue()); + + Object o = tested.read(); + Assert.assertNull(o); + } + + protected abstract ItemReader getItemReader() throws Exception; +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/IbatisPagingItemReaderParameterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/IbatisPagingItemReaderParameterTests.java new file mode 100644 index 000000000..e357c30bd --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/IbatisPagingItemReaderParameterTests.java @@ -0,0 +1,45 @@ +package org.springframework.batch.item.database; + +import org.junit.runner.RunWith; +import org.springframework.batch.item.sample.Foo; +import org.springframework.batch.item.ItemReader; +import org.springframework.orm.ibatis.SqlMapClientFactoryBean; +import org.springframework.core.io.ClassPathResource; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.ContextConfiguration; +import com.ibatis.sqlmap.client.SqlMapClient; + +import java.util.Collections; + +@SuppressWarnings("unchecked") +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "/org/springframework/batch/item/database/data-source-context.xml") +public class IbatisPagingItemReaderParameterTests extends AbstractPagingItemReaderParameterTests { + + protected ItemReader getItemReader() throws Exception { + SqlMapClientFactoryBean factory = new SqlMapClientFactoryBean(); + factory.setConfigLocation(new ClassPathResource("ibatis-config.xml", getClass())); + factory.setDataSource(dataSource); + factory.afterPropertiesSet(); + SqlMapClient sqlMapClient = createSqlMapClient(); + + IbatisPagingItemReader reader = new IbatisPagingItemReader(); + reader.setQueryId("getFoos3AndUp"); + reader.setParameterValues(Collections.singletonMap("limit", 3)); + reader.setSqlMapClient(sqlMapClient); + reader.setSaveState(true); + + reader.afterPropertiesSet(); + + return reader; + } + + private SqlMapClient createSqlMapClient() throws Exception { + SqlMapClientFactoryBean factory = new SqlMapClientFactoryBean(); + factory.setConfigLocation(new ClassPathResource("ibatis-config.xml", getClass())); + factory.setDataSource(dataSource); + factory.afterPropertiesSet(); + return (SqlMapClient) factory.getObject(); + } + +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JdbcPagingItemReaderClassicParameterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JdbcPagingItemReaderClassicParameterTests.java new file mode 100644 index 000000000..02b3dfdea --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JdbcPagingItemReaderClassicParameterTests.java @@ -0,0 +1,52 @@ +package org.springframework.batch.item.database; + +import org.junit.runner.RunWith; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.batch.item.sample.Foo; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.database.support.HsqlPagingQueryProvider; +import org.springframework.jdbc.core.simple.ParameterizedRowMapper; + +import javax.persistence.EntityManagerFactory; +import java.util.Collections; +import java.sql.ResultSet; +import java.sql.SQLException; + +@SuppressWarnings("unchecked") +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "/org/springframework/batch/item/database/JdbcPagingItemReaderParameterTests-context.xml") +public class JdbcPagingItemReaderClassicParameterTests extends AbstractPagingItemReaderParameterTests { + + protected ItemReader getItemReader() throws Exception { + + JdbcPagingItemReader reader = new JdbcPagingItemReader(); + reader.setDataSource(dataSource); + HsqlPagingQueryProvider queryProvider = new HsqlPagingQueryProvider(); + queryProvider.setSelectClause("select ID, NAME, VALUE"); + queryProvider.setFromClause("from T_FOOS"); + queryProvider.setWhereClause("where VALUE >= ?"); + queryProvider.setSortKey("ID"); + reader.setParameterValues(Collections.singletonMap("limit", 3)); + reader.setQueryProvider(queryProvider); + reader.setParameterizedRowMapper( + new ParameterizedRowMapper() { + public Foo mapRow(ResultSet rs, int i) throws SQLException { + Foo foo = new Foo(); + foo.setId(rs.getInt(1)); + foo.setName(rs.getString(2)); + foo.setValue(rs.getInt(3)); + return foo; + } + } + ); + reader.setPageSize(3); + reader.afterPropertiesSet(); + reader.setSaveState(true); + + return reader; + + } + +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JdbcPagingItemReaderNamedParameterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JdbcPagingItemReaderNamedParameterTests.java new file mode 100644 index 000000000..c66c6cc04 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JdbcPagingItemReaderNamedParameterTests.java @@ -0,0 +1,50 @@ +package org.springframework.batch.item.database; + +import org.junit.runner.RunWith; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.batch.item.sample.Foo; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.database.support.HsqlPagingQueryProvider; +import org.springframework.jdbc.core.simple.ParameterizedRowMapper; + +import java.util.Collections; +import java.sql.ResultSet; +import java.sql.SQLException; + +@SuppressWarnings("unchecked") +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "/org/springframework/batch/item/database/JdbcPagingItemReaderParameterTests-context.xml") +public class JdbcPagingItemReaderNamedParameterTests extends AbstractPagingItemReaderParameterTests { + + protected ItemReader getItemReader() throws Exception { + + JdbcPagingItemReader reader = new JdbcPagingItemReader(); + reader.setDataSource(dataSource); + HsqlPagingQueryProvider queryProvider = new HsqlPagingQueryProvider(); + queryProvider.setSelectClause("select ID, NAME, VALUE"); + queryProvider.setFromClause("from T_FOOS"); + queryProvider.setWhereClause("where VALUE >= :limit"); + queryProvider.setSortKey("ID"); + reader.setParameterValues(Collections.singletonMap("limit", 3)); + reader.setQueryProvider(queryProvider); + reader.setParameterizedRowMapper( + new ParameterizedRowMapper() { + public Foo mapRow(ResultSet rs, int i) throws SQLException { + Foo foo = new Foo(); + foo.setId(rs.getInt(1)); + foo.setName(rs.getString(2)); + foo.setValue(rs.getInt(3)); + return foo; + } + } + ); + reader.setPageSize(2); + reader.afterPropertiesSet(); + reader.setSaveState(true); + + return reader; + + } + +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JpaPagingItemReaderParameterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JpaPagingItemReaderParameterTests.java new file mode 100644 index 000000000..74e7c4123 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/JpaPagingItemReaderParameterTests.java @@ -0,0 +1,40 @@ +package org.springframework.batch.item.database; + +import org.junit.runner.RunWith; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.batch.item.sample.Foo; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.ExecutionContext; +import org.springframework.orm.ibatis.SqlMapClientFactoryBean; +import org.springframework.core.io.ClassPathResource; +import org.springframework.beans.factory.annotation.Autowired; +import com.ibatis.sqlmap.client.SqlMapClient; + +import javax.persistence.EntityManagerFactory; +import java.util.Collections; + +@SuppressWarnings("unchecked") +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class JpaPagingItemReaderParameterTests extends AbstractPagingItemReaderParameterTests { + + @Autowired + private EntityManagerFactory entityManagerFactory; + + protected ItemReader getItemReader() throws Exception { + + String jpqlQuery = "select f from Foo f where f.value >= :limit"; + + JpaPagingItemReader reader = new JpaPagingItemReader(); + reader.setQueryString(jpqlQuery); + reader.setParameterValues(Collections.singletonMap("limit", 3)); + reader.setEntityManagerFactory(entityManagerFactory); + reader.setPageSize(3); + reader.afterPropertiesSet(); + reader.setSaveState(true); + + return reader; + } + +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/Db2PagingQueryProviderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/Db2PagingQueryProviderTests.java index 153d1ae17..745faf07c 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/Db2PagingQueryProviderTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/Db2PagingQueryProviderTests.java @@ -22,7 +22,7 @@ public class Db2PagingQueryProviderTests extends AbstractSqlPagingQueryProviderT @Test @Override public void testGenerateRemainingPagesQuery() { - String sql = "SELECT id, name, age FROM foo WHERE id > ? AND bar = 1 ORDER BY id ASC FETCH FIRST 100 ROWS ONLY"; + String sql = "SELECT id, name, age FROM foo WHERE bar = 1 AND id > ? ORDER BY id ASC FETCH FIRST 100 ROWS ONLY"; String s = pagingQueryProvider.generateRemainingPagesQuery(pageSize); Assert.assertEquals("", sql, s); } 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 f641ec196..76ee1b9d2 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 @@ -87,7 +87,7 @@ public class DerbyPagingQueryProviderTests extends AbstractSqlPagingQueryProvide @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 id > ? AND bar = 1) WHERE ROW_NUMBER <= 100"; + 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 s = pagingQueryProvider.generateRemainingPagesQuery(pageSize); Assert.assertEquals("", sql, s); } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/HsqlPagingQueryProviderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/HsqlPagingQueryProviderTests.java index cc3e13db1..b6969196e 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/HsqlPagingQueryProviderTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/HsqlPagingQueryProviderTests.java @@ -37,7 +37,7 @@ public class HsqlPagingQueryProviderTests extends AbstractSqlPagingQueryProvider @Test @Override public void testGenerateRemainingPagesQuery() { - String sql = "SELECT TOP 100 id, name, age FROM foo WHERE id > ? AND bar = 1 ORDER BY id ASC"; + String sql = "SELECT TOP 100 id, name, age FROM foo WHERE bar = 1 AND id > ? ORDER BY id ASC"; String s = pagingQueryProvider.generateRemainingPagesQuery(pageSize); Assert.assertEquals("", sql, s); } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/JdbcParameterUtilsTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/JdbcParameterUtilsTests.java new file mode 100644 index 000000000..f6ce2e3b7 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/JdbcParameterUtilsTests.java @@ -0,0 +1,60 @@ +/* + * Copyright 2002-2008 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 + * + * http://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 static org.junit.Assert.*; +import org.junit.Test; + +import java.util.List; +import java.util.ArrayList; + +/** + * @author Thomas Risberg + */ +public class JdbcParameterUtilsTests { + + @Test + public void testCountParameterPlaceholders() { + assertEquals(0, JdbcParameterUtils.countParameterPlaceholders(null, null)); + assertEquals(0, JdbcParameterUtils.countParameterPlaceholders("", null)); + assertEquals(1, JdbcParameterUtils.countParameterPlaceholders("?", null)); + assertEquals(1, JdbcParameterUtils.countParameterPlaceholders("The \"big\" ? 'bad wolf'", null)); + assertEquals(2, JdbcParameterUtils.countParameterPlaceholders("The big ?? bad wolf", null)); + assertEquals(3, JdbcParameterUtils.countParameterPlaceholders("The big ? ? bad ? wolf", null)); + assertEquals(1, JdbcParameterUtils.countParameterPlaceholders("The \"big?\" 'ba''ad?' ? wolf", null)); + assertEquals(1, JdbcParameterUtils.countParameterPlaceholders(":parameter", null)); + assertEquals(1, JdbcParameterUtils.countParameterPlaceholders("The \"big\" :parameter 'bad wolf'", null)); + assertEquals(1, JdbcParameterUtils.countParameterPlaceholders("The big :parameter :parameter bad wolf", null)); + assertEquals(2, JdbcParameterUtils.countParameterPlaceholders("The big :parameter :newpar :parameter bad wolf", null)); + assertEquals(2, JdbcParameterUtils.countParameterPlaceholders("The big :parameter, :newpar, :parameter bad wolf", null)); + assertEquals(1, JdbcParameterUtils.countParameterPlaceholders("The \"big:\" 'ba''ad:p' :parameter wolf", null)); + assertEquals(1, JdbcParameterUtils.countParameterPlaceholders("¶meter", null)); + assertEquals(1, JdbcParameterUtils.countParameterPlaceholders("The \"big\" ¶meter 'bad wolf'", null)); + assertEquals(1, JdbcParameterUtils.countParameterPlaceholders("The big ¶meter ¶meter bad wolf", null)); + assertEquals(2, JdbcParameterUtils.countParameterPlaceholders("The big ¶meter &newparameter ¶meter bad wolf", null)); + assertEquals(2, JdbcParameterUtils.countParameterPlaceholders("The big ¶meter, &newparameter, ¶meter bad wolf", null)); + assertEquals(1, JdbcParameterUtils.countParameterPlaceholders("The \"big &x \" 'ba''ad&p' ¶meter wolf", null)); + assertEquals(2, JdbcParameterUtils.countParameterPlaceholders("The big :parameter, &newparameter, ¶meter bad wolf", null)); + assertEquals(2, JdbcParameterUtils.countParameterPlaceholders("The big :parameter, &sameparameter, &sameparameter bad wolf", null)); + assertEquals(2, JdbcParameterUtils.countParameterPlaceholders("The big :parameter, :sameparameter, :sameparameter bad wolf", null)); + assertEquals(0, JdbcParameterUtils.countParameterPlaceholders("xxx & yyy", null)); + List l = new ArrayList(); + assertEquals(3, JdbcParameterUtils.countParameterPlaceholders("select :par1, :par2 :par3", l)); + assertEquals(3, l.size()); + } + +} 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 2e928c575..d6d03381d 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 @@ -37,7 +37,7 @@ public class MySqlPagingQueryProviderTests extends AbstractSqlPagingQueryProvide @Test @Override public void testGenerateRemainingPagesQuery() { - String sql = "SELECT id, name, age FROM foo WHERE id > ? AND bar = 1 ORDER BY id ASC LIMIT 100"; + String sql = "SELECT id, name, age FROM foo WHERE bar = 1 AND id > ? ORDER BY id ASC LIMIT 100"; String s = pagingQueryProvider.generateRemainingPagesQuery(pageSize); Assert.assertEquals("", sql, s); } 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 a38037c9d..4637bc3d5 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 @@ -15,14 +15,14 @@ public class OraclePagingQueryProviderTests extends AbstractSqlPagingQueryProvid @Test @Override public void testGenerateFirstPageQuery() { - String sql = "SELECT id, name, age FROM foo WHERE ROWNUM <= 100 AND bar = 1 ORDER BY id ASC"; + String sql = "SELECT id, name, age FROM foo WHERE bar = 1 AND ROWNUM <= 100 ORDER BY id ASC"; String s = pagingQueryProvider.generateFirstPageQuery(pageSize); Assert.assertEquals("", sql, s); } @Test @Override public void testGenerateRemainingPagesQuery() { - String sql = "SELECT id, name, age FROM foo WHERE id > ? AND ROWNUM <= 100 AND bar = 1 ORDER BY id ASC"; + String sql = "SELECT id, name, age FROM foo WHERE bar = 1 AND id > ? AND ROWNUM <= 100 ORDER BY id ASC"; String s = pagingQueryProvider.generateRemainingPagesQuery(pageSize); Assert.assertEquals("", sql, s); } 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 7672d791c..8c039b876 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 @@ -22,7 +22,7 @@ public class PostgresPagingQueryProviderTests extends AbstractSqlPagingQueryProv @Test @Override public void testGenerateRemainingPagesQuery() { - String sql = "SELECT id, name, age FROM foo WHERE id > ? AND bar = 1 ORDER BY id ASC LIMIT 100"; + String sql = "SELECT id, name, age FROM foo WHERE bar = 1 AND id > ? ORDER BY id ASC LIMIT 100"; String s = pagingQueryProvider.generateRemainingPagesQuery(pageSize); Assert.assertEquals("", sql, s); } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlServerPagingQueryProviderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlServerPagingQueryProviderTests.java index 50f17fa04..c02d93b81 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlServerPagingQueryProviderTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlServerPagingQueryProviderTests.java @@ -22,7 +22,7 @@ public class SqlServerPagingQueryProviderTests extends AbstractSqlPagingQueryPro @Test @Override public void testGenerateRemainingPagesQuery() { - String sql = "SELECT TOP 100 id, name, age FROM foo WHERE id > ? AND bar = 1 ORDER BY id ASC"; + String sql = "SELECT TOP 100 id, name, age FROM foo WHERE bar = 1 AND id > ? ORDER BY id ASC"; String s = pagingQueryProvider.generateRemainingPagesQuery(pageSize); Assert.assertEquals("", sql, s); } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlWindowingPagingQueryProviderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlWindowingPagingQueryProviderTests.java index 2496f8fcc..e65447f32 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlWindowingPagingQueryProviderTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SqlWindowingPagingQueryProviderTests.java @@ -37,7 +37,7 @@ public class SqlWindowingPagingQueryProviderTests extends AbstractSqlPagingQuery @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 id > ? AND bar = 1) WHERE ROW_NUMBER <= 100"; + 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 s = pagingQueryProvider.generateRemainingPagesQuery(pageSize); assertEquals("", sql, s); } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SybasePagingQueryProviderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SybasePagingQueryProviderTests.java index 111b7cf73..0a2c35bd4 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SybasePagingQueryProviderTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/database/support/SybasePagingQueryProviderTests.java @@ -22,7 +22,7 @@ public class SybasePagingQueryProviderTests extends AbstractSqlPagingQueryProvid @Test @Override public void testGenerateRemainingPagesQuery() { - String sql = "SELECT TOP 100 id, name, age FROM foo WHERE id > ? AND bar = 1 ORDER BY id ASC"; + String sql = "SELECT TOP 100 id, name, age FROM foo WHERE bar = 1 AND id > ? ORDER BY id ASC"; String s = pagingQueryProvider.generateRemainingPagesQuery(pageSize); Assert.assertEquals("", sql, s); } diff --git a/spring-batch-infrastructure/src/test/resources/org/springframework/batch/item/database/JdbcPagingItemReaderParameterTests-context.xml b/spring-batch-infrastructure/src/test/resources/org/springframework/batch/item/database/JdbcPagingItemReaderParameterTests-context.xml new file mode 100644 index 000000000..eee73841b --- /dev/null +++ b/spring-batch-infrastructure/src/test/resources/org/springframework/batch/item/database/JdbcPagingItemReaderParameterTests-context.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-batch-infrastructure/src/test/resources/org/springframework/batch/item/database/JpaPagingItemReaderParameterTests-context.xml b/spring-batch-infrastructure/src/test/resources/org/springframework/batch/item/database/JpaPagingItemReaderParameterTests-context.xml new file mode 100644 index 000000000..500260735 --- /dev/null +++ b/spring-batch-infrastructure/src/test/resources/org/springframework/batch/item/database/JpaPagingItemReaderParameterTests-context.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-batch-infrastructure/src/test/resources/org/springframework/batch/item/database/ibatis-foo.xml b/spring-batch-infrastructure/src/test/resources/org/springframework/batch/item/database/ibatis-foo.xml index e3be45fe9..bceca92d4 100644 --- a/spring-batch-infrastructure/src/test/resources/org/springframework/batch/item/database/ibatis-foo.xml +++ b/spring-batch-infrastructure/src/test/resources/org/springframework/batch/item/database/ibatis-foo.xml @@ -22,6 +22,10 @@ select ID, NAME, VALUE from T_FOOS + +