3
votes

I am trying to have a list in a model using @DBRef but I can't get it to work. This is my User model:

@Data
@Document
public class User {

    @Id
    @JsonSerialize(using = ToStringSerializer.class)
    private ObjectId id;

    @Indexed(unique = true)
    @NotBlank
    private String email;

    @NotBlank
    private String name;

    @NotBlank
    private String password;

    @DBRef
    private List<Server> servers;
}

Server model:

@Data
@Document
public class Server {

    @Id
    @JsonSerialize(using = ToStringSerializer.class)
    private ObjectId id;

    @NotBlank
    private String name;

    @NotBlank
    private String host;
}

The structure is very simple, every user can have multiple servers. But when I add servers to the user the server is created, but the servers array contains one null entry("servers" : [ null ]). So the server isn't added to the user. This is how I create a server and add it to an user:

@PostMapping
public Mono create(@Valid @RequestBody Server server, Mono<Authentication> authentication) {
    return this.serverRepository.save(server).then(authentication.flatMap(value -> {
        User user = (User) value.getDetails();
        user.getServers().add(server);

        return userRepository.save(user);
    })).map(value -> server);
}

So I simply create and save a server, add the server the user and then save the user. But it doesn't work. I keep having an array with one null entry.

I've seen this page: http://www.baeldung.com/cascading-with-dbref-and-lifecycle-events-in-spring-data-mongodb. But it is for saving the child document, not for linking it. Also it is for a single document, not for an array or list.

Why is my list not being saved correctly?

All my libraries are coming from spring boot version 2.0.0.M6.

UPDATE When removing @DBRef from the user's servers property the servers are getting saved, but they of course get double created, in the server collection and in every user.servers. So the error has something to do with references.

3
can you put Authentication class also, so that we can try the code as it is from our end - pvpkiran
the above code works for me - pvpkiran
@pvpkiran The Authentication interface is from spring(org.springframework.security.core.Authentication) - Jan Wytze
@pvpkiran When I edit other properties from the user object it works fine, it is only the Server list that doesn't work. - Jan Wytze
can you check if user.getServers() returns you a list ? - pvpkiran

3 Answers

2
votes

After some googling I found the answer...
https://jira.spring.io/browse/DATAMONGO-1583
https://jira.spring.io/browse/DATAMONGO-1584

Reactive mongo doesn't support this.

0
votes

Actually there is a way to resolve DbRefs without to using the blocking driver. Yes - the references are resolved in a blocking fashion, but does not require a second connection. In order to achieve this we have to write our own DbRefResolver: NbDbRefResolver.java. In the provided resolver there is a flag: RESOLVE_DB_REFS_BY_ID_ONLY. If is switched on will not going to resolve the DbRefs from the database, but instead will resolve them to fake objects with id only. It is up to implementation to fill the references later in non-blocking fashion.

If the flag RESOLVE_DB_REFS_BY_ID_ONLY is set to false it will eagerly resolve the references by using the non-blocking driver, but will block the execution until the references are resolved. Here is how to register the DbRefResolver in the app: DbConfig.kt

Files attached are provided here: https://jira.spring.io/browse/DATAMONGO-1584

0
votes

Me did it like that for roles :

  @Unwrapped(onEmpty = Unwrapped.OnEmpty.USE_NULL)
  private Collection<Role> roles;

you can check the doc (2021) here : https://spring.io/blog/2021/04/20/what-s-new-in-spring-data-2021-0