Philip
Philip

Reputation: 185

Spring boot mongo audit @version issue

I just started a new project and would like to use Sprint Boot 2.1 and ran into a problem at the very beginning. What I would like to do is use Spring Boot Mongo to manage the database. I would like to have an optimistic lock with @Version annotation. However, I found that it seems like @Version would affect the save() behavior in MongoRepository, which means, dup key error.

The following is the sample code.

POJO

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Document
    public class Person {

        @Id public ObjectId id;
        @CreatedDate public LocalDateTime createOn;
        @LastModifiedDate public LocalDateTime modifiedOn;
        @Version public long version;
        private String name;
        private String email;

        public Person(String name, String email) {
            this.name = name;
            this.email = email;
        }

       @Override
        public String toString() {
            return String.format("Person [id=%s, name=%s, email=%s, createdOn=%s, modifiedOn=%s, version=%s]", id, name, email, createOn, modifiedOn, version);
        }
    }

MongoConfig

    @Configuration
    @EnableMongoRepositories("com.project.server.repo")
    @EnableMongoAuditing
    public class MongoConfig {

    }

Repository

    public interface PersonRepo extends MongoRepository<Person, ObjectId> {
        Person save(Person person);
        Person findByName(String name);
        Person findByEmail(String email);
        long count();
        @Override
        void delete(Person person);
    }

As indicated in Official Doc, I have my version field in long, but the dup key error occurs at the second save, which means it tried to insert again, even with the id in the object.
I also tried with Long in version field, which has no dup key occurred and save as update as expected, but the createdOn become null in the first save (which means insert)

Controller

Person joe = new Person("Joe", "[email protected]");
System.out.println(joe.toString());
this.personRepo.save(joe);
Person who = this.personRepo.findByName("Joe");
System.out.println(who.toString());
who.setEmail("[email protected]");
this.personRepo.save(who);
Person who1 = this.personRepo.findByName("Joe");
Person who2 = this.personRepo.findByEmail("[email protected]");
System.out.println(who1.toString());
System.out.println(who2.toString());

log with dup key (long version)

    2018-11-11 02:09:31.435  INFO 4319 --- [on(6)-127.0.0.1] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:2, serverValue:186}] to localhost:27017
    Person [id=null, name=Joe, [email protected], createdOn=null, modifiedOn=null, version=0]
    2018-11-11 02:09:37.254 DEBUG 4319 --- [nio-8080-exec-1] o.s.data.auditing.AuditingHandler        : Touched Person [id=null, name=Joe, [email protected], createdOn=2018-11-11T02:09:37.252, modifiedOn=2018-11-11T02:09:37.252, version=0] - Last modification at 2018-11-11T02:09:37.252 by unknown
    2018-11-11 02:09:37.259 DEBUG 4319 --- [nio-8080-exec-1] o.s.data.mongodb.core.MongoTemplate      : Inserting Document containing fields: [createOn, modifiedOn, version, name, email, _class] in collection: person
    2018-11-11 02:09:37.297 DEBUG 4319 --- [nio-8080-exec-1] o.s.d.m.r.query.MongoQueryCreator        : Created query Query: { "name" : "Joe" }, Fields: { }, Sort: { }
    2018-11-11 02:09:37.304 DEBUG 4319 --- [nio-8080-exec-1] o.s.data.mongodb.core.MongoTemplate      : find using query: { "name" : "Joe" } fields: Document{{}} for class: class com.seedu.server.model.Person in collection: person
    Person [id=5be71ee11ad34410df06852c, name=Joe, [email protected], createdOn=2018-11-11T02:09:37.252, modifiedOn=2018-11-11T02:09:37.252, version=0]
    2018-11-11 02:09:37.323 DEBUG 4319 --- [nio-8080-exec-1] o.s.data.auditing.AuditingHandler        : Touched Person [id=5be71ee11ad34410df06852c, name=Joe, [email protected], createdOn=2018-11-11T02:09:37.323, modifiedOn=2018-11-11T02:09:37.323, version=0] - Last modification at 2018-11-11T02:09:37.323 by unknown
    2018-11-11 02:09:37.324 DEBUG 4319 --- [nio-8080-exec-1] o.s.data.mongodb.core.MongoTemplate      : Inserting Document containing fields: [_id, createOn, modifiedOn, version, name, email, _class] in collection: person
    org.springframework.dao.DuplicateKeyException: E11000 duplicate key error collection: seedu.person index: _id_ dup key: { : ObjectId('5be71ee11ad34410df06852c') }; nested exception is com.mongodb.MongoWriteException: E11000 duplicate key error collection: seedu.person index: _id_ dup key: { : ObjectId('5be71ee11ad34410df06852c') }

log with createdDate null (Long version)

    2018-11-11 02:07:28.858  INFO 4310 --- [on(6)-127.0.0.1] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:2, serverValue:183}] to localhost:27017
    Person [id=null, name=Joe, [email protected], createdOn=null, modifiedOn=null, version=null]
    2018-11-11 02:07:31.519 DEBUG 4310 --- [nio-8080-exec-1] o.s.data.auditing.AuditingHandler        : Touched Person [id=null, name=Joe, [email protected], createdOn=null, modifiedOn=2018-11-11T02:07:31.518, version=0] - Last modification at 2018-11-11T02:07:31.518 by unknown
    2018-11-11 02:07:31.525 DEBUG 4310 --- [nio-8080-exec-1] o.s.data.mongodb.core.MongoTemplate      : Inserting Document containing fields: [modifiedOn, version, name, email, _class] in collection: person
    2018-11-11 02:07:31.564 DEBUG 4310 --- [nio-8080-exec-1] o.s.d.m.r.query.MongoQueryCreator        : Created query Query: { "name" : "Joe" }, Fields: { }, Sort: { }
    2018-11-11 02:07:31.571 DEBUG 4310 --- [nio-8080-exec-1] o.s.data.mongodb.core.MongoTemplate      : find using query: { "name" : "Joe" } fields: Document{{}} for class: class com.seedu.server.model.Person in collection: person
    Person [id=5be71e631ad34410d6a3b123, name=Joe, [email protected], createdOn=null, modifiedOn=2018-11-11T02:07:31.518, version=0]
    2018-11-11 02:07:31.590 DEBUG 4310 --- [nio-8080-exec-1] o.s.data.auditing.AuditingHandler        : Touched Person [id=5be71e631ad34410d6a3b123, name=Joe, [email protected], createdOn=null, modifiedOn=2018-11-11T02:07:31.590, version=1] - Last modification at 2018-11-11T02:07:31.590 by unknown
    2018-11-11 02:07:31.598 DEBUG 4310 --- [nio-8080-exec-1] o.s.data.mongodb.core.MongoTemplate      : Calling update using query: { "_id" : { "$oid" : "5be71e631ad34410d6a3b123" }, "version" : { "$numberLong" : "0" } } and update: { "modifiedOn" : { "$date" : 1541873251590 }, "version" : { "$numberLong" : "1" }, "name" : "Joe", "email" : "[email protected]", "_class" : "com.seedu.server.model.Person" } in collection: person
    2018-11-11 02:07:31.602 DEBUG 4310 --- [nio-8080-exec-1] o.s.d.m.r.query.MongoQueryCreator        : Created query Query: { "name" : "Joe" }, Fields: { }, Sort: { }
    2018-11-11 02:07:31.602 DEBUG 4310 --- [nio-8080-exec-1] o.s.data.mongodb.core.MongoTemplate      : find using query: { "name" : "Joe" } fields: Document{{}} for class: class com.seedu.server.model.Person in collection: person
    2018-11-11 02:07:31.603 DEBUG 4310 --- [nio-8080-exec-1] o.s.d.m.r.query.MongoQueryCreator        : Created query Query: { "email" : "[email protected]" }, Fields: { }, Sort: { }
    2018-11-11 02:07:31.604 DEBUG 4310 --- [nio-8080-exec-1] o.s.data.mongodb.core.MongoTemplate      : find using query: { "email" : "[email protected]" } fields: Document{{}} for class: class com.seedu.server.model.Person in collection: person
    Person [id=5be71e631ad34410d6a3b123, name=Joe, [email protected], createdOn=null, modifiedOn=2018-11-11T02:07:31.590, version=1]
    Person [id=5be71e631ad34410d6a3b123, name=Joe, [email protected], createdOn=null, modifiedOn=2018-11-11T02:07:31.590, version=1]

As I know, the spring use id existence as the save behavior control, which means if the id exists then save would be like the insert for mongo. However, in here, version seems also affect the save behavior or affect the way that spring recognize the id existence.

Question: How can I use MongoAudit with MongoRepository together? Is there any mistake/bug I made?

Upvotes: 3

Views: 5061

Answers (2)

Pankaj Kumar
Pankaj Kumar

Reputation: 301

For working with @Version, You have to retrieve the data model first from the database and after updating the data, you have to save the same data to the database. For example:

  personRepo.findByName(name).ifPresent(person-> {
            person.setEmail("[email protected]");
            personRepo.save(person);
            log.info("Updated Data: {}", person);
        });

@CreatedDate will always be null if you didn't add the @Version to your model class. It works with @Version

One more point to add here if you didn't add @Version to your model class and you're trying to update the same with having model class @Version, it will again give you duplicate id error.

Upvotes: 3

Philip
Philip

Reputation: 185

I still can't figure out the problem. However, even though I have the exactly the same setting as the post above, since I upgrade Spring Boot from 2.1.0 to 2.1.1, everything works fine now(no matter what type of version I am using, Long/long)

Following is the library version I'm using right now.

spring-boot-starter-data-mongodb:2.1.1.RELEASE:
  -> spring-data-mongo:2.1.3.RELEASE
  -> mongodb-driver:3.8.2

Upvotes: 1

Related Questions