user1955934
user1955934

Reputation: 3495

Spring WebFlux Reactor - Updating Object In Flux

Given following method:

Flux<Person> getAllPerson(Criteria criteria) 

How do i process the flux by changing a field in each of the Person object in the flux?

getAllPerson(criteria)
                .map(person -> person.setLastUpdated(new Date())

The setLastUpdated(new Date()) returns void

I've tried a lot of different ways but i can't figure out the way to make it compile, for e.g.:

getAllPerson(criteria)
                .map(person -> {
                     person.setLastUpdated(new Date())
                     return person;
                 });

Upvotes: 3

Views: 5299

Answers (3)

OmicronPsi
OmicronPsi

Reputation: 1

You leverage Flux's handle method instead of map (see https://projectreactor.io/docs/core/release/reference/index.html#_handle). With handle you can managed errors that can occur while processing your records.

Date date = new Date();
getAllPerson(criteria)
      .handle(p, sink) -> {
           Person p1 = p; // clone p if needed
           try { 
              // assuming your mutation can generate errors
              person.withLastUpdated(date);
              sink.next(p1);
           } catch(Exception e) {
              sink.error(e);
           }
      })

Upvotes: 0

Michael Berry
Michael Berry

Reputation: 72284

Your second example:

getAllPerson(criteria)
            .map(person -> {
                 person.setLastUpdated(new Date())
                 return person;
             });

...won't compile, but only because it's missing a semicolon on the setLastUpdated() line.

However, by far the best thing to do here (as suggested already) is to make your Person class immutable. You can then use the "wither" pattern (either using lombok or your own implementation) to simply do:

getAllPerson(criteria)
            .map(person -> person.withLastUpdated(new Date()));

If you can't make Person immutable (either because it's an external library, or its mutability is simply too baked into existing code), then all is not lost. You can still produce an immutable wrapper quite easily, eg:

class ImmutablePerson {
    Person person;

    public ImmutablePerson(Person p) {
        this.person = p;
    }

    public Person withFirstName(String name) {
        return new Person(name, person.getLastName(), person.getLastUpdated());
    }

    public Person withLastName(String name) {
        return new Person(person.getFirstName(), name, person.getLastUpdated());
    }

    public Person withLastUpdated(Date date) {
        return new Person(person.getFirstName(), person.getLastName(), date);
    }

    public String getFirstName() {return person.getFirstName();}
    public String getLastName() {return person.getLastName();}
    public Date getLastUpdated() {return person.getLastUpdated();}
    public Person toPerson() {return new Person(person.getFirstName(), person.getLastName(), person.getLastUpdated());}
}

You can then just map to ImmutablePerson in the first step of your reactive chain like so:

getAllPerson(criteria)
        .map(ImmutablePerson::new)
        .map(p -> p.withLastUpdated(new Date()))
        //etc.

Upvotes: 1

Martin Tarj&#225;nyi
Martin Tarj&#225;nyi

Reputation: 9947

You can use the doOnNext operator which is intended to execute this kind of side-effect operations.

Although, generally it is a better approach to keep the Person class immutable and when you would need to modify something you would just create a copy of it, in that case you would be able to use the map operator as expected.

Upvotes: 2

Related Questions