diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/RepositoryItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/RepositoryItemReader.java
index 950fe2186..716a6699f 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/RepositoryItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/RepositoryItemReader.java
@@ -59,6 +59,10 @@ import org.springframework.util.MethodInvoker;
* saveState=false if used in a multi-threaded client (no restart available).
*
*
+ *
+ * The RepositoryItemReader only reads Java Objects i.e. non primitives.
+ *
+ *
* @author Michael Minella
* @since 2.2
*/
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/RepositoryItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/RepositoryItemWriter.java
index 754da4cf2..b49b1b0ab 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/RepositoryItemWriter.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/RepositoryItemWriter.java
@@ -47,6 +47,10 @@ import org.springframework.util.MethodInvoker;
* transactions.
*
*
+ *
+ * The RepositoryItemWriter only stores Java Objects i.e. non primitives.
+ *
+ *
* @author Michael Minella
* @since 2.2
*/
@@ -59,7 +63,7 @@ public class RepositoryItemWriter implements ItemWriter, InitializingBean
private String methodName;
/**
- * Specifies what method on the repository to call. This method must the type of
+ * Specifies what method on the repository to call. This method must have the type of
* object passed to this writer as the sole argument.
*
* @param methodName
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/builder/RepositoryItemWriterBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/builder/RepositoryItemWriterBuilder.java
new file mode 100644
index 000000000..db9372531
--- /dev/null
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/builder/RepositoryItemWriterBuilder.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2017 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.data.builder;
+
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.springframework.batch.item.data.RepositoryItemWriter;
+import org.springframework.cglib.proxy.Enhancer;
+import org.springframework.cglib.proxy.MethodInterceptor;
+import org.springframework.cglib.proxy.MethodProxy;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.util.Assert;
+
+/**
+ * A builder implementation for the {@link RepositoryItemWriter}.
+ *
+ * @author Glenn Renfro
+ * @since 4.0
+ * @see RepositoryItemWriter
+ */
+public class RepositoryItemWriterBuilder {
+
+ private CrudRepository repository;
+
+ private String methodName;
+
+ private RepositoryMethodReference repositoryMethodReference;
+
+ /**
+ * Specifies what method on the repository to call. This method must have the type of
+ * object passed to this writer as the sole argument.
+ *
+ * @param methodName the name of the method to be used for saving the item.
+ * @return The current instance of the builder.
+ * @see RepositoryItemWriter#setMethodName(String)
+ */
+ public RepositoryItemWriterBuilder methodName(String methodName) {
+ this.methodName = methodName;
+
+ return this;
+ }
+
+ /**
+ * Set the {@link org.springframework.data.repository.CrudRepository} implementation
+ * for persistence
+ *
+ * @param repository the Spring Data repository to be set
+ * @return The current instance of the builder.
+ * @see RepositoryItemWriter#setRepository(CrudRepository)
+ */
+ public RepositoryItemWriterBuilder repository(CrudRepository repository) {
+ this.repository = repository;
+
+ return this;
+ }
+
+ /**
+ * Specifies a repository and the type-safe method to call for the writer. The method
+ * configured via this mechanism must take
+ * {@link org.springframework.data.domain.Pageable} as the last
+ * argument. This method can be used in place of {@link #repository(CrudRepository)},
+ * {@link #methodName(String)}}.
+ *
+ * Note: The repository that is used by the repositoryMethodReference must be
+ * non-final.
+ *
+ * @param repositoryMethodReference of the used to get a repository and type-safe
+ * method for use by the writer.
+ * @return The current instance of the builder.
+ * @see RepositoryItemWriter#setMethodName(String)
+ * @see RepositoryItemWriter#setRepository(CrudRepository)
+ *
+ */
+ public RepositoryItemWriterBuilder repository(RepositoryItemWriterBuilder.RepositoryMethodReference repositoryMethodReference) {
+ this.repositoryMethodReference = repositoryMethodReference;
+
+ return this;
+ }
+
+ /**
+ * Builds the {@link RepositoryItemWriter}.
+ *
+ * @return a {@link RepositoryItemWriter}
+ */
+ public RepositoryItemWriter build() {
+ if (this.repositoryMethodReference != null) {
+ this.methodName = this.repositoryMethodReference.getMethodName();
+ this.repository = this.repositoryMethodReference.getRepository();
+ }
+
+ Assert.hasText(this.methodName, "methodName is required.");
+ Assert.notNull(this.repository, "repository is required.");
+
+ RepositoryItemWriter writer = new RepositoryItemWriter<>();
+ writer.setMethodName(this.methodName);
+ writer.setRepository(this.repository);
+ return writer;
+ }
+
+ /**
+ * Establishes a proxy that will capture a the Repository and the associated
+ * methodName that will be used by the writer.
+ * @param The type of repository that will be used by the writer. The class must
+ * not be final.
+ */
+ public static class RepositoryMethodReference {
+ private RepositoryItemWriterBuilder.RepositoryMethodIterceptor repositoryInvocationHandler;
+
+ private CrudRepository, ?> repository;
+
+ public RepositoryMethodReference(CrudRepository, ?> repository) {
+ this.repository = repository;
+ this.repositoryInvocationHandler = new RepositoryItemWriterBuilder.RepositoryMethodIterceptor();
+ }
+
+ /**
+ * The proxy returned prevents actual method execution and is only used to gather,
+ * information about the method.
+ * @return T is a proxy of the object passed in in the constructor
+ */
+ public T methodIs() {
+ Enhancer enhancer = new Enhancer();
+ enhancer.setSuperclass(this.repository.getClass());
+ enhancer.setCallback(this.repositoryInvocationHandler);
+ return (T) enhancer.create();
+ }
+
+ CrudRepository, ?> getRepository() {
+ return this.repository;
+ }
+
+ String getMethodName() {
+ return this.repositoryInvocationHandler.getMethodName();
+ }
+ }
+
+ private static class RepositoryMethodIterceptor implements MethodInterceptor {
+ private String methodName;
+
+ @Override
+ public Object intercept(Object o, Method method, Object[] objects,
+ MethodProxy methodProxy) throws Throwable {
+ this.methodName = method.getName();
+ return null;
+ }
+
+ String getMethodName() {
+ return this.methodName;
+ }
+
+ }
+}
diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/data/builder/RepositoryItemWriterBuilderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/data/builder/RepositoryItemWriterBuilderTests.java
new file mode 100644
index 000000000..72bc9f718
--- /dev/null
+++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/data/builder/RepositoryItemWriterBuilderTests.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2017 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.data.builder;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import org.springframework.batch.item.data.RepositoryItemWriter;
+import org.springframework.data.repository.CrudRepository;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.verify;
+
+/**
+ * @author Glenn Renfro
+ */
+public class RepositoryItemWriterBuilderTests {
+ @Mock
+ private TestRepository repository;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testNullRepository() throws Exception {
+ try {
+ new RepositoryItemWriterBuilder().methodName("save").build();
+
+ fail("IllegalArgumentException should have been thrown");
+ }
+ catch (IllegalArgumentException iae) {
+ assertEquals("IllegalArgumentException message did not match the expected result.",
+ "repository is required.", iae.getMessage());
+ }
+ }
+
+ @Test
+ public void testEmptyMethodName() throws Exception {
+ try {
+ new RepositoryItemWriterBuilder().repository(this.repository).build();
+
+ fail("IllegalArgumentException should have been thrown");
+ }
+ catch (IllegalArgumentException iae) {
+ assertEquals("IllegalArgumentException message did not match the expected result.",
+ "methodName is required.", iae.getMessage());
+ }
+ }
+
+ @Test
+ public void testWriteItems() throws Exception {
+ RepositoryItemWriter writer = new RepositoryItemWriterBuilder()
+ .methodName("save")
+ .repository(this.repository)
+ .build();
+
+ List items = Collections.singletonList("foo");
+
+ writer.write(items);
+
+ verify(this.repository).save("foo");
+ }
+
+ @Test
+ public void testWriteItemsTestRepsository() throws Exception {
+ RepositoryItemWriter writer = new RepositoryItemWriterBuilder()
+ .methodName("foo")
+ .repository(this.repository)
+ .build();
+
+ List items = Collections.singletonList("foo");
+
+ writer.write(items);
+
+ verify(this.repository).foo("foo");
+ }
+
+ @Test
+ public void testWriteItemsTestRepsositoryMethodIs() throws Exception {
+ RepositoryItemWriterBuilder.RepositoryMethodReference
+ repositoryMethodReference = new RepositoryItemWriterBuilder.RepositoryMethodReference<>(
+ this.repository);
+ repositoryMethodReference.methodIs().foo(null);
+
+ RepositoryItemWriter writer = new RepositoryItemWriterBuilder()
+ .methodName("foo")
+ .repository(repositoryMethodReference)
+ .build();
+
+ List items = Collections.singletonList("foo");
+
+ writer.write(items);
+
+ verify(this.repository).foo("foo");
+ }
+
+ public interface TestRepository extends CrudRepository {
+
+ Object foo(String arg1);
+
+ }
+}