Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NullPointerException thrown for Updates with Maps that contain null values #4567

Closed
lukasraska opened this issue Nov 24, 2023 · 1 comment
Closed
Assignees
Labels
type: regression A regression from a previous release

Comments

@lukasraska
Copy link

Hello,
(probably) due to #4555 where QueryMapper.convertSimpleOrDocument was refactored to use Stream collect, it's now not possible to update documents with arbitrary Map that has null values.

It can be replicated with following entity:

@Document
public class TestPojo {
    @Id
    private String id;
    private Map<String, Object> data;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Map<String, Object> getData() {
        return data;
    }

    public void setData(Map<String, Object> data) {
        this.data = data;
    }

and following test case:

public interface TestPojoRepository extends MongoRepository<TestPojo, String> {}

@Test
void testUpdateNullValue() {
    var obj = new TestPojo();
    obj.setData(Collections.singletonMap("test", "value"));
    var saved = pojoRepository.save(obj);

    var query = new Query().addCriteria(Criteria.where("id").is(saved.getId()));
    var upd = new Update().set("data", Collections.singletonMap("test", null));

    mongoTemplate.updateFirst(query, upd, TestPojo.class);
}

which yields following exception:

java.lang.NullPointerException
	at java.base/java.util.Objects.requireNonNull(Objects.java:233)
	at java.base/java.util.stream.Collectors.lambda$uniqKeysMapAccumulator$1(Collectors.java:180)
	at java.base/java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
	at java.base/java.util.Collections$2.tryAdvance(Collections.java:5073)
	at java.base/java.util.Collections$2.forEachRemaining(Collections.java:5081)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
	at org.springframework.data.mongodb.core.convert.QueryMapper.convertSimpleOrDocument(QueryMapper.java:620)
	at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedValue(QueryMapper.java:525)
	at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedObjectForField(QueryMapper.java:351)
	at org.springframework.data.mongodb.core.convert.UpdateMapper.getMappedObjectForField(UpdateMapper.java:160)
	at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedObject(QueryMapper.java:170)
	at org.springframework.data.mongodb.core.convert.UpdateMapper.getMappedObject(UpdateMapper.java:66)
	at org.springframework.data.mongodb.core.convert.QueryMapper.convertSimpleOrDocument(QueryMapper.java:603)
	at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedKeyword(QueryMapper.java:410)
	at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedObject(QueryMapper.java:129)
	at org.springframework.data.mongodb.core.convert.UpdateMapper.getMappedObject(UpdateMapper.java:66)
	at org.springframework.data.mongodb.core.QueryOperations$UpdateContext.getMappedUpdate(QueryOperations.java:895)
	at org.springframework.data.mongodb.core.MongoTemplate.doUpdate(MongoTemplate.java:1720)
	at org.springframework.data.mongodb.core.MongoTemplate.updateFirst(MongoTemplate.java:1643)
	at com.example.demo.DemoApplicationTests.testUpdateNullValue(DemoApplicationTests.java:36)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

Which is caused by this known "bug" within Stream implementation - https://bugs.openjdk.org/browse/JDK-8148463

Is there any plan to either revert the refactor to the previous implementation logic or rewrite it from scratch in a way where it will support null values? I'm willing to create PR + write relevant test cases if I get suggestion what would be the suggested fix.

Thanks

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Nov 24, 2023
@christophstrobl
Copy link
Member

thanks for reporting - sorry for the inconvenience. we'll get that fixed.

@christophstrobl christophstrobl added type: regression A regression from a previous release type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged labels Nov 24, 2023
@christophstrobl christophstrobl self-assigned this Nov 24, 2023
mp911de pushed a commit that referenced this issue Dec 11, 2023
We now use regular iteration instead of the Stream API.

Closes: #4567
Original pull request: #4568
mp911de pushed a commit that referenced this issue Dec 11, 2023
We now use regular iteration instead of the Stream API.

Closes: #4567
Original pull request: #4568
@mp911de mp911de removed the type: bug A general bug label Dec 11, 2023
@mp911de mp911de added this to the 4.1.7 (2023.0.7) milestone Dec 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: regression A regression from a previous release
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants