Uploaded image for project: 'Spring Data MongoDB'
  1. Spring Data MongoDB
  2. DATAMONGO-2346

Field annotated with @LastModifiedDate is not persisted in Kotlin data classes



    • Type: Bug
    • Status: Closed
    • Priority: Minor
    • Resolution: Fixed
    • Affects Version/s: 2.1.10 (Lovelace SR10)
    • Fix Version/s: 2.2 RC3 (Moore)
    • Component/s: None
    • Labels:


      After updating from 2.0.x to 2.1.10, we noticed that the new value of a field annotated with the @LastModifiedDate annotation is not persisted, in case of Kotlin data classes.

      Please, consider the following data class: 

      data class Entity(
       // ...
       @LastModifiedDate val modificationDate: Instant?,
       // ...

       and the following use case:

      def inserted = entityRepository.save(entity)
      def modified = // do modifications of 'inserted' that take some time
      def updated = entityRepository.save(modified)
      def fetched = entityRepository.findById(100L).get()

      Now, updated.modificationDate is updated and points to a correct point of time, but fetched.modificationDate is has the old value and is the same as inserted.modificationDate.

      When the field is var instead of val then the new value of modificationDate is persisted, and fetched.modificationDate has the expected value.

      data class Entity(
       // ...
       @LastModifiedDate var modificationDate: Instant?,
       // ...

      Adding a wither doesn't change much, and the field is not persisted, too.

      data class Entity(
       // ...
       @LastModifiedDate val modificationDate: Instant?,
       // ...
      ) {
       fun withModificationDate(modificationDate: Instant): EntityWither = copy(modificationDate = modificationDate)

      I prepared a minimal, example project that shows the issue here.

      Also, I noticed that in ReactiveMongoTemplate::doSaveVersioned the annotated field is changed, but then the old value is persisted:

      BeforeConvertEvent<T> event = new BeforeConvertEvent<>(toSave, collectionName);
      // The 'afterEvent' contains the correct value of the 'modificationDate' field 
      T afterEvent = ReactiveMongoTemplate.this.maybeEmitEvent(event).getSource();
      // Notice that 'toSave' is mapped, not 'afterEvent'. It means that we skip the changes in the entity caused by the auditing process. 'AfterEvent' should be passed to forEntity(), not 'toSave', I guess(?)
      MappedDocument mapped = operations.forEntity(toSave).toMappedDocument(mongoConverter);
      Document document = mapped.getDocument();
      ReactiveMongoTemplate.this.maybeEmitEvent(new BeforeSaveEvent<>(afterEvent, document, collectionName));
      // There's an inconsistency, we persist 'toSave' which doesn't contain the auditing changes, but then we return 'afterEvent' with the changes.
      return doUpdate(collectionName, query, mapped.updateWithoutId(), afterEvent.getClass(), false, false)
       .map(result -> {
       return maybeEmitEvent(new AfterSaveEvent<T>(afterEvent, document, collectionName)).getSource();

      If this is indeed a bug, I would like to take a deeper look at it, and prepare a PR with a fix.




            cstrobl Christoph Strobl
            wjur Wojciech Jurczyk
            Last updater:
            Mark Paluch
            0 Vote for this issue
            3 Start watching this issue