Inheritance With Custom Discriminator Field

258 views
Skip to first unread message

kreep

unread,
Jan 30, 2011, 2:34:20 PM1/30/11
to Morphia
Short version:

I want to do the equivalent of this in Morphia:

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="OBJECT_TYPE",
discriminatorType=DiscriminatorType.STRING) //Discriminator column
cannot be auot-created className field

Long Version:

I'm a bit of a Morphia newbie so I apologize in advance if this is a
dumb question.

I have a document that contains an embedded document with an address.
This address is either a PO Box or a street address. Because my
program handles po boxes and street addresses in significantly
different ways, I have a model class for each, both of which descend
from an abstract class Address.


With:

public class User
{
@Embedded List<Address> addresses;
}

Everything works fine as long as the className property is set on the
addresses being loaded.

The issue is that the data is not initially created by the application
(a ETL process runs on a regular basis and loads the db). I could
modify the ETL process to add an appropriate className field, but I
don't like having it have to know so much about the application. Also,
there's already a natural discriminator field in the data. Is there
any annotation or overriding I can do to get the mapper to map to a
POBoxAddress or StreetAddress based on the value of that column
instead of className?

Scott Hernandez

unread,
Jan 30, 2011, 3:07:29 PM1/30/11
to mor...@googlegroups.com
At the moment the className is the discriminator that must exist. If
you cannot declare the concrete type, and in this case you use an
interface, then the system must store the className. This is how
heterogeneous lists are handled.

There are two issues which should help the situation but aren't there yet.
http://code.google.com/p/morphia/issues/detail?id=82
http://code.google.com/p/morphia/issues/detail?id=22


There is a way to do this but it isn't very modular or clean.

There is an ObjectFactory interface that you can implement (extending
DefaultCreator).

class CustomCreator extends DefaultCreator {
@Override
public Object createInstance(Mapper mapr, MappedField mf, DBObject dbObj) {
if(ReflectionUtils.implementsInterface(mf.getType(),
Address.class) || ReflectionUtils.implementsInterface(mf.getSubType(),
Address.class))
//create and return correct instance of Address
else
return super.createInstance(....);
}
}

Then you must set this to be the ObjectFactory for your instances of
the mapper (in Morphia or the Datastore).

This is all untested and I've written it in textfield in my web-based
mail reader, so please do fix my typo/syntax errors.

I would suggest that for now you not do this because it may change
over time as it is mostly internal bits, but if you must get this
working there are few options. When the other issues above are
resolved there will be a much cleaner way to do this.

If I get time later, and you don't get it working, I can mock up a
quick test to see if there are any (conceptual) issues.

Reply all
Reply to author
Forward
0 new messages