Hi Jakob,
Thanks for your reply! Perhaps I have some ideas on how we could do this. Here are
two:
Idea 1
------
Join all the columns from all tables in the hierarchy together, and then look to see
which join columns are not null, in order to determine the true subclass
I demonstrate this using three classes, just to make it clear that it could scale
(even if sloppily):
- public class A implements InterfaceA
- public class B extends A implements InterfaceA
- public class C extends A implements InterfaceA
A select for InterfaceA would then do something like this:
SELECT a.id, a.value_, b.id, b.value_, c.id, c.value_
FROM (a_table AS a LEFT OUTER JOIN b_table AS b ON a.id=b.id) LEFT OUTER JOIN c_table
AS c ON a.id=c.id
Materializing would look like this:
//This interface is sketchy, but you'll get the idea (I hope)
HierarchyTree tree = HierarchyTree.getTree(superclassWeQueried)
//Loop over the ResultSet
while (rs.next()) {
Class classToMaterialize = superclassWeQueried;
Class[] classesToCheck = null;
while ((classesToCheck = tree.getBranchBelow(classToMaterialize)) != null) {
for (int i=0; i < classesToCheck.length; i++) {
String currentClassPkeyColumn = getPrimaryKeyColumnName(classesToCheck[i]);
rs.getInt(currentClassPkeyColumn);
if (rs.wasNull()) {
continue;
} else {
//The primary key was not null. Therefore, there is data at or below this
branch
classToMaterialize = classesToCheck[i];
break;
}
}
}
Object newMember = this.materialize(rs, classToMaterialize);
someListOfObjectsToReturn.add(newMember);
}
Idea 2
------
Require that we include a column in each class-descriptor that specifies the class as
which this row should be instantiated. This is just like the model where you have
multiple classes mapped to a single table, except that the materilization processes
would have to know to go digging deeper for the further information.
The drawback with this approach could possibly be that multiple queries would be
required to get the further information. This could be minimized by making a list of
identities that need further queries. Or maybe it could be solved by using proxies.
If you queried for InterfaceA.class, then you would get back objects that are
assignment compatible with the true subclass, but have materialized only the elements
defined in InterfaceA. If you cast it to its subclass and access the deeper parts of
the interface, it would trigger the proxy to perform materialization.
---------------------------------
Thoughts on any of this? Also, am I off base by thinking this feature should be
supported?
Sincerely yours,
Justis Peters
Oculan Corp.
[EMAIL PROTECTED]
Jakob Braeuchi [EMAIL PROTECTED] wrote:
> hi justis, wallace,
>
> this problem is caused by Identity#equals noch checking objectsRealClass
> . consider the following situation: a select for InterfaceA fires 2 selects
> SELECT A0.VALUE_,A0.ID FROM A_TABLE A0
> retrieving Table_A objects with ids 1 and 2
> SELECT A0.VALUE_,A0.ID FROM B_TABLE A0 INNER JOIN A_TABLE A1 ON A0.ID=A1.ID
> retrieving Table_B object with id 1
>
> when building the objects from the resultset in
> RsIterator#getObjectFromResultSet an identity is built from the row and
> looked up in the cache. the object (id = 1) from table_B is considered
> to be in the cache because the topLevelClass (InterfaceA) and the pk (1)
> matches !
>
> this problem could be solved by also checking the objectsRealClass in
> Identity#equals. i remember there was quite a big discussion about
> Identity, so i'm not sure if this is a correct soluion ??
>
> another solution could be to use a dedicated column to refer to the
> super-class in the reference-descriptor. this would avoid the pk-clash
> in the cache.
>
> another way is to completly avoid using extents and super-references ;)
>
>
> <class-descriptor class="polymorphtest.InterfaceA">
> <extent-class class-ref="polymorphtest.A" />
> <extent-class class-ref="polymorphtest.B" />
> </class-descriptor>
>
> <class-descriptor class="polymorphtest.A" table="A_TABLE">
> <field-descriptor name="id" column="ID" jdbc-type="INTEGER"
> primarykey="true" autoincrement="true" />
> <field-descriptor name="someValueFromA" column="VALUE_"
> jdbc-type="INTEGER" />
> </class-descriptor>
>
> <class-descriptor class="polymorphtest.B" table="B_TABLE">
> <field-descriptor name="id" column="ID" jdbc-type="INTEGER"
> primarykey="true" autoincrement="true" />
> <field-descriptor name="someValueFromB" column="VALUE_"
> jdbc-type="INTEGER" />
> <reference-descriptor name="super" class-ref="polymorphtest.A">
> <foreignkey field-ref="id" />
> </reference-descriptor>
> </class-descriptor>
>
> jakob
>
> Justis Peters wrote:
>
> >Hi All!
> >
> >Just a couple weeks ago I started using OJB, and I am extremely impressed.
> >My gratitude goes to all who put in the work to make it possible.
> >
> >Upon converting some of the more complicated parts of my object model, I
> >encountered a strange issue. Here is the bug I attempted to submit to
> >scarab. Apparently, scarab is having problems. At first, it assigned an
> >ID to my issue that was already assigned to another issue, so I can't
> >retrieve it. Now, it won't let me enter new issues and keeps directing me
> >to the query page isntead.
> >
> >Anyhow, here is the summary of the bug. Any help would be appreciated:
> >
> >==========================================================================
> >When selecting an entire extent from a parent class, the behavior varies
> >depending on whether you are using multi-table joins or distinct tables
> >for each class. Everything seems to work correctly if you use distinct
> >tables for each class. If you are using multi-table joins, however, your
> >objects are not always materialized as the class you would expect. Here
> >is the scenario for the test case I made:
> >
> >Object model
> >------------
> >- public interface InterfaceA
> >- public class A implements InterfaceA
> >- public class B extends A implements InterfaceA
> >
> >Test case 1
> >-----------
> >- start with a new JVM
> >- select all from extent InterfaceA.class, loop through and display the
> >class for each
> >- select all from extent A.class, loop through and display the class for
> >each
> >- select all from extent B.class, loop through and display the class for
> >each
> >
> >Test case 2
> >-----------
> >- restart with a new JVM
> >- select all from extent B.class, loop through and display the class for
> >each
> >- select all from extent A.class, loop through and display the class for
> >each
> >- select all from extent InterfaceA.class, loop through and display the
> >class for each
> >
> >Results
> >-------
> >- If using distinct tables for each class (see schema1.sql and
> >repository_user1.xml), both test cases materialize all objects as the
> >correct subclass and returns the expected instances with the appropriate
> >queries.
> >- If using multi-table joins (see schema2.sql and repository_user2.xml),
> >test case 1 displays everything as being class "A", even if it was a "B".
> >Instances of "B" are retrieved and displayed twice in the queries for
> >"InstanceA". Once you get to querying for "B", it turns up absolutely no
> >instances. I imagine this is because it is cached. Test case 2 correctly
> >materializes the objects a "B" and "A", depending on which table they
> >appear in; when you query against InterfaceA.class, however, it still
> >displays the duplicates for instances of "B".
> >
> >
> >For convenience in debugging, I have written a command-line test that lets
> >you choose the order in which the extents are retrieved and displayed.
> >You can run it by doing:
> > java polymorphtest.PolymorphTest 0 1 2 (test case 1)
> >OR
> > java polymorphtest.PolymorphTest 2 1 0 (test case 2)
> >
> >
> >All the related classes, schemas, and O/R mappings are attached to this
> >bug. Please contact me if you need help reproducing the errors.
> >
> >================================================================
> >
> >I don't want to send the attachments to the whole list, but I will be glad
> >to email the tarball to whomever requests it. Thanks in advance for your
> >help!
> >
> >Sincerely,
> >Justis Peters
> >Oculan Corp.
> >[EMAIL PROTECTED]
> >
> >---------------------------------------------------------------------
> >To unsubscribe, e-mail: [EMAIL PROTECTED]
> >For additional commands, e-mail: [EMAIL PROTECTED]
> >
> >
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [EMAIL PROTECTED]
> For additional commands, e-mail: [EMAIL PROTECTED]
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]