diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/MongoItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/MongoItemReader.java
index 3208602e7..cafe40894 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/MongoItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/MongoItemReader.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 the original author or authors.
+ * Copyright 2012-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.
@@ -25,6 +25,8 @@ import java.util.regex.Pattern;
import com.mongodb.util.JSON;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemReader;
import org.springframework.beans.factory.InitializingBean;
@@ -45,14 +47,23 @@ import org.springframework.util.StringUtils;
*
*
*
- * It executes the JSON {@link #setQuery(String)} to retrieve the requested
- * documents. The query is executed using paged requests specified in the
+ * If you set JSON String query {@link #setQuery(String)} then
+ * it executes the JSON to retrieve the requested documents.
+ *
+ *
+ *
+ * If you set Query object {@link #setQuery(Query)} then
+ * it executes the Query to retrieve the requested documents.
+ *
+ *
+ *
+ * The query is executed using paged requests specified in the
* {@link #setPageSize(int)}. Additional pages are requested as needed to
* provide data when the {@link #read()} method is called.
*
*
*
- * The JSON query provided supports parameter substitution via ?<index>
+ * The JSON String query provided supports parameter substitution via ?<index>
* placeholders where the <index> indicates the index of the
* parameterValue to substitute.
*
@@ -65,12 +76,16 @@ import org.springframework.util.StringUtils;
*
*
* @author Michael Minella
+ * @author Takaaki Iida
*/
public class MongoItemReader extends AbstractPaginatedDataItemReader implements InitializingBean {
-
+
+ private static final Logger log = LoggerFactory.getLogger(MongoItemReader.class);
+
private static final Pattern PLACEHOLDER = Pattern.compile("\\?(\\d+)");
private MongoOperations template;
- private String query;
+ private Query query;
+ private String queryString;
private Class extends T> type;
private Sort sort;
private String hint;
@@ -82,6 +97,15 @@ public class MongoItemReader extends AbstractPaginatedDataItemReader imple
super();
setName(ClassUtils.getShortName(MongoItemReader.class));
}
+
+ /**
+ * A Mongo Query to be used.
+ *
+ * @param query Mongo Query to be used.
+ */
+ public void setQuery(Query query) {
+ this.query = query;
+ }
/**
* Used to perform operations against the MongoDB instance. Also
@@ -99,10 +123,10 @@ public class MongoItemReader extends AbstractPaginatedDataItemReader imple
* via ?<index> placeholders where the <index> indicates the index of the
* parameterValue to substitute.
*
- * @param query JSON formatted Mongo query
+ * @param queryString JSON formatted Mongo query
*/
- public void setQuery(String query) {
- this.query = query;
+ public void setQuery(String queryString) {
+ this.queryString = queryString;
}
/**
@@ -163,30 +187,41 @@ public class MongoItemReader extends AbstractPaginatedDataItemReader imple
@Override
@SuppressWarnings("unchecked")
protected Iterator doPageRead() {
-
- Pageable pageRequest = PageRequest.of(page, pageSize, sort);
-
- String populatedQuery = replacePlaceholders(query, parameterValues);
-
- Query mongoQuery;
-
- if(StringUtils.hasText(fields)) {
- mongoQuery = new BasicQuery(populatedQuery, fields);
- }
- else {
- mongoQuery = new BasicQuery(populatedQuery);
- }
-
- mongoQuery.with(pageRequest);
-
- if(StringUtils.hasText(hint)) {
- mongoQuery.withHint(hint);
- }
-
- if(StringUtils.hasText(collection)) {
- return (Iterator) template.find(mongoQuery, type, collection).iterator();
+ if (queryString != null) {
+ Pageable pageRequest = new PageRequest(page, pageSize, sort);
+
+ String populatedQuery = replacePlaceholders(queryString, parameterValues);
+
+ Query mongoQuery = null;
+
+ if(StringUtils.hasText(fields)) {
+ mongoQuery = new BasicQuery(populatedQuery, fields);
+ }
+ else {
+ mongoQuery = new BasicQuery(populatedQuery);
+ }
+
+ mongoQuery.with(pageRequest);
+
+ if(StringUtils.hasText(hint)) {
+ mongoQuery.withHint(hint);
+ }
+
+ if(StringUtils.hasText(collection)) {
+ return (Iterator) template.find(mongoQuery, type, collection).iterator();
+ } else {
+ return (Iterator) template.find(mongoQuery, type).iterator();
+ }
+
} else {
- return (Iterator) template.find(mongoQuery, type).iterator();
+ Pageable pageRequest = new PageRequest(page, pageSize);
+ query.with(pageRequest);
+
+ if(StringUtils.hasText(collection)) {
+ return (Iterator) template.find(query, type, collection).iterator();
+ } else {
+ return (Iterator) template.find(query, type).iterator();
+ }
}
}
@@ -199,8 +234,18 @@ public class MongoItemReader extends AbstractPaginatedDataItemReader imple
public void afterPropertiesSet() throws Exception {
Assert.state(template != null, "An implementation of MongoOperations is required.");
Assert.state(type != null, "A type to convert the input into is required.");
- Assert.state(query != null, "A query is required.");
- Assert.state(sort != null, "A sort is required.");
+ Assert.state(queryString != null || query != null, "A query is required.");
+
+ if (queryString != null) {
+ Assert.state(sort != null, "A sort is required.");
+ }
+ if (query != null) {
+ Assert.state(query.getSortObject() != null, "A Sort in Query object is required.");
+ }
+
+ if (query != null && query.getLimit() != 0) {
+ log.warn("PageSize in Query object was ignored. Please set it by MongoItemReader.setPageSize().");
+ }
}
// Copied from StringBasedMongoQuery...is there a place where this type of logic is already exposed?
diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/data/MongoItemReaderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/data/MongoItemReaderTests.java
index d4b978c12..6b2c64995 100644
--- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/data/MongoItemReaderTests.java
+++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/data/MongoItemReaderTests.java
@@ -25,7 +25,9 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Order;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Query;
@@ -33,7 +35,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;
public class MongoItemReaderTests {
@@ -60,8 +62,8 @@ public class MongoItemReaderTests {
}
@Test
- public void testAfterPropertiesSet() throws Exception{
- reader = new MongoItemReader<>();
+ public void testAfterPropertiesSetForQueryString() throws Exception{
+ reader = new MongoItemReader();
try {
reader.afterPropertiesSet();
@@ -109,6 +111,31 @@ public class MongoItemReaderTests {
reader.afterPropertiesSet();
}
+
+ @Test
+ public void testAfterPropertiesSetForQueryObject() throws Exception{
+ reader = new MongoItemReader();
+
+ reader.setTemplate(template);
+ reader.setTargetType(String.class);
+
+ Query query1 = new Query();
+ reader.setQuery(query1);
+
+ try {
+ reader.afterPropertiesSet();
+ fail("Sort was not set but exception was not thrown.");
+ } catch (IllegalStateException iae) {
+ assertEquals("A Sort in Query object is required.", iae.getMessage());
+ } catch (Throwable t) {
+ fail("Wrong exception was thrown.");
+ }
+
+ Query query2 = new Query().with(new Sort(new Order(Sort.Direction.ASC, "_id")));
+ reader.setQuery(query2);
+
+ reader.afterPropertiesSet();
+ }
@Test
public void testBasicQueryFirstPage() {
@@ -223,4 +250,102 @@ public class MongoItemReaderTests {
assertEquals("{ \"name\" : -1 }", query.getSortObject().toJson());
assertEquals("collection", collectionContainer.getValue());
}
+
+ @Test
+ public void testQueryObject() throws Exception {
+ reader = new MongoItemReader();
+ reader.setTemplate(template);
+
+ Query query = new Query()
+ .with(new Sort(new Order(Sort.Direction.ASC, "_id")));
+ reader.setQuery(query);
+ reader.setTargetType(String.class);
+
+ reader.afterPropertiesSet();
+
+ ArgumentCaptor queryContainer = ArgumentCaptor.forClass(Query.class);
+ when(template.find(queryContainer.capture(), eq(String.class))).thenReturn(new ArrayList());
+
+ assertFalse(reader.doPageRead().hasNext());
+
+ Query actualQuery = queryContainer.getValue();
+ assertFalse(reader.doPageRead().hasNext());
+ assertEquals(10, actualQuery.getLimit());
+ assertEquals(0, actualQuery.getSkip());
+ }
+
+ @Test
+ public void testQueryObjectWithIgnoredPageSize() throws Exception {
+ reader = new MongoItemReader();
+ reader.setTemplate(template);
+
+ Query query = new Query()
+ .with(new Sort(new Order(Sort.Direction.ASC, "_id")))
+ .with(new PageRequest(0, 50));
+ reader.setQuery(query);
+ reader.setTargetType(String.class);
+
+ reader.afterPropertiesSet();
+
+ ArgumentCaptor queryContainer = ArgumentCaptor.forClass(Query.class);
+ when(template.find(queryContainer.capture(), eq(String.class))).thenReturn(new ArrayList());
+
+ assertFalse(reader.doPageRead().hasNext());
+
+ Query actualQuery = queryContainer.getValue();
+ assertFalse(reader.doPageRead().hasNext());
+ assertEquals(10, actualQuery.getLimit());
+ assertEquals(0, actualQuery.getSkip());
+ }
+
+ @Test
+ public void testQueryObjectWithPageSize() throws Exception {
+ reader = new MongoItemReader();
+ reader.setTemplate(template);
+
+ Query query = new Query()
+ .with(new Sort(new Order(Sort.Direction.ASC, "_id")))
+ .with(new PageRequest(30, 50));
+ reader.setQuery(query);
+ reader.setTargetType(String.class);
+ reader.setPageSize(100);
+
+ reader.afterPropertiesSet();
+
+ ArgumentCaptor queryContainer = ArgumentCaptor.forClass(Query.class);
+ when(template.find(queryContainer.capture(), eq(String.class))).thenReturn(new ArrayList());
+
+ assertFalse(reader.doPageRead().hasNext());
+
+ Query actualQuery = queryContainer.getValue();
+ assertFalse(reader.doPageRead().hasNext());
+ assertEquals(100, actualQuery.getLimit());
+ assertEquals(0, actualQuery.getSkip());
+ }
+
+ @Test
+ public void testQueryObjectWithCollection() throws Exception {
+ reader = new MongoItemReader();
+ reader.setTemplate(template);
+
+ Query query = new Query()
+ .with(new Sort(new Order(Sort.Direction.ASC, "_id")));
+ reader.setQuery(query);
+ reader.setTargetType(String.class);
+ reader.setCollection("collection");
+
+ reader.afterPropertiesSet();
+
+ ArgumentCaptor queryContainer = ArgumentCaptor.forClass(Query.class);
+ ArgumentCaptor stringContainer = ArgumentCaptor.forClass(String.class);
+ when(template.find(queryContainer.capture(), eq(String.class), stringContainer.capture())).thenReturn(new ArrayList());
+
+ assertFalse(reader.doPageRead().hasNext());
+
+ Query actualQuery = queryContainer.getValue();
+ assertFalse(reader.doPageRead().hasNext());
+ assertEquals(10, actualQuery.getLimit());
+ assertEquals(0, actualQuery.getSkip());
+ assertEquals("collection", stringContainer.getValue());
+ }
}