google-code-export / morphia

Automatically exported from code.google.com/p/morphia
1 stars 0 forks source link

Retrieving a Map of Maps #427

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
I would like to be able to persist and retrieve, amongst other things, a map of 
maps in a MongoDB collection.

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.

What version are you using? (0.99/2.8.0/2.0.7)(Morphia/Driver/MongoDB)

Please include a stack trace below:
Exception in thread "main" java.lang.RuntimeException: 
java.lang.RuntimeException: java.lang.RuntimeException: 
java.lang.RuntimeException: java.lang.RuntimeException: 
java.lang.RuntimeException: java.lang.NullPointerException
    at com.google.code.morphia.mapping.Mapper.fromDb(Mapper.java:483)
    at com.google.code.morphia.mapping.Mapper.fromDBObject(Mapper.java:267)
    at com.google.code.morphia.query.MorphiaIterator.processItem(MorphiaIterator.java:53)
    at com.google.code.morphia.query.MorphiaIterator.next(MorphiaIterator.java:48)
    at test.Owner.main(InventoryMain.java:98)
Caused by: java.lang.RuntimeException: java.lang.RuntimeException: 
java.lang.RuntimeException: java.lang.RuntimeException: 
java.lang.RuntimeException: java.lang.NullPointerException
    at com.google.code.morphia.mapping.EmbeddedMapper.fromDBObject(EmbeddedMapper.java:146)
    at com.google.code.morphia.mapping.Mapper.readMappedField(Mapper.java:499)
    at com.google.code.morphia.mapping.Mapper.fromDb(Mapper.java:480)
    ... 4 more
Caused by: java.lang.RuntimeException: java.lang.RuntimeException: 
java.lang.RuntimeException: java.lang.RuntimeException: 
java.lang.NullPointerException
    at com.google.code.morphia.mapping.Mapper.fromDb(Mapper.java:483)
    at com.google.code.morphia.mapping.EmbeddedMapper.fromDBObject(EmbeddedMapper.java:137)
    ... 6 more
Caused by: java.lang.RuntimeException: java.lang.RuntimeException: 
java.lang.RuntimeException: java.lang.NullPointerException
    at com.google.code.morphia.mapping.EmbeddedMapper.fromDBObject(EmbeddedMapper.java:146)
    at com.google.code.morphia.mapping.Mapper.readMappedField(Mapper.java:499)
    at com.google.code.morphia.mapping.Mapper.fromDb(Mapper.java:480)
    ... 7 more
Caused by: java.lang.RuntimeException: java.lang.RuntimeException: 
java.lang.NullPointerException
    at com.google.code.morphia.mapping.Mapper.fromDb(Mapper.java:483)
    at com.google.code.morphia.mapping.EmbeddedMapper.fromDBObject(EmbeddedMapper.java:137)
    ... 9 more
Caused by: java.lang.RuntimeException: java.lang.NullPointerException
    at com.google.code.morphia.mapping.ValueMapper.fromDBObject(ValueMapper.java:27)
    at com.google.code.morphia.mapping.Mapper.readMappedField(Mapper.java:497)
    at com.google.code.morphia.mapping.Mapper.fromDb(Mapper.java:480)
    ... 10 more
Caused by: java.lang.NullPointerException
    at com.google.code.morphia.mapping.DefaultCreator.createMap(DefaultCreator.java:97)
    at com.google.code.morphia.converters.MapOfValuesConverter.decode(MapOfValuesConverter.java:37)
    at com.google.code.morphia.converters.DefaultConverters.decode(DefaultConverters.java:199)
    at com.google.code.morphia.converters.DefaultConverters.decode(DefaultConverters.java:203)
    at com.google.code.morphia.converters.MapOfValuesConverter.decode(MapOfValuesConverter.java:40)
    at com.google.code.morphia.converters.DefaultConverters.fromDBObject(DefaultConverters.java:129)
    at com.google.code.morphia.mapping.ValueMapper.fromDBObject(ValueMapper.java:25)
    ... 12 more

Original issue reported on code.google.com by ron.tuf...@gmail.com on 10 Sep 2012 at 3:15

GoogleCodeExporter commented 9 years ago
Whilst obfuscating some of the business details, I edited the 7th line of the 
stack trace. The edit was incomplete. Please ignore any miss-match between the 
class name and the package details.

Original comment by ron.tuf...@gmail.com on 10 Sep 2012 at 3:18

GoogleCodeExporter commented 9 years ago
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.

 - Instead of using the @Property annotation use @Embedded. and
 - Declare the the concrete HashMap and not use the Map interface

@Embedded("vehicles")
private HashMap<String, HashMap<String, Integer>> vehicles = new 
HashMap<String, HashMap<String, Integer>>();

Specifying the concrete class in either the @Property or @Embedded annotation 
did nothing to help resolve the constructor for the inner HashMap.

Original comment by ron.tuf...@gmail.com on 11 Sep 2012 at 8:20