6

Following question has been separated from this one: ArrayIndexOutOfBoundsException while Spring save data to MongoDB

I have problem with saving Object to MongoDB. I've noticed that problem might be caused by too complex object. I have following class hierarchy: enter image description here

ClassA is superclass for ClassB and ClassC. ClassD contains map of maps. ClassC contains ClassB.

Code which I invoke is following:

ClassC c = new ClassC()
c.setName("NAME");
mongoOperation.save(c, "Mongo"); // MongoOperations object

The problem is that Mongo doesn't save object's data. It saves only _id and _class.

Actual data

{
    "_id" : ObjectId("53e86cd9c506f66eafaa03cb"),
    "_class" : "com.sample.ClassC"
}

Expected data

{
    "_id" : ObjectId("53e86cd9c506f66eafaa03cb"),
    "_class" : "com.sample.ClassC",
    "name" : "NAME"
}

Funny thing is that when I comment out map field in ClassD everything works fine.

Is it possible to be caused by too complex object which I try to serialize?


EDIT

When I remove bObject from ClassC it also works fine.


EDIT 2

All classes are simple beans with setters and getters.

e.g.

public class ClassD{

    private TreeMap<String, TreeMap<String,String>> map;

    public TreeMap<String, TreeMap<String, String>> getMap() {
        return map;
    }

    public void setMap(TreeMap<String, TreeMap<String, String>> map) {
        this.map = map;
    }
}

EDIT 3

Full example below, it has same class hierarchy as picture above.

public class Application implements CommandLineRunner {

    @Autowired
    private MongoTemplate mongoTemplate;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        ClassC cObject = new ClassC();
        cObject.setName("Jon");
        try {
            mongoTemplate.save(cObject);
        }catch(Exception e){
            e.printStackTrace();
        }
        mongoTemplate.save(cObject);
    }
}


class ClassA{

    private String name;

    private ClassD dObject;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public ClassD getdObject() {
        return dObject;
    }

    public void setdObject(ClassD dObject) {
        this.dObject = dObject;
    }
}

class ClassB extends ClassA {
}

class ClassC extends ClassA {
    private ClassB b;

    public ClassB getB() {
        return b;
    }

    public void setB(ClassB b) {
        this.b = b;
    }
}

class ClassD {

    private TreeMap<String, TreeMap<String, String>> map = new TreeMap<>();

    public TreeMap<String, TreeMap<String, String>> getMap() {
        return map;
    }
    public void setMap(TreeMap<String, TreeMap<String, String>> map) {
        this.map = map;
    }

}
2
  • Can you provide the code of ClassD? Commented Aug 13, 2014 at 9:26
  • All classes are simple beans with setters and getters. Commented Aug 13, 2014 at 9:33

2 Answers 2

3
+50

I guess the MongoConverter in specific version of your spring-data-mongodb.jar works incorrectly. Spring must convert your ClassC instance into DBObject format, then call DBCollection.save to save data into database. You can check the content of DBObject parameter in method "com.mongodb.DBCollection.save" whether it contains correct data as you expect.

I copy your ClassC with complete structure and test, it's fine and cannot reproduce what you described above. I use spring-data-mongdb-1.2.3-RELEASE.jar. What's the version you adopt?

Sign up to request clarification or add additional context in comments.

2 Comments

My version is 1.5.0. When I used 1.0.1 it worked fine, but now I cannot downgrade it to any version lesser than 1.5.0...
Yes, version 1.5.0 has the bug. If you can use HashMap instead TreeMap, it will help you, else you have to wait for fixing the bug by the author. It happens in spring-data-commons-1.8.0-RELEASE.jar. The code treats TreeMap as normal entity class, but not container class. Unfortunately, the field definition in TreeMap has something different to HashMap, such as private transient EntrySet entrySet = null; in TreeMap - it's one of reasons to emit that exception.
3

The following code seems to work:

@EnableAutoConfiguration
public class Application implements CommandLineRunner {

    @Autowired
    private MongoTemplate mongoTemplate;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        Customer customer = new Customer("myself");
        ClassB classB = new ClassB();
        TreeMap<String, TreeMap<String, String>> map =  new TreeMap<String, TreeMap<String, String>>();
        TreeMap<String, String> innermap = new TreeMap<String, String>();
        innermap.put("iam", "cool");
        map.put("innermap", innermap);
        TreeMap<String, String> innermap2 = new TreeMap<String, String>();
        innermap2.put("youare", "yellow");
        map.put("innermap2", innermap2);
        classB.setMap(map);
        customer.setClassB(classB);
        try {
            mongoTemplate.save(customer);
        } catch (Exception e) {
            e.printStackTrace();
        }
        mongoTemplate.save(customer);
        System.out.println(mongoTemplate.findAll(Customer.class));;
    }
}


public class ClassB {

    private TreeMap<String, TreeMap<String, String>> map = new TreeMap<String, TreeMap<String, String>>();

    public TreeMap<String, TreeMap<String, String>> getMap() {
        return map;
    }

    public void setMap(TreeMap<String, TreeMap<String, String>> map) {
        this.map = map;
    }
}


@Document(collection ="customer")
public class Customer {

    @Id
    private String id;

    private String name;

    private ClassB classB;

    public Customer() {
    }

    public Customer(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public ClassB getClassB() {
        return classB;
    }

    public void setClassB(ClassB classB) {
        this.classB = classB;
    }

    @Override
    public String toString() {
        return "Customer [id=" + id + ", name=" + name + ", classB=" + classB
                + "]";
    }

}

But the ArrayIndexOutOfBoundsException-issue is still present.

1 Comment

Your code works fine, but it has completely different class hierarchy. I've attached my code example.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.