Uploaded image for project: 'Spring Data Neo4j'
  1. Spring Data Neo4j
  2. DATAGRAPH-1212

Neo4jAuditing not work because of early ID generation

    Details

    • Type: Bug
    • Status: Open
    • Priority: Major
    • Resolution: Unresolved
    • Affects Version/s: 5.2 M2 (Moore), 5.1.5 (Lovelace SR5)
    • Fix Version/s: None
    • Component/s: CORE
    • Labels:
      None

      Description

      Situation

      We're trying to use auditing features via @EnableNeo4jAuditing. For our entity classes, we have a common base class (abstract because we don't want it to appear in the label hierarchy):

      public abstract class BaseEntity {
        public static final String ATTR_UUID = "UUID";
        public static final String ATTR_CREATED_ON = "createdOn";
      
        public static final class UuidGenerationStrategy implements IdStrategy {
          @Override
          public Object generateId(Object entity) {
            return UuidHelper.generateTechnicalId();
          }
        }
      
        @Property(name = ATTR_UUID)
        @Id
        @Index(unique = true)
        @GeneratedValue(strategy = UuidGenerationStrategy.class)
        private String uuid;
      
        @Property(name = ATTR_CREATED_ON)
        @DateLong
        @CreatedDate
        private Instant createdOn;
      
        public String getUuid() {
          return uuid;
        }
      
        public Instant getCreatedOn() {
          return createdOn;
        }
      }

      When calling Neo4jSession#save, the Neo4jAuditingEventListener is called automatically as expected and internally calls IsNewAwareAuditingHandler#markAudited, which then calls Neo4jPersistentEntity#isNew to decide whether the entity should be marked as created (AuditingHandler#markCreated) or as modified (AuditingHandler#markModified).

      Problem

      The way SDN is initialised via Neo4jAuditingRegistrar, Neo4jPersistentEntity#isNew uses the IsNewStrategy implemented in Neo4jPersistentEntity.Neo4jIsNewStrategy#isNew which is not working because it makes its decision based on whether the entity has an id assigned - which is happening automatically before any event listener is called (SaveEventDeleage#preSaveCheck -> SaveEventDelegate#visit -> MappingContext#nativeId -> MappingContext#generateIdIfNecessary).
      So SDN will always assume that an entity is not new.

      Workarounds

      We found two workarounds which are neither documented nor nice but I'm adding them here in case anyone else runs into this issue:

      • Implement org.springframework.data.domain.Persistable in BaseEntity and provide a custom isNew() implementation there. In that case Neo4jPersistentEntity would use PersistableIsNewStrategy#INSTANCE which is calling the entity's isNew() method.
      • Implement a custom AuditingHandler, extend Neo4jAuditingRegistrar to use the custom implementation and implement a custom @EnableNeo4jAuditing annotation.

      Thoughts on the Solution

      I would be happy to create a pull request but I'm not sure what's the preferred way to solve the problem:

      • Generating the Id later would probably require quite some refactoring around SaveEventDelegate and MappingContext and have other side effects. But I feel that this is the only clean solution within the semantics of SDN where each Repository#save call actually results in a MERGE statement.
      • If a @Version property is present in the entity, this field could probably be safely used. This solution is btw the default implementation used in Spring Data's BasicPersistentEntity, which is explicitly overridden in SDN. However that's not applicable to everyone's setup.
      • Easier approaches such as checking if createdOn is null also don't work with the semantics of SDN (save = MERGE) as functionality would be only correct if the user loads the entity from the DB before.

        Attachments

          Activity

            People

            • Assignee:
              gerrit.meier Gerrit Meier
              Reporter:
              nioertel nioertel
              Last updater:
              nioertel
            • Votes:
              2 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

              • Created:
                Updated: