class RepositoryItemWriter<T> implements ItemWriter<T>, InitializingBean {

	protected static final Log logger = LogFactory.getLog(RepositoryItemWriter.class);

	private CrudRepository<T, ?> repository;

	private String methodName;

	/**
	 * Specifies what method on the repository to call. This method must have the type of
	 * object passed to this writer as the <em>sole</em> argument.
	 * @param methodName {@link String} containing the method name.
	 */
	public void setMethodName(String methodName) {
		this.methodName = methodName;
	}

	/**
	 * Set the {@link org.springframework.data.repository.CrudRepository} implementation
	 * for persistence
	 * @param repository the Spring Data repository to be set
	 */
	public void setRepository(CrudRepository<T, ?> repository) {
		this.repository = repository;
	}

	/**
	 * Write all items to the data store via a Spring Data repository.
	 *
	 * @see org.springframework.batch.item.ItemWriter#write(Chunk)
	 */
	@Override
	public void write(Chunk<? extends T> chunk) throws Exception {
		if (!chunk.isEmpty()) {
			doWrite(chunk);
		}
	}

	/**
	 * Performs the actual write to the repository. This can be overridden by a subclass
	 * if necessary.
	 * @param items the list of items to be persisted.
	 * @throws Exception thrown if error occurs during writing.
	 */
	protected void doWrite(Chunk<? extends T> items) throws Exception {
		if (logger.isDebugEnabled()) {
			logger.debug("Writing to the repository with " + items.size() + " items.");
		}

		if (this.methodName == null) {
			this.repository.saveAll(items);
			return;
		}

		MethodInvoker invoker = createMethodInvoker(repository, methodName);

		for (T object : items) {
			invoker.setArguments(object);
			doInvoke(invoker);
		}
	}

	/**
	 * Check mandatory properties - there must be a repository.
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
		Assert.state(repository != null, "A CrudRepository implementation is required");
		if (this.methodName != null) {
			Assert.state(StringUtils.hasText(this.methodName), "methodName must not be empty.");
		}
		else {
			logger.debug("No method name provided, CrudRepository.saveAll will be used.");
		}
	}

	private Object doInvoke(MethodInvoker invoker) throws Exception {
		try {
			invoker.prepare();
		}
		catch (ClassNotFoundException | NoSuchMethodException e) {
			throw new DynamicMethodInvocationException(e);
		}

		try {
			return invoker.invoke();
		}
		catch (InvocationTargetException e) {
			if (e.getCause() instanceof Exception) {
				throw (Exception) e.getCause();
			}
			else {
				throw new InvocationTargetThrowableWrapper(e.getCause());
			}
		}
		catch (IllegalAccessException e) {
			throw new DynamicMethodInvocationException(e);
		}
	}

	private MethodInvoker createMethodInvoker(Object targetObject, String targetMethod) {<FILL_FUNCTION_BODY>}

}

		MethodInvoker invoker = new MethodInvoker();
		invoker.setTargetObject(targetObject);
		invoker.setTargetMethod(targetMethod);
		return invoker;
	
<no_super_class>