Search code examples
spring-data-jpaspring-aopcqrssaga

How solve the problem of that a message consumer try to retrieve an object that has not yet been inserted on DB?


I am implementing a Saga.

After a SomthingCreated event is published and then Something is retrieved on the other process.

The problem is that SomthingRepository.findById(somthingCreated.id()) returns null on the other process because the Something object is not inserted on DB yet.

@Transactional
public String createSomething(long userId, String name) {
        
    String somethingId = somethingRepository.newSomethingId();
    Something something = new Something(userId, name);
    somethingRepository.save(something);
    System.out.println("save:"+System.currentTimeMillis());

    return somethingId;
}

the SomthingCreated event is published after save() using Aspect

@After("execution(* com.some.something.application.*.*(..))")
public void publish() {
    DomainEventPublisher.instance().commitPublishing();
    System.out.println("publish:"+System.currentTimeMillis());
}

And then, the message consumer invokes application logic.

@Transactional
public String handle(SomthingCreated somthingCreated) {
    System.out.println("handle:"+System.currentTimeMillis());       
    Something something = somethingRepository.findById(somthingCreated.id());
    something.do(); //<-- NULLPointerException
}

I think The execution time is like this,
save:1643092272658
publish:1643092272852
handle:1643092272870
(the interval between publish and handle is faster than the interval between save and publish although the event passed the network!)

What is that I am missing. I am using JPA and RabbitMQ on Spring.

The repository is like this,

@Repository
public class JpaSomethingRepository implements SomethingRepository {

    @PersistenceContext
    private EntityManager entityManager;
    
    @Override
    public void save(Something something) {
        entityManager.persist(something);
    }

How can I solve this problem?

I could add sleep-method on handle-method and It works but It seems ugly.

I am using entityManager.persist() with @Transactional but why it is not inserted to DB in the same transaction?

Thank you for your answer in advance.


Solution

  • It was because of the order in which the advice of AOP was applied.

    I read this @Tansactional and @Aspect ordering

    And then, I added the code below to configure the Aspect order.

    @EnableAspectJAutoProxy
    @EnableTransactionManagement(order = 2)
    
    @Aspect
    @Order(1)
    public class EventProcessor {
        ...
        
        @After("execution(* com.some.somethin.application.*.*(..))")
        public void publish() {
            DomainEventPublisher.instance().commitPublishing();
        }
    }
    

    I applied for the order so that more inner @transactional proxy is executed first and then the EventProcessor proxy publish event.