class Neo4jItemReader<T> extends AbstractPaginatedDataItemReader<T> implements InitializingBean {

	protected Log logger = LogFactory.getLog(getClass());

	private SessionFactory sessionFactory;

	private String startStatement;

	private String returnStatement;

	private String matchStatement;

	private String whereStatement;

	private String orderByStatement;

	private Class<T> targetType;

	private Map<String, Object> parameterValues;

	/**
	 * Optional parameters to be used in the cypher query.
	 * @param parameterValues the parameter values to be used in the cypher query
	 */
	public void setParameterValues(Map<String, Object> parameterValues) {
		this.parameterValues = parameterValues;
	}

	protected final Map<String, Object> getParameterValues() {
		return this.parameterValues;
	}

	/**
	 * The start segment of the cypher query. START is prepended to the statement provided
	 * and should <em>not</em> be included.
	 * @param startStatement the start fragment of the cypher query.
	 */
	public void setStartStatement(String startStatement) {
		this.startStatement = startStatement;
	}

	/**
	 * The return statement of the cypher query. RETURN is prepended to the statement
	 * provided and should <em>not</em> be included
	 * @param returnStatement the return fragment of the cypher query.
	 */
	public void setReturnStatement(String returnStatement) {
		this.returnStatement = returnStatement;
	}

	/**
	 * An optional match fragment of the cypher query. MATCH is prepended to the statement
	 * provided and should <em>not</em> be included.
	 * @param matchStatement the match fragment of the cypher query
	 */
	public void setMatchStatement(String matchStatement) {
		this.matchStatement = matchStatement;
	}

	/**
	 * An optional where fragment of the cypher query. WHERE is prepended to the statement
	 * provided and should <em>not</em> be included.
	 * @param whereStatement where fragment of the cypher query
	 */
	public void setWhereStatement(String whereStatement) {
		this.whereStatement = whereStatement;
	}

	/**
	 * A list of properties to order the results by. This is required so that subsequent
	 * page requests pull back the segment of results correctly. ORDER BY is prepended to
	 * the statement provided and should <em>not</em> be included.
	 * @param orderByStatement order by fragment of the cypher query.
	 */
	public void setOrderByStatement(String orderByStatement) {
		this.orderByStatement = orderByStatement;
	}

	protected SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	/**
	 * Establish the session factory for the reader.
	 * @param sessionFactory the factory to use for the reader.
	 */
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	/**
	 * The object type to be returned from each call to {@link #read()}
	 * @param targetType the type of object to return.
	 */
	public void setTargetType(Class<T> targetType) {
		this.targetType = targetType;
	}

	protected final Class<T> getTargetType() {
		return this.targetType;
	}

	protected String generateLimitCypherQuery() {
		StringBuilder query = new StringBuilder(128);

		query.append("START ").append(startStatement);
		query.append(matchStatement != null ? " MATCH " + matchStatement : "");
		query.append(whereStatement != null ? " WHERE " + whereStatement : "");
		query.append(" RETURN ").append(returnStatement);
		query.append(" ORDER BY ").append(orderByStatement);
		query.append(" SKIP ").append(pageSize * page);
		query.append(" LIMIT ").append(pageSize);

		String resultingQuery = query.toString();

		if (logger.isDebugEnabled()) {
			logger.debug(resultingQuery);
		}

		return resultingQuery;
	}

	/**
	 * Checks mandatory properties
	 *
	 * @see InitializingBean#afterPropertiesSet()
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
		Assert.state(sessionFactory != null, "A SessionFactory is required");
		Assert.state(targetType != null, "The type to be returned is required");
		Assert.state(StringUtils.hasText(startStatement), "A START statement is required");
		Assert.state(StringUtils.hasText(returnStatement), "A RETURN statement is required");
		Assert.state(StringUtils.hasText(orderByStatement), "A ORDER BY statement is required");
	}

	@SuppressWarnings("unchecked")
	@Override
	protected Iterator<T> doPageRead() {<FILL_FUNCTION_BODY>}

}

		Session session = getSessionFactory().openSession();

		Iterable<T> queryResults = session.query(getTargetType(), generateLimitCypherQuery(), getParameterValues());

		if (queryResults != null) {
			return queryResults.iterator();
		}
		else {
			return new ArrayList<T>().iterator();
		}
	
<methods>public non-sealed void <init>() ,public void setPageSize(int) <variables>private final java.util.concurrent.locks.Lock lock,protected volatile int page,protected int pageSize,protected Iterator<T> results