max wrote:
(and of course there are limitations...because of its nature...and hey, could that be why it is not recommended ? :)
You mean, the hibernate's nature or the composite id's one?
Because, in the latter, I would really like to know which are the limitations you speak about.
The only limit I see is in the composite id design under xml mappings. There, one must define a <composite-id> entry which can't be completed or overriden later in the class tree.
In example, the following tables:
Code:
CREATE TABLE basetype (
id INTEGER NOT NULL,
payload1 VARCHAR(32),
PRIMARY KEY(id)
);
CREATE TABLE atype (
id INTEGER NOT NULL,
payload2 VARCHAR(32),
PRIMARY KEY(id),
FOREIGN KEY(id) REFERENCES basetype(id)
);
CREATE TABLE btype (
id INTEGER NOT NULL,
payload3 VARCHAR(32),
PRIMARY KEY(id),
FOREIGN KEY(id) REFERENCES basetype(id)
);
CREATE TABLE version (
id INTEGER NOT NULL,
payload4 VARCHAR(32),
PRIMARY KEY(id)
);
CREATE TABLE aversioned (
idversion INTEGER NOT NULL,
ida INTEGER NOT NULL,
payload5 VARCHAR(32),
payload6 VARCHAR(32),
PRIMARY KEY(idversion, ida),
FOREIGN KEY(idversion) REFERENCES version(id),
FOREIGN KEY(ida) REFERENCES atype(id)
);
CREATE TABLE bversioned (
idversion INTEGER NOT NULL,
idb INTEGER NOT NULL,
payload5 VARCHAR(32),
payload7 VARCHAR(32),
PRIMARY KEY(idversion, ida),
FOREIGN KEY(idversion) REFERENCES version(id),
FOREIGN KEY(idb) REFERENCES btype(id)
);
may be mapped (and I would like to) by the following class tree:
Code:
class BaseType {
Integer id;
String payload1;
Set<BaseVersioned> versionedTypes;
}
class AType extends BaseType {
String payload2;
}
class BType extends BaseType {
String payload3;
}
class Version {
Integer id;
String payload4;
}
abstract class BaseVersioned {
Version version;
String payload5;
}
class AVersioned extends BaseVersioned {
AType a;
String payload6;
}
class BVersioned extends BaseVersioned {
BType b;
String payload7;
}
Well. The problem is at the "versioned" tree.
Under xml mapping you would specify BaseVersioned as an abstract <class>, in which AVersioned and BVersioned are defined as <union-class>es. The problem is that you must define a <composite-id> entry at the BaseVersioned level and can't simply define just a partial primary key in it, to be completed in AVersioned and BVersioned <union-class>es. Xml mapping is somehow imperfect in dealing with composite ids. The hibernate team didn't explore composites to an enough powerful extent, I believe.
On the other hand, annontation would theoretically allow such a design: you define an @Id @ManyToOne on the version field in the BaseVersioned, then AVersioned completes the composite id thanks to the "a" field, while BVersioned completes the composite id with the "b" field.
A step toward perfection? Well, no: it doesn't work. Hibernate doesn't even want to deal with such a case reporting an error at configure time because of a "id overriding" (?!?!). A "composites are evil" message would fit better.
Also please note that this simple, fallback case:
Code:
class AVersioned {
Version version;
AType a;
String payload5;
String payload6;
}
class BVersioned {
Version version;
BType b;
String payload5;
String payload7;
}
won't work either with annotations. When building suitable (and useless) AVersioned and BVersioned primary-key classes and using the @IdClass annotation in AVersioned and BVersioned, hibernate complains spitting an "org.hibernate.AnnotationException: AVersionedPK has no persistent id property".
The same happens when you want to reduce coding by marking the PK classes with @MappedSuperclass and attempting to extend your AVersioned and BVersioned with them (which would make sense to me).
So, where's the very bad design of this simple case? Am I so guilty in just posting it? Isn't instead that the hibernate staff and, maybe, all the ejb3 designers, fear the inherent complexity of composite keys and prefer to relegate them to the "legacy" jail instead of taking a serious approach on the matter?