Skip to content

Commit e8a515a

Browse files
blake-baumanfmbenhassine
authored andcommitted
Allow keys other than _id for primary key in MongoItemWriter
When moving data between different data store types, such as between MongoDB and a SQL database, not all systems have a "_id" as their pimary key. Most often it has a different name or can be multiple keys/columns used as a composite key. This change allows the app to specify an alternate key or a set of keys which, together, can be used as the primary key for a MongoDB insert/update/upsert. Signed-off-by: blake_bauman <blake_bauman@apple.com>
1 parent 589c823 commit e8a515a

File tree

2 files changed

+84
-5
lines changed

2 files changed

+84
-5
lines changed

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/MongoItemWriter.java

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616

1717
package org.springframework.batch.item.data;
1818

19+
import static java.util.stream.Collectors.toList;
20+
21+
import java.util.List;
22+
1923
import org.bson.Document;
2024
import org.bson.types.ObjectId;
2125

@@ -92,6 +96,8 @@ public enum Mode {
9296

9397
private Mode mode = Mode.UPSERT;
9498

99+
private List<String> primaryKeys = List.of(ID_KEY);
100+
95101
public MongoItemWriter() {
96102
super();
97103
this.bufferKey = new Object();
@@ -163,6 +169,27 @@ public String getCollection() {
163169
return collection;
164170
}
165171

172+
/**
173+
* Set the primary keys to associate with the document being written. These fields
174+
* should uniquely identify a single object.
175+
* @param primaryKeys The primary keys to use.
176+
* @since 5.2.3
177+
*/
178+
public void setPrimaryKeys(List<String> primaryKeys) {
179+
Assert.notEmpty(primaryKeys, "The primaryKeys list must have one or more keys.");
180+
181+
this.primaryKeys = primaryKeys;
182+
}
183+
184+
/**
185+
* Get the list of primary keys associated with the document being written.
186+
* @return the list of primary keys
187+
* @since 5.2.3
188+
*/
189+
public List<String> getPrimaryKeys() {
190+
return primaryKeys;
191+
}
192+
166193
/**
167194
* If a transaction is active, buffer items to be written just before commit.
168195
* Otherwise write items using the provided template.
@@ -213,9 +240,14 @@ private void remove(Chunk<? extends T> chunk) {
213240
for (Object item : chunk) {
214241
Document document = new Document();
215242
mongoConverter.write(item, document);
216-
Object objectId = document.get(ID_KEY);
217-
if (objectId != null) {
218-
Query query = new Query().addCriteria(Criteria.where(ID_KEY).is(objectId));
243+
244+
List<Criteria> criteriaList = primaryKeys.stream()
245+
.filter(document::containsKey)
246+
.map(key -> Criteria.where(key).is(document.get(key)))
247+
.collect(toList());
248+
if (!criteriaList.isEmpty()) {
249+
Query query = new Query();
250+
criteriaList.forEach(query::addCriteria);
219251
bulkOperations.remove(query);
220252
}
221253
}
@@ -229,8 +261,21 @@ private void upsert(Chunk<? extends T> chunk) {
229261
for (Object item : chunk) {
230262
Document document = new Document();
231263
mongoConverter.write(item, document);
232-
Object objectId = document.get(ID_KEY) != null ? document.get(ID_KEY) : new ObjectId();
233-
Query query = new Query().addCriteria(Criteria.where(ID_KEY).is(objectId));
264+
265+
Query query = new Query();
266+
List<Criteria> criteriaList = primaryKeys.stream()
267+
.filter(document::containsKey)
268+
.map(key -> Criteria.where(key).is(document.get(key)))
269+
.collect(toList());
270+
271+
if (criteriaList.isEmpty()) {
272+
Object objectId = document.get(ID_KEY) != null ? document.get(ID_KEY) : new ObjectId();
273+
query.addCriteria(Criteria.where(ID_KEY).is(objectId));
274+
}
275+
else {
276+
criteriaList.forEach(query::addCriteria);
277+
}
278+
234279
bulkOperations.replaceOne(query, document, upsert);
235280
}
236281
bulkOperations.execute();

spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/builder/MongoItemWriterBuilder.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.batch.item.data.builder;
1818

19+
import java.util.List;
20+
1921
import org.springframework.batch.item.data.MongoItemWriter;
2022
import org.springframework.batch.item.data.MongoItemWriter.Mode;
2123
import org.springframework.data.mongodb.core.MongoOperations;
@@ -37,6 +39,8 @@ public class MongoItemWriterBuilder<T> {
3739

3840
private Mode mode = Mode.UPSERT;
3941

42+
private List<String> primaryKeys = List.of();
43+
4044
/**
4145
* Indicates if the items being passed to the writer are to be saved or removed from
4246
* the data store. If set to false (default), the items will be saved. If set to true,
@@ -93,6 +97,32 @@ public MongoItemWriterBuilder<T> collection(String collection) {
9397
return this;
9498
}
9599

100+
/**
101+
* Set the primary keys to associate with the document being written. These fields
102+
* should uniquely identify a single object.
103+
* @param primaryKeys The keys to use.
104+
* @see MongoItemWriter#setPrimaryKeys(List)
105+
* @since 5.2.3
106+
*/
107+
public MongoItemWriterBuilder<T> primaryKeys(List<String> primaryKeys) {
108+
this.primaryKeys = List.copyOf(primaryKeys);
109+
110+
return this;
111+
}
112+
113+
/**
114+
* Set the primary keys to associate with the document being written. These fields
115+
* should uniquely identify a single object.
116+
* @param primaryKeys The keys to use.
117+
* @see MongoItemWriter#setPrimaryKeys(List)
118+
* @since 5.2.3
119+
*/
120+
public MongoItemWriterBuilder<T> primaryKeys(String... primaryKeys) {
121+
this.primaryKeys = List.of(primaryKeys);
122+
123+
return this;
124+
}
125+
96126
/**
97127
* Validates and builds a {@link MongoItemWriter}.
98128
* @return a {@link MongoItemWriter}
@@ -105,6 +135,10 @@ public MongoItemWriter<T> build() {
105135
writer.setMode(this.mode);
106136
writer.setCollection(this.collection);
107137

138+
if (!this.primaryKeys.isEmpty()) {
139+
writer.setPrimaryKeys(this.primaryKeys);
140+
}
141+
108142
return writer;
109143
}
110144

0 commit comments

Comments
 (0)