hibernatejpaspring-data-jpa

domain events registered during @PrePersist are not evaluated


I created a simple project with contains only one Entity which inherits from AbstractAggregateRoot and tries to register an event during the @PrePersist JPA lifecycle event.

    @PrePersist
    fun addCreatedEvent() {
        logger.info("Adding created event for $this")
        this.registerEvent(ExampleCreated(this))
    }

But when calling the save/saveAndFlush methods on the repository, the event will not be passed on to the EventListener methods on my service.

    fun createExample(id: Int, name: String): ExampleEntity{

        val entityToSave = ExampleEntity(id, name)
        logger.info("saving entity $entityToSave")
        val savedEntity = repository.saveAndFlush(entityToSave)
        logger.info("saved entity $savedEntity")
        return savedEntity
    }

The problem seems to be, that the PrePersist method is called on the object instance that is returned from the save method but the domainEvents method is called on the instance that is passed to the save method.

I created a small project to show this problem: https://github.com/thuri/spring-domain-events

I'm not sure whether I'm doing something unintended here or if this is a bug. Would be great if somebody could help me out here.

If there is any other way to create an domain event for "Entity Created" then that may be enough for me.

When doing something similar with @PreRemove the handling seems to work correctly. But that's probably just because the instance passed to the repositorys delete methode is already contained in the persistence context.

UPDATE regarding @PreRemove: there are some cases in which this also won't work. Not sure yet what is the reason there. If I call the service methods from the main method than it doesn' work. If the service get's is own method which calls the methods on it's own it does work. Probably something to to with the @Transactional

UPDATE2 regarding @PreRemove: When the entity is detached/removed from entity manager the PreRemove lifecycle event is called on a new instance that is fetched from DB but the domainEvents method is called on the instance that was passed to the delete method of the repository. So we have a similar problem as with the PrePersist


Solution

  • After all I've come to the conclusion that the hibernate event listeners may actually not the right place to create the events for "ItemCreated" or "ItemDeleted".

    I think it's save to say that the Domain Objects themselves may not be able to register that they have been created as a new Domain Object or that they have been deleted. Because actually the a Service may be in charge of creating new Domain Objects and deleting the ones that are no longer necessary.

    So we could create such events with [Springs Application Events](https://docs.spring.io/spring-framework/reference/core/beans/context-introduction.html#context-functionality-events) .

    So we could inject an ApplicationEventPublisher into the service and let it fire the Created or Removed Events. But we'd need to make sure to think hard about the ordering in which the domain events are executed. Because it may be that the domain object actually has some events registered which will be fired on repository save* or delete*methods.
    But the events fired via event publisher may be executed earlier or later. That would depend on the annotations on the methods with handle the events and the phase they are attached to. (@EventListener and @TransactionalEventListener) and also the @Async annotation