2

I'm creating an object Employee, and I also want to create another object Event which acts as a log of the creation of the employee. However, the way I'm doing it does not persist the Event.

    @Transactional
    public Mono<Employee> createEmployee(String name) {

        return Mono.fromCallable(() -> {
            Employee employee = new Employee();
            employee.setName(name);
            return employee;
        })
        .flatMap(employee -> {
            return employeeRepository.save(employee);
        })
        .doOnNext(employee -> {
            Event event = new Event();
            event.setDetail("Created employee : " + employee.getName());

            eventRepository
                .save(event)
                // .subscribe() // that didn't help
                ;
        });
    }

Repositories :

@Repository
public interface EmployeeRepository extends ReactiveCrudRepository<Employee, UUID> {
}

@Repository
public interface EventRepository extends ReactiveCrudRepository<Event, UUID> {
}

The employee will be saved, but not the event. I'm not really sure how this should be coded.

lgean
  • 265
  • 1
  • 2
  • 11

3 Answers3

1

Like this instead:

 .flatMap(employee -> {
        return employeeRepository.save(employee);
    })
 .flatMap(employee -> {
        Event event = new Event();
        event.setDetail("Created employee : " + employee.getName());

        eventRepository
            .save(event)
            .thenReturn(employee);
    })

So, this way you get a fresh Employee after saving in the first flatMap(), propagate it downstream to the next flatMap(), call .save(event) for returning its Mono, but then you replace that Mono with a new one based on an employee variable.

This way everything is going to be subscribed at the right time in right order.

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • Thanks ! Works nicely. However the "why" is a bit confusing to me. I understand that in the end Fluxes/Monos need to be subscribed in order to be "processed", but why couldn't I just trigger the .save() with a .subscribe() right after, just to say "Hey someone's listening, so do your thing" ? Does this "sub-stream" have to be eventually part of the main stream that ends up with the final return statement ? There's a few bits of this process that I don't get :) thanks ! – lgean Aug 21 '19 at 07:57
  • Well, I think it depends from case to case, but I believe that your `eventRepository.save(event)` deals with some data base, like MongoDB. There is no guarantee that its `Mono` for subscription is going to be performed in the same process and in the proper order as your main stream: the subscription to the `Publisher` doesn't mean an immediate execution. And I guess in your case there is some buffer for DB commands to handle, so your out of order subscription might just break something over there. – Artem Bilan Aug 21 '19 at 13:21
  • How's this different from what the OP is doing? In the .doOnNext(), he's subscribing to the inner stream, right? It means it should be triggered. This is exactly what .flatMap is doing. I think doing a .block instead of .subscribe inside the doOnNext should work pretty fine. This would ensure that the function returns only when the event is persisted. – Prashant Pandey Sep 16 '19 at 05:24
0

If you have a reactive treatment inside doOnNext, it will never be run. So, it's up to you to make the subscription. But I think the best is to use the solution of @Artem Bilan.

Zrom
  • 1,192
  • 11
  • 24
-1

This should work:

@Transactional
public Mono<Employee> createEmployee(String name) {

    return Mono.fromCallable(() -> {
        Employee employee = new Employee();
        employee.setName(name);
        return employee;
    })
    .flatMap(employee -> {
        return employeeRepository.save(employee);
    })
    .doOnNext(employee -> {
        Event event = new Event();
        event.setDetail("Created employee : " + employee.getName());

        eventRepository
            .save(event)
            .block() // that didn't help
            ;
    });
}
Prashant Pandey
  • 4,332
  • 3
  • 26
  • 44
  • Why the downvote? This code will work perfectly. The doOnNext would not move forward until the persist takes place. This is ensured by .block(). – Prashant Pandey Sep 21 '19 at 14:09
  • You have to avoid the use of block, you can have an exception like this: java.lang.IllegalStateException: block () / blockFirst () / blockLast () are blocking, which is not supported in thread-http- nio-4. look at this question: https://stackoverflow.com/questions/51449889/block-blockfirst-blocklast-are-blocking-error-when-calling-bodytomono-afte – Zrom Sep 30 '19 at 21:34