Jan Wytze
Jan Wytze

Reputation: 3497

Webflux mongo save multiple models

In my application I have servers and users, each user can have multiple servers. The servers list in User is a @DBref:

@DBRef
private List<Server> servers;

In the controller where I want to create a server I first create and save the server, after that I add the server to the server list in user and save the user:

@PostMapping
public Mono create(@Valid @RequestBody Server server, Mono<Authentication> authentication) {
    return this.serverRepository.save(server) // This works, the server is saved.
            .and(authentication.map(value -> {
                User user = (User) value.getDetails();
                System.out.println(user); // This works, so this function is called.
                if (user.getServers() == null) {
                    // Create empty list so .add can be used.
                    user.setServers(new ArrayList<>());
                }
                user.getServers().add(server);
                return userRepository.save(user); // Isn't working, in the UserCascadeSaveMongoEventListener I don't see it.
            }));
}

I think the way I use .and() isn't right.

To save the relation I have the UserCascadeSaveMongoEventListener:

public class UserCascadeSaveMongoEventListener extends AbstractMongoEventListener<Object> {

    @Autowired
    private MongoOperations mongoOperations;

    @Override
    public void onBeforeConvert(BeforeConvertEvent<Object> event) {
        Object source = event.getSource();
        System.out.println(source); // On server create only the Server is shown, User is not shown.
        if ((source instanceof User) && (((User) source).getServers() != null)) {
            mongoOperations.save(((User) source).getServers());
        }
    }
}

But this class doesn't matter at the moment because the user isn't save at all.

The repositories used are all normal ReactiveCrudRepositories.

What is the right way to reactively call multiple repositories with multiple Mono's?

Upvotes: 1

Views: 1688

Answers (1)

Brian Clozel
Brian Clozel

Reputation: 59086

the save call does not actually save, it's setting up a reactive pipeline that you can use to save. The operation is only performed when something subscribes to it:

Mono<User> savedUser = userRepository.save(user);

So you should compose the various bits in your controller method and make sure that no reactive type is left hanging alone.

You could have seen that by adding a log() operator after the save operation - nothing subscribes to it so the operation is not performed.

Your code should look like (that's if you want to return the created user as a response; if you don't want to return anything, you can add a final then() operator right at the end and make your controller method return a Mono<Void> that signals when the operation is done):

@PostMapping
public Mono<User> create(@Valid @RequestBody Server server, Mono<Authentication> authentication) {
    return this.serverRepository.save(server) // This works, the server is saved.
            .then(authentication.flatMap(value -> {
                User user = (User) value.getDetails();
                System.out.println(user); // This works, so this function is called.
                if (user.getServers() == null) {
                    // Create empty list so .add can be used.
                    user.setServers(new ArrayList<>());
                }
                user.getServers().add(server);
                return userRepository.save(user);
            }));
}

Upvotes: 2

Related Questions