class AbstractSqlPagingQueryProvider implements PagingQueryProvider {

	private String selectClause;

	private String fromClause;

	private String whereClause;

	private Map<String, Order> sortKeys = new LinkedHashMap<>();

	private String groupClause;

	private int parameterCount;

	private boolean usingNamedParameters;

	/**
	 * The setter for the group by clause
	 * @param groupClause SQL GROUP BY clause part of the SQL query string
	 */
	public void setGroupClause(String groupClause) {
		if (StringUtils.hasText(groupClause)) {
			this.groupClause = removeKeyWord("group by", groupClause);
		}
		else {
			this.groupClause = null;
		}
	}

	/**
	 * The getter for the group by clause
	 * @return SQL GROUP BY clause part of the SQL query string
	 */
	public String getGroupClause() {
		return this.groupClause;
	}

	/**
	 * @param selectClause SELECT clause part of SQL query string
	 */
	public void setSelectClause(String selectClause) {
		this.selectClause = removeKeyWord("select", selectClause);
	}

	/**
	 * @return SQL SELECT clause part of SQL query string
	 */
	protected String getSelectClause() {
		return selectClause;
	}

	/**
	 * @param fromClause FROM clause part of SQL query string
	 */
	public void setFromClause(String fromClause) {
		this.fromClause = removeKeyWord("from", fromClause);
	}

	/**
	 * @return SQL FROM clause part of SQL query string
	 */
	protected String getFromClause() {
		return fromClause;
	}

	/**
	 * @param whereClause WHERE clause part of SQL query string
	 */
	public void setWhereClause(String whereClause) {<FILL_FUNCTION_BODY>}

	/**
	 * @return SQL WHERE clause part of SQL query string
	 */
	protected String getWhereClause() {
		return whereClause;
	}

	/**
	 * @param sortKeys key to use to sort and limit page content
	 */
	public void setSortKeys(Map<String, Order> sortKeys) {
		this.sortKeys = sortKeys;
	}

	/**
	 * A Map&lt;String, Boolean&gt; of sort columns as the key and boolean for
	 * ascending/descending (ascending = true).
	 * @return sortKey key to use to sort and limit page content
	 */
	@Override
	public Map<String, Order> getSortKeys() {
		return sortKeys;
	}

	@Override
	public int getParameterCount() {
		return parameterCount;
	}

	@Override
	public boolean isUsingNamedParameters() {
		return usingNamedParameters;
	}

	/**
	 * The sort key placeholder will vary depending on whether named parameters or
	 * traditional placeholders are used in query strings.
	 * @return place holder for sortKey.
	 */
	@Override
	public String getSortKeyPlaceHolder(String keyName) {
		return usingNamedParameters ? ":_" + keyName : "?";
	}

	/**
	 * Check mandatory properties.
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 */
	@Override
	public void init(DataSource dataSource) throws Exception {
		Assert.notNull(dataSource, "A DataSource is required");
		Assert.hasLength(selectClause, "selectClause must be specified");
		Assert.hasLength(fromClause, "fromClause must be specified");
		Assert.notEmpty(sortKeys, "sortKey must be specified");
		StringBuilder sql = new StringBuilder(64);
		sql.append("SELECT ").append(selectClause);
		sql.append(" FROM ").append(fromClause);
		if (whereClause != null) {
			sql.append(" WHERE ").append(whereClause);
		}
		if (groupClause != null) {
			sql.append(" GROUP BY ").append(groupClause);
		}
		List<String> 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;
		}
	}

	/**
	 * Method generating the query string to be used for retrieving the first page. This
	 * method must be implemented in sub classes.
	 * @param pageSize number of rows to read per page
	 * @return query string
	 */
	@Override
	public abstract String generateFirstPageQuery(int pageSize);

	/**
	 * Method generating the query string to be used for retrieving the pages following
	 * the first page. This method must be implemented in sub classes.
	 * @param pageSize number of rows to read per page
	 * @return query string
	 */
	@Override
	public abstract String generateRemainingPagesQuery(int pageSize);

	private String removeKeyWord(String keyWord, String clause) {
		String temp = clause.trim();
		int length = keyWord.length();
		if (temp.toLowerCase().startsWith(keyWord) && Character.isWhitespace(temp.charAt(length))
				&& temp.length() > length + 1) {
			return temp.substring(length + 1);
		}
		else {
			return temp;
		}
	}

	/**
	 * @return sortKey key to use to sort and limit page content (without alias)
	 */
	@Override
	public Map<String, Order> getSortKeysWithoutAliases() {
		Map<String, Order> sortKeysWithoutAliases = new LinkedHashMap<>();

		for (Map.Entry<String, Order> sortKeyEntry : sortKeys.entrySet()) {
			String key = sortKeyEntry.getKey();
			int separator = key.indexOf('.');
			if (separator > 0) {
				int columnIndex = separator + 1;
				if (columnIndex < key.length()) {
					sortKeysWithoutAliases.put(key.substring(columnIndex), sortKeyEntry.getValue());
				}
			}
			else {
				sortKeysWithoutAliases.put(sortKeyEntry.getKey(), sortKeyEntry.getValue());
			}
		}

		return sortKeysWithoutAliases;
	}

}

		if (StringUtils.hasText(whereClause)) {
			this.whereClause = removeKeyWord("where", whereClause);
		}
		else {
			this.whereClause = null;
		}
	
<no_super_class>