db-ojb-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Brendan <bboe...@tpg.com.au>
Subject Re: Extents and the various inheritance hierarchy mappings
Date Thu, 06 Nov 2003 01:14:24 GMT
Hi All,

(Sorry for the delay in replying to Jakob's email.  Work has been just 
a little bit busy!)

> imo it's essential to keep the primary key unique within the 
> hierarchy. consider the following situation:
> the well known productgroup is abstract and has extents (ie. food and 
> non-food). we have a row in article-table referencing productgroup 42. 
> how do we know whether it's a food- or non-food-group ?
>

Good question.  I have some thoughts on what I would expect to be the 
behaviour of OJB (whether they are logical or realistic expectations 
remains to be seen ;-).  It would be good when having this dicussion if 
we could use a common set of classes to discuss the expected behaviour. 
  I'm not sure there is one in the test code that we could use.  Any 
suggestions?

I'll return to my example of the Entity-Role pattern as it also 
highlights some other issues.  I'll recouch your question in those 
terms: "We have a row in the ROLE table referencing entity 42.  How do 
we know whether it's a Person or a School?"  A similar question: "I 
have an entry in the role table with id 55.  How do I know whether that 
data belongs to a Role (if Role is not 'abstract' and is allowed to 
have instances), a Student or a Teacher?"

Please forgive me if, in the following discussion, I go over ground 
that has already been covered in depth before I started to look at this 
stuff.  If there is a design doc that discusses the resolution (and 
associated rationale) of any of these issues then I would appreciate it 
if someone would point me at it.

Here's what I think is the appropriate behaviour of OJB with regard to 
object loading and queries (against supertypes, types and subtypes, 
both as collection queries and identity queries):

1. When instantiating an object with a given id from the database, the 
type of the object should be the same as when it was first created (ie: 
not an instance of a superclass).  So, if I have created a Student 
object and have saved it and now load it again from scratch, the 
following queries should give me the 'same' instance of Student:
broker.getCollectionByQuery(Student.class)  --> a Student
broker.getCollectionByQuery(Role.class)  --> a Student (_not_ a Role)
(I'll discuss identity-based queries below.)

2. Anytime an object is referenced through a non-inheritance 
association (ie: 0..1, 1, 0..n, 1..n) then the _real_ object should be 
loaded, not a superclass.  In the process of loading a Student, an 
ephemeral instance of Role is also loaded to handle the superclass 
data.  Role has a foreign key back to Entity.  However, Entity is 
essentially abstract so I should expect to see the resultant student's 
entity as a Person, not an Entity.  ie:
aStudent.entity().getClass() == Person.class  (and not Entity.class)

3. When you ask for an object by it's identity, there are a number of 
ways of doing it.  OJB currently supports three constructors for 
Identity:
Identity(Class realClass, Class topLevel, Object[] pkValues)
Identity(Object objectToIdentify, PersistenceBroker targetBroker)
Identity(Object objectToIdentify, PersistenceBroker targetBroker, 
ClassDescriptor cld)
I'm not quite sure I understand the utility of the last two.  Can 
someone demonstrate a real-world use for these two forms?  The first 
one is the totally flexible option but, unfortunately, requires someone 
to know the inheritance hierarchy of the classes and/or extents.  
Shouldn't OJB support a simple getObjectByIdentity method:
broker.getObjectByIdentity(Object pkValue, Class type)
The second parameter is needed to identify not the _exact_ type of the 
class but the _starting point_ IN the _type hierarchy_ for the object 
you're trying to find.  Some examples:

3.1 Subclasses and superclass in one shared table

ROLE_TABLE
s1:  id = 33  class_name = Student
t1:  id = 34  class_name = Teacher

broker.getObjectByIdentity(new Integer(33), Student.class) --> s1  
(type = Student)
broker.getObjectByIdentity(new Integer(33), Teacher.class) --> null
broker.getObjectByIdentity(new Integer(33), Role.class) --> s1  (type = 
Student)

broker.getObjectByIdentity(new Integer(34), Student.class) --> null
broker.getObjectByIdentity(new Integer(34), Teacher.class) --> t1  
(type = Teacher)
broker.getObjectByIdentity(new Integer(34), Role.class) --> t1  (type = 
Teacher)

The rationale for the Role queries is as follows:
The Role object with id 33 is _actually_ an instance of Student.  The 
class_name determines this.
The Role object with id 34 is _actually_ an instance of Teacher.  The 
class_name determines this.

The rationale for the null results is as follows:
There is _no_ Teacher with an id of 33
There is _no_ Student with an id of 34

3.2 Subclasses in separate 'concrete' tables (with included superclass 
data)

STUDENT_TABLE
s1:  id = 33

TEACHER_TABLE
t1:  id = 34

broker.getObjectByIdentity(new Integer(33), Student.class) --> s1  
(type = Student)
broker.getObjectByIdentity(new Integer(33), Teacher.class) --> null
broker.getObjectByIdentity(new Integer(33), Role.class) --> s1  (type = 
Student)

broker.getObjectByIdentity(new Integer(34), Student.class) --> null
broker.getObjectByIdentity(new Integer(34), Teacher.class) --> t1  
(type = Teacher)
broker.getObjectByIdentity(new Integer(34), Role.class) --> t1  (type = 
Teacher)

The rationale for the Role queries is as follows:
The Role object with id 33 is _actually_ an instance of Student.  The 
extent definition is required to work this out.
The Role object with id 34 is _actually_ an instance of Teacher.  The 
extent definition is required to work this out.

The rationale for the null results is as follows:
There is _no_ Teacher with an id of 33
There is _no_ Student with an id of 34

3.3 Separate tables for inheritance, _shared_ primary key between 
subclasses and superclasses:

ROLE_TABLE
r1:  id = 33
r2:  id = 34

STUDENT_TABLE
s1:  id = 33

TEACHER_TABLE
t1:  id = 34

broker.getObjectByIdentity(new Integer(33), Student.class) --> s1  
(type = Student)
broker.getObjectByIdentity(new Integer(33), Teacher.class) --> null
broker.getObjectByIdentity(new Integer(33), Role.class) --> s1  (type = 
Student)

broker.getObjectByIdentity(new Integer(34), Student.class) --> null
broker.getObjectByIdentity(new Integer(34), Teacher.class) --> t1  
(type = Teacher)
broker.getObjectByIdentity(new Integer(34), Role.class) --> t1  (type = 
Teacher)

The rationale for the Role queries is as follows:
The Role object with id 33 is _actually_ an instance of Student.  The 
extent definition and a query down into subclass tables is required to 
work this out.
The Role object with id 34 is _actually_ an instance of Teacher.  The 
extent definition and a query down into subclass tables is required to 
work this out.

The rationale for the null results is as follows:
There is _no_ Teacher with an id of 33
There is _no_ Student with an id of 34

3.4 Separate tables for inheritance, separate key between subclasses 
and superclasses:

ROLE_TABLE
r1:  id = 33
r2:  id = 35

STUDENT_TABLE
s1:  id = 34  role_id = 33

TEACHER_TABLE
t1:  id = 36  role_id = 35

broker.getObjectByIdentity(new Integer(34), Student.class) --> s1  
(type = Student)
broker.getObjectByIdentity(new Integer(34), Teacher.class) --> null
broker.getObjectByIdentity(new Integer(34), Role.class) --> null
broker.getObjectByIdentity(new Integer(33), Student.class) --> null
broker.getObjectByIdentity(new Integer(33), Teacher.class) --> null
broker.getObjectByIdentity(new Integer(33), Role.class) --> s1  (type = 
Student)

broker.getObjectByIdentity(new Integer(36), Student.class) --> null
broker.getObjectByIdentity(new Integer(36), Teacher.class) --> t1  
(type = Teacher)
broker.getObjectByIdentity(new Integer(36), Role.class) --> null
broker.getObjectByIdentity(new Integer(35), Student.class) --> null
broker.getObjectByIdentity(new Integer(35), Teacher.class) --> null
broker.getObjectByIdentity(new Integer(35), Role.class) --> t1  (type = 
Teacher)

This is by far and away the most complicated case (and another reason I 
prefer the mapping in 3.3).

When you ask for a Student with id 34, it is pretty easy to see this 
gives you s1.  When you ask for a Role with id 34 you get back null.  
This is because there is no instance of class Role (or an instance that 
has a supertype or Role) with an id (or role_id in the case of a 
subtype) equal to 34.  There is, however an instance of Role with id 
33.  However, it's actual class is Student!  Of course, a query down 
into subclass tables and the use of extents is required to get the 
right object.

(Similar rationale for Teacher.)

It is interesting to note that the identity queries against the mapping 
schemes in 3.1, 3.2 and 3.3 all produce the same results.  The results 
from the mapping in 3.4 are a little counter-intuitive, imo.

I should also note that I think case 2 is broken with the inheritance 
hierarchy I prefer (3.3).   I have a pretty simple fix that will solve 
the problem but I think it should wait until we finalise exactly what 
behaviour we expect OJB to have.

Brendan


---------------------------------------------------------------------
To unsubscribe, e-mail: ojb-dev-unsubscribe@db.apache.org
For additional commands, e-mail: ojb-dev-help@db.apache.org


Mime
View raw message