upsert with MongoTemplate and use of @CreatedDate/@LastModifiedDate

Viewed 885

I have a pojo which looks as following:

@Document
public class MyPojo {
    @Id
    private String        id;
    ...
    @CreatedDate
    private ZonedDateTime createdAt;
    @LastModifiedDate
    private ZonedDateTime modifiedAt;
}

I want to upsert a document with the following logic:

  • if the document is new, both createdAt and modifiedAt are set to now.
  • if the document already exists, createdAt should not be updated and modifiedAt should be set to now.

I tried with the following code:

MongoTemplate template = ...; // wired
MyPojo obj = new MyPojo();

template.findAndReplace(
        new Query().addCriteria(...),
        obj,
        new FindAndReplaceOptions().upsert());

Here, the members id, createdAt and modifiedAt are not set (hence they are null).

This does not seem to work. The corresponding fields in the document BSON in the database are never set.

How do you upsert a document in combination with the @CreatedDate and @LastModifiedDate annotations? Is this possible, or do I need to roll out something of my own. In case of the latter, how can I achieve the upsert logic as stated above?

I am using the following configuration:

@Configuration
@EnableMongoAuditing(dateTimeProviderRef = "dateTimeConverter")

Here, dateTimeProviderRef is used to support ZonedDateTime objects.

Also, I noticed that when calling template.save(obj) or template.insert(obj) instead of findAndReplace(...) (as in my code snippet), the createdAt and modifiedAt are set upon creation, as expected. That said, I believe I need findAndReplace(), because I need an atomic upsert to happen.

1 Answers

You need to enable the auditing by using @EnableMongoAuditing at your @SpringBootApplication

@SpringBootApplication
@EnableMongoAuditing(modifyOnCreate = false)
public class DemoApplication {

Also, ZonedDateTime are not supported. Please use Instant instead.

Ref:

See this working example:

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.mongodb.config.EnableMongoAuditing;
import org.springframework.data.mongodb.core.FindAndReplaceOptions;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.Instant;

import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query.query;

@SpringBootApplication
@EnableMongoAuditing
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

@RestController
class Ctrl {
    @Autowired
    MongoTemplate template;

    @GetMapping("/templ")
    void templ() throws InterruptedException {
        template.insert(new MyPojo("TOM"));
        template.insert(new MyPojo("HARRY"));


        Thread.sleep(2000);

        //update BOB to Robert if exists or insert new
        //should inert new
        template.findAndReplace(
                query(where("name").is("BOB")),
                new MyPojo("Robert"),
                new FindAndReplaceOptions().upsert());


        Thread.sleep(2000);

        //update TOM to Tommy if exists or insert new
        template.findAndReplace(
                query(where("name").is("TOM")),
                new MyPojo("Tommy"),
                new FindAndReplaceOptions().upsert());


        template.findAll(MyPojo.class).forEach(System.out::println);
    }
}

@Document
@Data
@ToString
@NoArgsConstructor
class MyPojo {
    public MyPojo(String name) {
        this.name = name;
    }

    @Id
    private String id;
    String name = "";
    @CreatedDate
    private Instant createdAt;

    @LastModifiedDate
    private Instant modifiedAt;
}

Output:

MyPojo(id=5f736d33b72165754ea9fca1, name=Tommy, createdAt=2020-09-29T17:21:59.765Z, modifiedAt=null)
MyPojo(id=5f736d33b72165754ea9fca2, name=HARRY, createdAt=2020-09-29T17:21:55.732Z, modifiedAt=null)
MyPojo(id=5f736d352e78c143350ed0f8, name=Robert, createdAt=2020-09-29T17:21:57.748Z, modifiedAt=null)
Related