-->
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.  [ 6 posts ] 
Author Message
 Post subject: how to associate objects directly to a joined-subclass obj
PostPosted: Tue Nov 20, 2007 1:16 pm 
Newbie

Joined: Tue Nov 20, 2007 12:15 pm
Posts: 10
Salute, fellow hibernators.

Synopsis:
class Acquisition subclasses class Evidence using the 'Table per subclass' strategy ('joined-subclass') using foreign key 'evidence_id'. However Acquisition has its own surrogate primary key 'acquisition_id' in addition to the foreign key evidence_id.

Is there a way of mapping these classes such that:
Code:
session.get( Evidence.class, an_evidence_id );

and
Code:
session.get( Acquisition.class, an_acquisition_id );

both work? That is, acquisitionId/acquisition_id to effectively be also considered an identifier property of Acquisition.

Secondly, and related to this mapping problem, there is the question of classes that refer to the Acquisition subclass -- how should these be mapped? Since the Scan class has only a acquisition_id foreign key to Acquisition, and not to Evidence directly. Since Acquisition is effectively the top-level object/table of a wholly separate schema/object hierachy, we'd greatly prefer being able to work with & pass around acquisition_ids rather than having to force everything back to evidence_ids.

The linked exception occurs upon Session.save( aScan ), due to the incorrect Acquisition mapping as described above.

Thanks for reading.
Matt Harrison

Specifics below.

---------------

Hibernate version:
Code:
hibernate-3.2.5.ga.jar


Mapping documents:

------------------ org/eurocarbdb/dataaccess/core/Evidence.hbm.xml ------------------
Code:
   
    <class name="org.eurocarbdb.dataaccess.core.Evidence" table="evidence" schema="core">
       
        <!-- surrogate primary key generated by db sequence -->
        <id name="evidenceId" type="int" access="field">
            <column name="evidence_id" />
         <generator class="sequence" >
            <param name="sequence">core.evidence_evidence_id_seq</param>
         </generator>
        </id>
     
        ...       

    </class>


------------------ org/eurocarbdb/dataaccess/ms/Acquisition.hbm.xml ------------------
Code:
   
    <!-- ms.Acquisition subclasses core.Evidence -->
    <joined-subclass name="org.eurocarbdb.dataaccess.ms.Acquisition"
                     extends="org.eurocarbdb.dataaccess.core.Evidence"
                     table="acquisition"
                     schema="ms"
    >
        <!-- joins evidence table on fkey evidence_id -->
        <key column="evidence_id" />
       
        <!-- this is surrogate primary key of acquisition table, defined as a regular property -->
        <property name="acquisitionId" type="int" insert="false" generated="insert" access="field">
            <column name="acquisition_id" />
        </property>

        <!-- an Acquisition has many Scans -->
        <set name="scans" inverse="true">
            <key>
                <column name="acquisition_id" not-null="true" />
            </key>
            <one-to-many class="org.eurocarbdb.dataaccess.ms.Scan" />
        </set>

        ...       

    </joined-subclass>


------------------ org/eurocarbdb/dataaccess/ms/Scan.hbm.xml ------------------
Code:
   
    <class name="org.eurocarbdb.dataaccess.ms.Scan" table="scan" schema="ms">
   
        <!-- surrogate primary key generated by db sequence -->
        <id name="scanId" type="int" access="field">
            <column name="scan_id" />
            <generator class="sequence">
                <param name="sequence">ms.scan_scan_id_seq</param>
            </generator>
        </id>
       
        <!-- an Acquisition has many Scans -->
        <many-to-one name="acquisition" class="org.eurocarbdb.dataaccess.ms.Acquisition" fetch="select">
            <column name="acquisition_id" not-null="true" />
        </many-to-one>
       
    ...

    </class>



Code between sessionFactory.openSession() and session.close():
Code:
Acquisition a = session.get( Evidence.class, an_evidence_id );
Scan s = new Scan();
s.setAcquisition( s );
...
session.save( s );


Full stack trace of any exception that occurs:
Code:
ERROR from org.hibernate.util.JDBCExceptionReporter.logExceptions(JDBCExceptionReporter.java:78)
ERROR: insert or update on table "scan" violates foreign key constraint "scan_acquisition_id_fkey"
Detail: Key (acquisition_id)=(7) is not present in table "acquisition".


Name and version of the database you are using:
Code:
Postgres 7.2


The generated SQL (show_sql=true):
Code:
   
/* insert org.eurocarbdb.dataaccess.ms.Scan */
insert into ms.scan (acquisition_id, peak_processing_id, parent_structure_id, ms_exponent, polarity, deisotoped, charge_deconvoluted, base_peak_mz, base_peak_intensity, start_mz, end_mz, low_mz, high_mz, contributor_quality, scan_id)
values (7, 1, NULL, 2, 1, 1, 1, 2604.09814453125, 185.88235473632812, -1.0, -1.0, 9.92466926574707, 2748.443603515625, 0.0, 2)


Debug level Hibernate log excerpt:
(not given)


Top
 Profile  
 
 Post subject:
PostPosted: Fri Nov 23, 2007 1:34 pm 
Newbie

Joined: Tue Nov 20, 2007 12:15 pm
Posts: 10
Any help would really be appreciated. Mapping objects to a subclass of another object seems like a common problem, however I've not seen or read any examples or discussions of how this should be mapped, since a standard mapping to the subclass does not work as expected.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Nov 24, 2007 2:27 pm 
Newbie

Joined: Tue Nov 20, 2007 12:15 pm
Posts: 10
problem remains... i emphasise that this boundary case is not covered by any of the existing documentation.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Nov 25, 2007 8:13 am 
Expert
Expert

Joined: Fri Jul 13, 2007 8:18 am
Posts: 370
Location: london
Quote:
However Acquisition has its own surrogate primary key 'acquisition_id' in addition to the foreign key evidence_id.


It sounds like you want Acquisition to have 2 primary keys. This is impossible. In your mappings evidence_id is the primary key of Acquisition. acquisition_id, as a second unique identifier generated by the db, seems utterly redundant.

You map the scans collection using the column name "acquisition_id". However, hibernate will populate this field with the primary key of the Acquisition i.e. the evidence_id, causing your key violation.

I draw the conclusion that you're mapping to a legacy schema. Is there any scope for making schema changes?


Top
 Profile  
 
 Post subject:
PostPosted: Sun Nov 25, 2007 2:31 pm 
Newbie

Joined: Tue Nov 20, 2007 12:15 pm
Posts: 10
thatmikewilliams wrote:
Quote:
However Acquisition has its own surrogate primary key 'acquisition_id' in addition to the foreign key evidence_id.


It sounds like you want Acquisition to have 2 primary keys. This is impossible. In your mappings evidence_id is the primary key of Acquisition. acquisition_id, as a second unique identifier generated by the db, seems utterly redundant.

You map the scans collection using the column name "acquisition_id". However, hibernate will populate this field with the primary key of the Acquisition i.e. the evidence_id, causing your key violation.

I draw the conclusion that you're mapping to a legacy schema. Is there any scope for making schema changes?


Thanks very much for your reply :-)

Indeed, we can make schema changes, but naturally we'd prefer not to. We have generally taken the approach of having a surrogate primary key on all tables, even join tables (since in the majority of cases we cannot exclude the possibility of adding columns at a later date). Having surrogate primary keys on all data tables at least seems like a normal & sensible practise. In our schema, acquisition_id is just a regular synthetic surrogate primary key, and evidence_id is the foreign key.

Perhaps I'm asking the question the wrong way around. Does hibernate require that the key used to join the subclass (ie: Acquisition) also be the primary key of the subclass' table? Because it seems like any not-null unique column that is a foreign key to the superclass' table (ie: Evidence) should suffice as the <key ...>?

The only drawback I can see is that other tables/classes that subclass Evidence could potentially refer to an evidence_id that is referenced by another subclass, but this possibility is easily checked for. Are there other compelling reasons why <key ...> must also effecitvely be the <id ...> of the subclass?

Currently, we can get the behaviour we're looking for by declaring acquisition_id as unique and using property-ref in all classes that are associated to Acquisition, I presume this is a Hibernate "hack"?

Thanks for reading.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Nov 25, 2007 3:33 pm 
Expert
Expert

Joined: Fri Jul 13, 2007 8:18 am
Posts: 370
Location: london
Quote:
In our schema, acquisition_id is just a regular synthetic surrogate primary key, and evidence_id is the foreign key.


Actually, when I ran your example code, evidence id is created as the primary key of the Acquisition table (using hibernate to generate schema). Evidence id in Acqusition is effectively a surrogate primary key (albeit aligned to the PK of a related table) so there's no need to add another.

Quote:
The only drawback I can see is that other tables/classes that subclass Evidence could potentially refer to an evidence_id that is referenced by another subclass


Two objects can't share the same base class instance in Java so they won't be able to share the same row in the evidence table (assuming data is inserted via hibernate or at least conforms to the mappings).

Quote:
Currently, we can get the behaviour we're looking for by declaring acquisition_id as unique and using property-ref in all classes that are associated to Acquisition, I presume this is a Hibernate "hack"?


A hack - no. property-ref is designed for such a situation where a relationship is defined by something other than the primary key. However, this is mainly for mapping to legacy schemas. If you control the schema its better to use the real primary key.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 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:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.