I'm implementing generation of inherited classes in the AndroMDA Hibernate cartridge. I've chosen the <joined-subclass> strategy for the table mapping because tool users.
Things are almost working, but unfortunately have run into a limitation with Hibernate. The DTD defines a class mapping as:
Code:
<!ELEMENT class (
meta*,
jcs-cache?,
(id|composite-id),
discriminator?,
(version|timestamp)?,
(property|many-to-one|one-to-one|component|dynabean|any|map|set|list|bag|idbag|array|primitive-array)*,
((subclass*)|(joined-subclass*))
)>
Note the last line, the allowance is for one or more subclass elements, one or more joined-subclass elements, but not a combination of both.
I presume that this was intentional when the DTD was written. Is this still a factor or can this restriction be removed and the DTD updated to reflect this? We can ignore the rest of this message if so...
The reason that I need to mix these is AndroMDA (correctly) generates two classes on each build and expects a third class (generated the first time only) to exist -- the generated entity class, a factory class, and an entity implementation class containing the business logic of the entity. The generated entity class is abstract and therefore cannot be instantiated. The entity impl class is related to the entity with <subclass>. The results might look like the following:
Code:
/** @hibernate.class */
public abstract class Animal {
// fields, methods and relations generated from XMI model here
}
/** @hibernate.subclass */
public class AnimalImpl extends Animal {
// business methods here
}
public class AnimalFactory {
public static Animal findByPrimaryKey (Session sess, java.lang.Long id)
throws net.sf.hibernate.HibernateException
{
Animal object = (Animal) sess.load(AnimalImpl.class, id);
return object;
}
}
This all works fine without entity inheritance, now we want to add that. Consider this code:
Code:
/** @hibernate.joined-subclass */
public abstract class Dog {
// fields, methods and relations generated from XMI model here
}
/** @hibernate.subclass */
public class DogImpl extends Dog {
// business methods here
}
public class DogFactory {
public static Dog findByPrimaryKey (Session sess, java.lang.Long id)
throws net.sf.hibernate.HibernateException
{
Dog object = (Dog) sess.load(DogImpl.class, id);
return object;
}
}
If we could mix <joined-subclass> and <subclass>, this would work. It seems pretty odd to work the implementation class as a joined-subclass, and it's impractical from a business partner perspective to use <subclass> (each partner may have a different subclass, and we can't mix their data.)
Presuming that we can't change Hibernate, what could be changed in this pattern? I haven't found a good answer for that yet. Dog is abstract, so sess.load(Dog.class, id) will fail. Instantiating Dog as anything but a DogImpl doesn't make sense because we lose access to the business logic. Remember, the xxxImpl exists because the respective abstract base class is generated as a part of the build from the UML model. This separation allows us to generate without merging existing code in tricky ways.
I thought about whether this could be accomplished with Decorators instead of subclassing already, but the interfaces are not consistent in a way that would facilitate that.
Any thoughts?