springspring-data-rest

Does Spring Data REST event handlers use separate database transactions?


Spring Data REST has the following Event handlers which are fired on HTTP requests like POST, PUT etc.

@RepositoryEventHandler(Author.class) 
public class AuthorEventHandler {
    Logger logger = Logger.getLogger("Class AuthorEventHandler");

    @HandleBeforeCreate
    public void handleAuthorBeforeCreate(Author author){
        logger.info("Inside Author Before Create....");

    }

    @HandleAfterCreate
    public void handleAuthorAfterCreate(Author author){
        logger.info("Inside Author After Create ....");

    }

}

My question is if I access another database entity, eg.Book, within @HandleBeforeCreate and modify it, would it occur in a separate transaction or will it occur in the same transaction as the creation of the Author entity?

I already check the Spring Data REST docs but it is not mentioned there.


Solution

  • From my experience, those handlers are performed beyond the main transaction. Literally 'before' and 'after' it. About 'separate' transaction - if you mark your event handler as @Transactional it will be executed in its individual transaction.


    Publishing domain events from the aggregate root

    If you need to perform some extra actions within the main transaction you can use publishing events from the aggregate root. In this case, you should extend your entity from AbstractAggregateRoot and add to it some methods that register appropriate events, for example:

    @Entity
    public class Model extends AbstractAggregateRoot {
    
       // entity stuff...
    
       public Model initExtraAction(SomeData payload) {
            registerEvent(new ExtraActionEvent(this, payload));
            return this;
       }
    }
    

    where registerEvent is the AbstractAggregateRoot method, and ExtraActionEvent is your custom event, like the folowing:

    @Value
    public class ExtraActionEvent {
        private Model model;
        private SomeData payload;
    }
    

    Then you can implement an ordinary event handler

    @Service
    public class EventHandler {
    
        @EventListener
        @Transactional(propagation = MANDATORY) // optional 
        public void handleExtraActionEvent (ExtraActionEvent e) {
            Model model = e.getModel();
            SomeData payload = e.getPayload();
    
            // Extra actions...
        }
    }
    

    that will be called in the same transaction as the main one (which saves your entity) if you call initExtendAction method before invoking the save method of your repo (to make sure that this will be done in the same transaction you can use an optional @Transactional(propagation = MANDATORY) annotation):

    modelRepo.save(model.initExtraAction(payload));
    

    In the Spring Data REST project we can call initExtraAction method in the 'RepositoryEventHandler' before the entity will be created or updated:

    @RepositoryEventHandler(Model.class) 
    public class ModelEventHandler {
    
        @HandleBeforeCreate
        @HandleBeforeSave
        public void handleBeforeCreateOrSave(Model model){
            // Some manipulations...
            model.initExtraAction(...);
        }
    }
    

    You can find a full example of using AbstractAggregateRoot in the Oliver Gierke Spring RestBucks demo project.

    Additional info: DDD Aggregates and @DomainEvents