简体   繁体   中英

How does Grails MongoDB plugin handle polymorphism?

I have an object like the following:

class User {
    static mapWith="mongo"
    static embedded = [ 'profiles' ]

    String email
    List<Profile> profiles;
}

interface Profile {
}

class Profile1 implements Profile {
}

class Profile2 implements Profile {
}

If I add the concrete classes Profile1 or Profile2 to the User object and save it to the database it throws an exception when reading that object back out of MongoDB. I don't see any information being saved to the DB to identify which type of object it should instantiate in this situation. And there is exactly ZERO documentation about how this case is handled. Other frameworks have mechanisms for handling this so either Grails MongoDB is woefully broken, or this is just undocumented (again). So how do I fix this?

The exception is the following:

| Error 2013-06-12 18:48:00,390 [http-bio-8080-exec-5] ERROR errors.GrailsExceptionResolver  - InstantiationException occurred when processing request: [POST] /mpa/user/authenticate -parameters:

  email: carhubb@gmail.com
  password: ***
  com.mycompany.security.Profile. Stacktrace follows:
  Message: com.mycompany.security.Profile
  Line | Method
  ->>  342 | newInstance0                        in java.lang.Class
  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
  |    310 | newInstance                         in     ''

Grails MongoDB is not at all broken and every use case might not get documented. :)
Your use case works fine as expected and successfully tested as shown below.

// src/groovy
interface Profile {
    Integer getDuration()
}

import org.bson.types.ObjectId
class Profile1 implements Profile {
    ObjectId id
    String profileName
    String type
    Date effectiveStartDate
    Date effectiveEndDate

    Integer getDuration(){
        effectiveEndDate - effectiveStartDate
    }

    static mapWith = "mongo"
}

import org.bson.types.ObjectId
class Profile2 implements Profile{
    ObjectId id
    String profileName
    String type
    Date effectiveStartDate
    Date effectiveEndDate

    static mapWith = "mongo"

    Integer getDuration(){
        effectiveEndDate - effectiveStartDate
    }
}
class User {
    ObjectId id

    static mapWith = "mongo"
    static embedded = ['profiles']

    String email
    List<Profile> profiles
}

class UserController {

    def index() {
        def profile1 = new Profile1(type: 'Individual',
                                    profileName: 'IndividualProfile',
                                    effectiveStartDate: new Date(),
                                    effectiveEndDate: new Date() + 100) as Profile
        def profile2 = new Profile2(type: 'Company',
                                    profileName: 'CompanyProfile',
                                    effectiveStartDate: new Date(),
                                    effectiveEndDate: new Date() + 50) as Profile

        println profile1.duration //prints 100
        println profile2.duration //prints 50

        def user = new User(profiles: [profile1, profile2], email: 'abc@gmail.com').save(flush: true)

        render user as JSON
    }
}

//db.user.find()
{ 
    "_id" : ObjectId("51ba8d55892cb98368b2f1e5"), 
    "email" : "abc@gmail.com", 
    "profiles" : [{   
            "effectiveEndDate" : ISODate("2013-09-22T03:26:13.396Z"),   
            "effectiveStartDate" : ISODate("2013-06-14T03:26:13.396Z"),   
            "profileName" : "IndividualProfile",    
            "type" : "Individual" 
        },
        {   
            "effectiveEndDate" : ISODate("2013-08-03T03:26:13.423Z"),       
            "effectiveStartDate" : ISODate("2013-06-14T03:26:13.423Z"),   
            "profileName" : "CompanyProfile",
            "type" : "Company" 
        } 
    ], 
    "version" : 0 
}

You can also find the above setup here .

Note:- For simplistic usage Profile1 and Profile2 is designed alike.

The actual answer is that grails seems to handle classes but not interfaces which is truly bizarre because if you handle polymorphism for classes it's trivial to handle it for interfaces because you can handle it the same way. But if you use classes for all reference types it will add a special '_class' property to mongodb and it will use that to instantiate the object of the actual reference the object pointed to when it was saved. Now how hard was that to explain in one paragraph rather than trolling through pages of source code and unit tests?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM