I would like to be able to persist and retrieve, amongst other things, a map of maps in a MongoDB collection. I am using Java to access the MongoDB via Morphia.
The example I am using below is a collection that contains documents detailing the owners of various cars. In this example the number of vehicles of a specific make and model are stored in a map of maps
The majority of the properties are working with no problems experienced, but for the case where a property is a map of a map defined in the following way:
@Property("vehicles")
private Map<String, Map<String, Integer> vehicles = new HashMap<String, HashMap<String, Integer>>();
The object is created (some values inserted into the map) and persisted to the Mongo database as one would expect it to be:
"vehicles" : {
"FORD" : {
"FIESTA" : 1
},
"TOYOTA" : {
"COROLLA" : 1,
"PRIUS": 1
},
"BMW" : {
"SLK" : 1
}
}
However when the object is retrieved via java code (a query on the MongoDB console works as expected)) in the following way...
Query<Owner> q = ds.find(Owner.class);
System.out.println(q.countAll());
Iterable<Owner> i = q.fetch();
for (Owner o : i) {
System.out.println(o);
}
...the code dies in a horrible way on the q.fetch() line.
Please help :)
The issue stems from the fact that a Map (being an interface) does not have a default constructor, and while Morphia was correctly assigning the constructor for the concrete HashMap on the outer Map it was failing to resolve a constructor for the inner Map. This was resulting in the NullPointerException.
After a lot of debugging and trying this and that, eventually I stumbled (with the help of a colleague) on to the solution.
Declare the maps using the concrete HashMap and not use the Map interface
@Embedded("vehicles")
private HashMap<String, HashMap<String, Integer>> vehicles = new HashMap<String, HashMap<String, Integer>>();
For those of you who are wondering... specifying the concrete class in either the @Property or @Embedded annotation did nothing to help resolve the constructor for the inner HashMap.
Since we were using our own datatype this way
private HashMap<String, HashMap<String, OwnDataType>> vehicles = new HashMap<String, HashMap<String, OwnDataType>>();
all previous recommendations did not work; the only thing making morphia reading the data properly was to transform the OwnDataType to plural form, that is keeping a map inside the OwnDataType itself and not using maps inside of maps:
private HashMap<String, OwnDataTypes> vehicles = new HashMap<String, OwnDataTypes>();
Now everything is working fine.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With