MongoRepository saveAll() corrupts ID format

41 Views Asked by At

I've found some strange behavior of MongoRepository saveAll().

Here's the entity class and repository:

    @Document
    public class Entity {
        @MongoId
        private String id;
        private String name;
    }

    public interface EntityRepository extends MongoRepository<Entity, String> {}

And here's my test:

    @Test
    void test() {
        
        var e1 = Entity.builder().name("entity 1").build();
        var e2 = Entity.builder().name("entity 2").build();

        repository.saveAll(List.of(e1, e2));

        var e3 = Entity.builder().name("entity 3").build();
        var e4 = Entity.builder().name("entity 4").build();

        repository.save(e3);
        repository.save(e4);
    }

After that, I check my database and see that there are 4 records with the different IDs:

------------------
| _id | name     |
------------------
| 1   | entity 1 |
| 2   | entity 2 |
| 3   | entity 3 |
| 4   | entity 4 |
------------------

And here, this query returns value:

db.entity.find({_id: "3"});

but this one fails:

db.entity.find({_id: "1"})

Under the hood I've found that when you do .saveAll() then the ID format is not "1" but rather ObjectId("1"), but it's OK when you do .save()

Question: Does anybody knows what's going on here? Is there a way to overcome this strange behavior and use saveAll() for the sake of performance, but keep String IDs at the same time?

1

There are 1 best solutions below

1
On

In you test (test()), you are creating Entity instances without specifying the id. Since the id in the Entity class is defined as nullable, when you save these instances using EntityRepository.saveAll(entities), MongoDB will generate unique IDs for each document. Therefore, when you retrieve the saved Entity using EntityRepository.findAll(), the generated IDs will be different from the original ones in your test, resulting in the test failure.

pls see the original code of SimpleMongoRepository for more information

 /*
 * (non-Javadoc)
 * @see org.springframework.data.mongodb.repository.MongoRepository#saveAll(java.lang.Iterable)
 */
@Override
public<S extends T> List<S> saveAll(Iterable<S> entities){

  Assert.notNull(entities,"The given Iterable of entities not be null!");

  Streamable<S> source=Streamable.of(entities);
  boolean allNew=source.stream().allMatch(entityInformation::isNew);

  if(allNew){
    List<S> result=source.stream().collect(Collectors.toList());
    return new ArrayList<>(mongoOperations.insert(result,entityInformation.getCollectionName()));
  }

  return source.stream().map(this::save).collect(Collectors.toList());
}

In bottom line, MongoDB does not provide method for saving all given documents. We can use only insert, insertMany or save. That’s why saveAll method in MongoRepository has such logic. It covers something that we may want to use in our service but it is not available in MongoDB itself.