-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 
Author Message
 Post subject: Table-Per-Subclass and Extra ManyToOne Column
PostPosted: Mon Jul 19, 2004 12:49 pm 
Newbie

Joined: Fri Jul 02, 2004 12:07 pm
Posts: 5
Bit of a mystery here ... RTFM and even RTFB(!) hasn't cleared things up.

In essence:

1. We have a parent Context class with a set of child objects which are various kinds of jobs -- we'll just look at Timer jobs. The relationship is bidirectional, many-to-one on the child end.

Parent mapping of Context class has
Code:
  <set name="timers"  lazy="false" cascade="all" inverse="true" >
   <key column="PARENT_ID"/>
   <one-to-many class="job.Timer"/>
</set>   


2. The child job.Timer class implements the job interface. The documentation leads me to believe that you can treat an interface implementation exactly like an abstract superclass for purposes of polymorphic mapping.

Child mapping of the Job interface and Timer implementation has

Code:
<class name="Job"  table="JOB">
  <id column="JOB_ID" name="id" unsaved-value="0">
   <generator class="native"/>
  </id>

<!-- Child end of the association to the parent object-->     
<many-to-one name="parent" class="Context" column="parent_id" not-null="true" />

...

   <joined-subclass name="Timer" table="TIMER">
      <key column="TIMER_ID"></key>
   </joined-subclass>

...

As you see, the Job interface (and associated JOB table) holds the association with the parent Context, which is fine since all implementations of Job have a parent. All the examples seem to make it clear that the superclass (or interface?) holds the common properties.

Now cometh the mystery ...

If we do the indicated table structure:

Code:
TABLE "JOB"
  ("JOB_ID" NOT NULL,
   "PARENT_ID" NOT NULL
   );

and

TABLE "TIMER"
  ("TIMER_ID" NOT NULL
   );


we would expect all to go well.

4. All *does* go well for an initial instantiation and persistance.

5. But when you attempt to fetch back a Context with its associated TIMER objects, you see:
Code:
[7/19/04 11:19:13:082 EDT] 1c7463a0 JDBCException E net.sf.hibernate.util.JDBCExceptionReporter  ORA-00904: "TIMERS0_"."PARENT_ID": invalid identifier


Clearly we've generated a mapping that suggests to Hibernate that the subclass table TIMER has the PARENT_ID, whereas our intent was to keep it in the interface JOB table.

6. And, for grins, if we create a PARENT_ID column in the TIMER table, all goes well. Nothing is stored in the PARENT_ID of the TIMER table though; the real CONTEXT id is stored in PARENT_ID of JOB, just like we want it.

So ... where did I go wrong? How did the reference to TIMERS.PARENT_ID get generated?

Thanks for any help ... I've been over this many times, convinced it's something obvious. But not to me ...

===========================================
Hibernate 2.1.2
ORACLE 9.1


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 19, 2004 4:46 pm 
Newbie

Joined: Mon Jul 19, 2004 3:34 pm
Posts: 4
I believe the <key> element in your joined-subclass mapping should be the column which has a foreign key to the base class's table. In other words, TIMER needs a JOB_ID column, because you're using the table-per-subclass and each instance of the subclass needs an associated instance of the superclass.

Make sense? I'm a newbie too, so my answer is probably at best partial...


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 19, 2004 5:08 pm 
Newbie

Joined: Fri Jul 02, 2004 12:07 pm
Posts: 5
I wish it were so, but I think maybe not ...

My (clearly partial) understanding is that:

1. The <KEY> element in the joined subclass is TIMER_ID. TIMER_ID is a foreign key containing the value of the JOB table's JOB_ID, maintaining the Interface (or superclass) - to - subclass relationship. So I think we're covered there.

2. From what I can tell of the examples, the extension table that implements the subclass-specific properties -- TIMER -- doesn't need its own ID. It exists only as an extension to the JOB table...

Of course the <KEY> in the context is another matter entirely ... it implements the CONTEXT-TO-JOB (or TIMER implementation of JOB) relationship that's biting me you-know-where.

So from what I can see we have that covered...

But if not, can you explain how?

Thanks for replying!

R.



ekaun wrote:
I believe the <key> element in your joined-subclass mapping should be the column which has a foreign key to the base class's table. In other words, TIMER needs a JOB_ID column, because you're using the table-per-subclass and each instance of the subclass needs an associated instance of the superclass.

Make sense? I'm a newbie too, so my answer is probably at best partial...


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 19, 2004 5:55 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
TIMERS0_ is probably an alias of the JOB table. But we can't tell. 'Cos you didn't post the SQL.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 19, 2004 10:22 pm 
Newbie

Joined: Fri Jul 02, 2004 12:07 pm
Posts: 5
Well, I should'a done.

[7/19/04 11:19:12:598 EDT] 1c7463a0 SystemOut O Hibernate: select timers0_.WRKFL_TIMER_ID as WRKFL_TI1___, timers0_.PARENT_ID as PARENT_ID__, workflowtr1_.WRKFL_ID as XACTN_ID0_, workflowtr1_.legacyFAA_Id as legacyFA2_4_0_, workflowtr1_.busComment as busComment4_0_, workflowtr1_.creationTypeId as creation4_4_0_, workflowtr1_.from_prfl_id as from_prf5_4_0_, workflowtr1_.to_prfl_id as to_prfl_id4_0_, workflowtr1_.by_prfl_id as by_prfl_id4_0_, workflowtr1_.xactn_type as xactn_type4_0_, workflowtr1_.bsnss_actn_id as bsnss_ac9_4_0_, workflowtr1__1_.action as action1_0_, workflowtr1__1_.tdate as tdate1_0_, workflowtr1__1_.scope as scope1_0_, workflowtr1__1_.uiAlertID as uiAlertID1_0_, workflowtr2_.WRKFL_ID as XACTN_ID1_, workflowtr2_.legacyFAA_Id as legacyFA2_4_1_, workflowtr2_.busComment as busComment4_1_, workflowtr2_.creationTypeId as creation4_4_1_, workflowtr2_.from_prfl_id as from_prf5_4_1_, workflowtr2_.to_prfl_id as to_prfl_id4_1_, workflowtr2_.by_prfl_id as by_prfl_id4_1_, workflowtr2_.xactn_type as xactn_type4_1_, workflowtr2_.bsnss_actn_id as bsnss_ac9_4_1_, workflowtr2__1_.action as action1_1_, workflowtr2__1_.tdate as tdate1_1_, workflowtr2__1_.scope as scope1_1_, workflowtr2__1_.uiAlertID as uiAlertID1_1_, workflowco3_.wrkfl_cntxt_id as wrkfl_cn1_2_, workflowco3_.cntxt_prnt_id as cntxt_pr2_2_, workflowco3_.xactn_type as xactn_type2_, workflowco3_.crtn_type as crtn_type2_, workflowco3_.scope as scope2_, workflowco3_.ui_alert_id as ui_alert6_2_, workflowco3_.wrkfl_state as wrkfl_st7_2_, workflowco3_.from_prfl_id as from_prf8_2_, workflowco3_.to_prfl_id as to_prfl_id2_, workflowco3_.by_prfl_id as by_prfl_id2_, workflowco3_.bsnss_actn_rqrd_id as bsnss_a11_2_, workflowco3_.bsnss_actn_tx as bsnss_a12_2_, workflowco3_.bsnss_actn_taken_id as bsnss_a13_2_, workflowco3_.bsnss_actn_taken_tx as bsnss_a14_2_, workflowco3_.alert_rlse_dt as alert_r15_2_, workflowco3_.faf_crtn_dt as faf_crt16_2_, workflowco3_.init_dt as init_dt2_, workflowco3_.due_dt as due_dt2_, workflowco3_.read_dt as read_dt2_, workflowco3_.ac_asgnmt_dt as ac_asgn20_2_, workflowco3_.elgbl_cls_dt as elgbl_c21_2_, workflowco3_.cmplt_cls_dt as cmplt_c22_2_, workflowco3_.out_assign as out_assign2_, workflowco3_.desc_assign as desc_as24_2_, workflowco3_.faa_id as faa_id2_, workflowco4_.wrkfl_cntxt_id as wrkfl_cn1_3_, workflowco4_.cntxt_prnt_id as cntxt_pr2_3_, workflowco4_.xactn_type as xactn_type3_, workflowco4_.crtn_type as crtn_type3_, workflowco4_.scope as scope3_, workflowco4_.ui_alert_id as ui_alert6_3_, workflowco4_.wrkfl_state as wrkfl_st7_3_, workflowco4_.from_prfl_id as from_prf8_3_, workflowco4_.to_prfl_id as to_prfl_id3_, workflowco4_.by_prfl_id as by_prfl_id3_, workflowco4_.bsnss_actn_rqrd_id as bsnss_a11_3_, workflowco4_.bsnss_actn_tx as bsnss_a12_3_, workflowco4_.bsnss_actn_taken_id as bsnss_a13_3_, workflowco4_.bsnss_actn_taken_tx as bsnss_a14_3_, workflowco4_.alert_rlse_dt as alert_r15_3_, workflowco4_.faf_crtn_dt as faf_crt16_3_, workflowco4_.init_dt as init_dt3_, workflowco4_.due_dt as due_dt3_, workflowco4_.read_dt as read_dt3_, workflowco4_.ac_asgnmt_dt as ac_asgn20_3_, workflowco4_.elgbl_cls_dt as elgbl_c21_3_, workflowco4_.cmplt_cls_dt as cmplt_c22_3_, workflowco4_.out_assign as out_assign3_, workflowco4_.desc_assign as desc_as24_3_, workflowco4_.faa_id as faa_id3_, timers0_.WRKFL_TIMER_ID as MYAPP_J1_4_, timers0__1_.CREAT_XACTN_ID as CREAT_XA2_7_4_, timers0__1_.COMPL_XACTN_ID as COMPL_XA3_7_4_, timers0__1_.parent_id as parent_id7_4_ from WRKFL_TIMER timers0_ inner join MYAPP_JOB timers0__1_ on timers0_.WRKFL_TIMER_ID=timers0__1_.MYAPP_JOB_ID left outer join XACTN_WRKFL workflowtr1_ on timers0__1_.CREAT_XACTN_ID=workflowtr1_.WRKFL_ID left outer join XACTN workflowtr1__1_ on workflowtr1_.WRKFL_ID=workflowtr1__1_.XACTN_ID left outer join XACTN_WRKFL workflowtr2_ on timers0__1_.COMPL_XACTN_ID=workflowtr2_.WRKFL_ID left outer join XACTN workflowtr2__1_ on workflowtr2_.WRKFL_ID=workflowtr2__1_.XACTN_ID left outer join WRKFL_CNTXT workflowco3_ on timers0__1_.parent_id=workflowco3_.wrkfl_cntxt_id left outer join WRKFL_CNTXT workflowco4_ on workflowco3_.cntxt_prnt_id=workflowco4_.wrkfl_cntxt_id where timers0_.PARENT_ID=?

[7/19/04 11:19:13:066 EDT] 1c7463a0 JDBCException W net.sf.hibernate.util.JDBCExceptionReporter SQL Error: 904, SQLState: 42000

[7/19/04 11:19:13:082 EDT] 1c7463a0 JDBCException E net.sf.hibernate.util.JDBCExceptionReporter ORA-00904: "TIMERS0_"."PARENT_ID": invalid identifier

gavin wrote:
TIMERS0_ is probably an alias of the JOB table. But we can't tell. 'Cos you didn't post the SQL.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 19, 2004 10:32 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Quote:
from WRKFL_TIMER timers0_


Um. So timers0_ is actually an alias of some other random table that you didn't even show us the mapping for?

I think you need to spend some hours trying to resolve your problem using the Hibernate log, and your debugger, before posting here again.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 20, 2004 6:41 am 
Newbie

Joined: Fri Jul 02, 2004 12:07 pm
Posts: 5
No, actually I'd simplified the table names for the original post, but was reluctant to do so with complex sql. But thanks for the help.

gavin wrote:
Quote:
from WRKFL_TIMER timers0_


Um. So timers0_ is actually an alias of some other random table that you didn't even show us the mapping for?

I think you need to spend some hours trying to resolve your problem using the Hibernate log, and your debugger, before posting here again.


Top
 Profile  
 
 Post subject: Re: Table-Per-Subclass and Extra ManyToOne Column
PostPosted: Tue Jul 20, 2004 8:32 pm 
Newbie

Joined: Fri Jul 02, 2004 12:07 pm
Posts: 5
Problem solved. Many thanks to all who helped.

The essence is that I confused which end of the stick was which (no jokes about bums and holes in the ground). In looking at:

Code:
Wrong mapping
----------------------------------------
<set name="timers"  lazy="false" cascade="all" inverse="true" >
   <key column="PARENT_ID"/>
   <one-to-many class="job.Timer"/>
</set>   


I was simultaneously addressing the CONTEXT end of the stick with the set, and the JOB end of the stick with the PARENT_ID column.

A set wants to know which column of the serialized child class to store in its own space, not which column of the child to store itself in!

So poor old CONTEXT was looking to the TIMER's parent_id with an eye to storing PARENT_ID in its own CONTEXT table, and found no such column.

A corrected mapping is:

Code:
Corrected mapping, but from memory and at home
----------------------------------------------------------
<set name="timers"  lazy="false" cascade="all" inverse="true"  table="TIMERS">
   <key column="TIMER_ID"/>
   <one-to-many class="job.Timer"/>
</set>   


One more word on the confusion that made this simple mapping so difficult, at least for me. The documentation isn't entirely clear on the conceptual underpinnings of the subclass-to-table polymorphic mapping strategy. I was continually pulled between trying to "grasp" the polymorphism from the superclass/interface end (JOB) and the subclass end (TIMER).

The answer appears to be unequivocal and, once you think of it, obvious. Gavin, I'm sure you'll gently correct me if I go astray.

The people who wrote Hibernate are object oriented programmers. They looked at this problem from an OO perspective. In Java, for example, you deal directly with a subclass and implicitly with its superclass -- not the other way round.

So it is with Hibernate. In dealing with a polymorphic set of tables, you deal directly with the subclass table (TIMER in our example). Hibernate will extend any TIMER-focused operations to the superclass JOB table if and as needed. As usual, doing too much on the programming end is the cardinal sin. Do cardinals sin much?

Anyway, problem solved. Thanks to all.


[quote="robinpc"]Bit of a mystery here ... RTFM and even RTFB(!) hasn't cleared things up.

In essence:

1. We have a parent Context class with a set of child objects which are various kinds of jobs -- we'll just look at Timer jobs. The relationship is bidirectional, many-to-one on the child end.

Parent mapping of Context class has
Code:
  <set name="timers"  lazy="false" cascade="all" inverse="true" >
   <key column="PARENT_ID"/>
   <one-to-many class="job.Timer"/>
</set>   


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
cron
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.