1
votes

I'm trying to save and load an object containing a @DBRef to another object that happens to be of a child class type and Spring Data MongoDB loads the field as null.

class Person {
    @Id
    private long id;
    @DBRef
    private Food food;
}

class Food {
    @Id
    private long id;
}

class Burger extends Food {}

I'm saving the objects separately:

Burger burger = new Burger();
foodRepository.save(burger);

Person person = new Person();
person.setFood(burger);
personRepository.save(person);

What happens is that the burger object gets saved in the food collection with the _class value of Burger in MongoDB and the $ref in the Person document points to burger and not food.

person collection:

{
    "_id" : NumberLong(1),
    "food" : {
        "$ref" : "burger",
        "$id" : NumberLong(2)
    },
    "_class" : "Person"
}

food collection:

{
    "_id" : NumberLong(2),
    "_class" : "Burger"
}

If I load the object using findAll() or findById(), the food field is null. But if i use findByFood() with the burger object, the person object is returned. What am I not understanding correctly here?

personRepository.findById(1L).getFood(); // null
personRepository.findByFood(burger);     // Person object
1

1 Answers

0
votes

This got answered in the Spring Data MongoDB JIRA board by Christoph Strobl. Pasting his answer below in case anyone finds this. I personally picked Option 1 to solve my issue.


Persisting different types of Food via a Repository uses the Repository interface type argument to determine the actual MongoDB collection. In this case all subtypes of Food will end up in the food collection, unless they are explicitly redirected via the collection attribute of the @Document annotation.

The Person repository on the other hand does not know about the Food Repository and the collection routing to the food collection. So the mapping sees the Burger entity in Person#food and creates a reference to the burger collection, that then cannot be resolved, leading to the null value you see.

{
    "_id" : 1,
    "food" : {
        "$ref" : "burger",
        "$id" : 1
    },
    "_class" : "Person"
}

You could try one of the following:

  1. Use the @Document annotation and explicitly name the collection to store Food and it sub types, so that the mapping layer can set the $ref correctly.
@Document("food")
class Food {
    @Id private long id;
}
  1. Use MongoOpperations#save to persist Food. That way every subclass will end up in its own collection, which allows "$ref" : "burger" to be resolved.