class JpaPagingItemReader<T> extends AbstractPagingItemReader<T> {

	private EntityManagerFactory entityManagerFactory;

	private EntityManager entityManager;

	private final Map<String, Object> jpaPropertyMap = new HashMap<>();

	private String queryString;

	private JpaQueryProvider queryProvider;

	private Map<String, Object> parameterValues;

	private Map<String, Object> hintValues;

	private boolean transacted = true;// default value

	public JpaPagingItemReader() {
		setName(ClassUtils.getShortName(JpaPagingItemReader.class));
	}

	/**
	 * Create a query using an appropriate query provider (entityManager OR
	 * queryProvider).
	 */
	private Query createQuery() {
		if (queryProvider == null) {
			return entityManager.createQuery(queryString);
		}
		else {
			return queryProvider.createQuery();
		}
	}

	public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
		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<String, Object> parameterValues) {
		this.parameterValues = parameterValues;
	}

	/**
	 * Set the query hint values for the JPA query. Query hints can be used to give
	 * instructions to the JPA provider.
	 * @param hintValues a map where each key is the name of the hint, and the
	 * corresponding value is the hint's value.
	 * @since 5.2
	 */
	public void setHintValues(Map<String, Object> hintValues) {
		this.hintValues = hintValues;
	}

	/**
	 * By default (true) the EntityTransaction will be started and committed around the
	 * read. Can be overridden (false) in cases where the JPA implementation doesn't
	 * support a particular transaction. (e.g. Hibernate with a JTA transaction). NOTE:
	 * may cause problems in guaranteeing the object consistency in the
	 * EntityManagerFactory.
	 * @param transacted indicator
	 */
	public void setTransacted(boolean transacted) {
		this.transacted = transacted;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		super.afterPropertiesSet();

		if (queryProvider == null) {
			Assert.state(entityManagerFactory != null, "EntityManager is required when queryProvider is null");
			Assert.state(StringUtils.hasLength(queryString), "Query string is required when queryProvider is null");
		}
	}

	/**
	 * @param queryString JPQL query string
	 */
	public void setQueryString(String queryString) {
		this.queryString = queryString;
	}

	/**
	 * @param queryProvider JPA query provider
	 */
	public void setQueryProvider(JpaQueryProvider queryProvider) {
		this.queryProvider = queryProvider;
	}

	@Override
	protected void doOpen() throws Exception {
		super.doOpen();

		entityManager = entityManagerFactory.createEntityManager(jpaPropertyMap);
		if (entityManager == null) {
			throw new DataAccessResourceFailureException("Unable to obtain an EntityManager");
		}
		// set entityManager to queryProvider, so it participates
		// in JpaPagingItemReader's managed transaction
		if (queryProvider != null) {
			queryProvider.setEntityManager(entityManager);
		}

	}

	@Override
	@SuppressWarnings("unchecked")
	protected void doReadPage() {<FILL_FUNCTION_BODY>}

	@Override
	protected void doClose() throws Exception {
		entityManager.close();
		super.doClose();
	}

}


		EntityTransaction tx = null;

		if (transacted) {
			tx = entityManager.getTransaction();
			tx.begin();

			entityManager.flush();
			entityManager.clear();
		} // end if

		Query query = createQuery().setFirstResult(getPage() * getPageSize()).setMaxResults(getPageSize());

		if (parameterValues != null) {
			for (Map.Entry<String, Object> me : parameterValues.entrySet()) {
				query.setParameter(me.getKey(), me.getValue());
			}
		}

		if (this.hintValues != null) {
			this.hintValues.forEach(query::setHint);
		}

		if (results == null) {
			results = new CopyOnWriteArrayList<>();
		}
		else {
			results.clear();
		}

		if (!transacted) {
			List<T> queryResult = query.getResultList();
			for (T entity : queryResult) {
				entityManager.detach(entity);
				results.add(entity);
			} // end if
		}
		else {
			results.addAll(query.getResultList());
			tx.commit();
		} // end if
	
<methods>public void <init>() ,public void afterPropertiesSet() throws java.lang.Exception,public int getPage() ,public int getPageSize() ,public void setPageSize(int) <variables>private volatile int current,private volatile boolean initialized,private final java.util.concurrent.locks.Lock lock,protected org.apache.commons.logging.Log logger,private volatile int page,private int pageSize,protected volatile List<T> results