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.