Default implementation mapping for object without "_class" attribute in spring-data-mongodb

Viewed 422

I have the next problem: at the start of the project I had only one implementation of some class:

class Wrapper {
    private Child child;
}

class Child { ... }

and when mongodb serializes this class it never set _class attribute for the child object. Now I need to make the next classes structure:

class Wrapper {
    private Child child;
}

abstract class Child { ... }

class ChildA extends Child { ... }

class ChildB extends Child { ... }

Obviously, then I try to deserialize old documents, it throws an exception because it can't instantiate an abstract class.

The question is: is it any way to specify the default implementation for object deserialization? For example, I want to deserialize the child field into the Child1 class if the _class attribute is not present like this parameter in Jackson library?

1 Answers

This may not be the optimal solution, because I do suspect there are ways to control the mongo deserialization. However, if you want to avoid digging into that well, you could use one of the Mongo lifecycle events. In particular, the onAfterLoad is dispatched after a document is read from the database, but before it has been converted into a java object.

So, using the event, you can examine the incoming json, and even change it before it gets converted... Here is an example for your situation:

@Component
public class WrapperRepositoryListener extends AbstractMongoEventListener<Wrapper> {

    @Override
    public void onAfterLoad(AfterLoadEvent<Wrapper> event) {
        Document doc = event.getDocument();
        Document child = (Document)doc.get("child");        
        if (!child.containsKey("_class")) {   
            child.put("_class", "ChildA");
        }
    }
}

Alternatively, (although I know this isn't the question you asked), depending on how much data you have out there, you could write a mongodb script that simply updates all of the existing records to have the _class field:

db.wrappers.update(
    { 
        "child" : { $exists : true},
        "child._class" : { $exists : false }
    }, 
    {
        $set:{
            "child._class" : "ChildA"
        }
    },    
    { 
        multi: true, 
        upsert: false    
    }
)
Related