-->
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.  [ 7 posts ] 
Author Message
 Post subject: composite-id and key-many-to-one
PostPosted: Mon Nov 10, 2003 9:59 am 
Newbie

Joined: Mon Nov 10, 2003 9:07 am
Posts: 5
Hi,

First, I'm a somewhat experienced OJB user, attempting to learn Hibernate for evaluation purposes. As such, I am starting with a data model that works with OJB already, and am attempting to get similar functionality within Hibernate (using the DAO pattern).

My problem seems to be with composite id's. I've searched the documentation numerous times, and also searched these forums, but haven't found anything that specifically relates to my problem (although I could have missed something). I'm unable to get an "anonymous" <composite-id> relationship using <key-many-to-one> with bi-directional references between two mappings.

I'm trying to map an Address to Country and State, where State has a Country and Country contains States. Whether or not this is a good model is not debatable (altho it's obviously not ;) )

Even when I remove the <bag> of states from Country (and thus break the bi-directional mapping), the external reference from Address still fails (even though I override .equals(Object obj)) with this error:
Code:
ObjectNotFoundException: No row with the given identifier exists: test.common.data.model.State@6567, of class: test.common.data.model.State


Here is a snippet my hibernate mapping XML file:

Code:
    <!--+
        | Address
        +-->
    <class name="test.common.data.model.Address" schema="TEST" table="ADDRESS">
        <id column="ADDRESS_ID" name="addressId" type="integer">
            <generator class="sequence"/>
        </id>
        <property column="STREET_1" length="100" name="street1" type="string"/>
        <property column="CITY" length="100" name="city" type="string"/>
        <many-to-one name="state" class="test.common.data.model.State">
            <column name="COUNTRY_CODE"/>
            <column name="STATE_CODE"/>
        </many-to-one>
        <many-to-one name="country" class="test.common.data.model.Country" column="COUNTRY_CODE" insert="false" update="false" /> <!-- insert/update false is required because COUNTRY_CODE defined in State above -->
        <property column="POSTAL_CODE" length="10" name="postalCode" type="string"/>
    </class>
    <!--+
        | Country
        +-->
    <class name="test.common.data.model.Country" schema="TEST" table="COUNTRY">
        <id column="COUNTRY_CODE" length="2" name="countryCode" type="string"> <!-- ISO-3166 code -->
            <generator class="assigned"/>
        </id>
        <property column="DESCRIPTION" length="200" name="name" type="string"/>
        <bag name="states" table="STATE" lazy="true">
            <key>
                <column name="COUNTRY_CODE"/>
                <column name="STATE_CODE"/>
            </key>
            <one-to-many class="test.common.data.model.State"/>
        </bag>
    </class>
    <!--+
        | State
        +-->
    <class name="test.common.data.model.State" schema="TEST" table="STATE">
        <composite-id>
            <key-many-to-one name="country" class="test.common.data.model.Country" column="COUNTRY_CODE"/>
            <key-property name="stateCode" column="STATE_CODE"/> <!-- ISO-3166-2 code -->
        </composite-id>
        <property column="DESCRIPTION" length="100" name="name" type="string"/>
    </class>


And here are the associated tables:
Code:
create table ADDRESS
(
  ADDRESS_ID   NUMBER(10) not null,
  STREET_1     VARCHAR2(100),
  CITY         VARCHAR2(100),
  STATE_CODE   CHAR(4),
  COUNTRY_CODE CHAR(2),
  POSTAL_CODE  VARCHAR2(10)
);
alter table ADDRESS
  add constraint PK_ADDRESS primary key (ADDRESS_ID);
alter table ADDRESS
  add constraint ADDRESS_FK_1 foreign key (COUNTRY_CODE);
alter table ADDRESS
  add constraint ADDRESS_FK_2 foreign key (COUNTRY_CODE,STATE_CODE) 

create table COUNTRY
(
  COUNTRY_CODE CHAR(2) not null,
  DESCRIPTION  VARCHAR2(200)
);
alter table COUNTRY
  add constraint COUNTRY_PK primary key (COUNTRY_CODE);
 
create table STATE
(
  COUNTRY_CODE CHAR(2) not null,
  STATE_CODE   CHAR(4) not null,
  DESCRIPTION  VARCHAR2(100)
);
alter table STATE
  add constraint PK_STATE primary key (COUNTRY_CODE,STATE_CODE);


It's relatively easy to map this in OJB using <reference-descriptor> and <inverse-foreign-key>. But I'm struggling mightily with the correct Hibernate syntax.

My java code is POJO's with setters and getters, with the exception of State, which implements Serializable and overrides .equals(Object obj) and hashcode(). Unfortunately, overriding .hashcode() breaks OJB. I can post this code if necessary.

Is it even possible to correctly model this relationship this way in hibernate? I'd rather not introduce a new class to represent the PK of STATE (as this could have serious OJB implications), but I realize I may have to. Also, should I look into <compsite-element> for help? That's the one area I haven't explored thoroughly. And finally, what explicitly does overriding .equals(Object obj) and .hashcode() do in Hibernate? For the caching mechanisms? Or something more "important"?

Thanks in advance for your help! And let me know if I need to post anything else.

Oh, and one more thing before I forget it -- if possible, I would suggest explicitly checking for "Serializable" in net.sf.hibernate.loader.Loader.getKeyFromResultSet(Loader.java:286) and throwing a more meaningful exception than "ClassCastException". I know the docs state that you must implement Serializable, but there is very little example code for "anonymous" <composite-id> usage, and as such, I missed it the first time. Just my 2 cents.

_________________
Phil Barnes


Top
 Profile  
 
 Post subject: composit-id
PostPosted: Tue Nov 11, 2003 11:46 am 
Newbie

Joined: Mon Nov 10, 2003 9:07 am
Posts: 5
Ok, let me ask this a different way to see if I can at least illicit some feedback...

Let's say I can change the database tables... what would be a better approach? To put surrogate keys on both the COUNTRY and STATE tables and reference those tables from ADDRESS with those keys? I understand the importance of surrogate keys, but in this case, ISO guarantees that the COUNTRY_CODE and COUNTRY_CODE/STATE_CODE combination will always be unique -- so why would one "force" duplicate unique keys (other than db efficiency)?

Thanks in advance for your input?

_________________
Phil Barnes


Top
 Profile  
 
 Post subject:
PostPosted: Tue Nov 11, 2003 11:55 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
I don't think anyone here understands your problem.

Apart from talking a lot about OJB, which you may as well assume that we don't know much about, you havn't really stated what the problem is.

<composite-id>s are very easy to use in Hibernate (except for unsaved-value not being workable). There are examples in the .test package.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Nov 11, 2003 12:08 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Helpfully, you can also use this example:


http://forum.hibernate.org/viewtopic.ph ... 89#2177689


Top
 Profile  
 
 Post subject: composite-id
PostPosted: Tue Nov 11, 2003 12:28 pm 
Newbie

Joined: Mon Nov 10, 2003 9:07 am
Posts: 5
Sorry about that. I was afraid my message would be interpreted as a "OJB can do this, Hibernate can't" message, which it is definitely not. I'm just very familiar with it, and was hopeful that someone on the Hibernate team might be remotely familiar in order to draw a correlation. I apologize.

I guess what I'm trying to ask, is; Can I model something like this in Hibernate using composite keys for COUNTRY and STATE?

Address ---1-------1---> State
Address ---1-------1---> Country

Country ---1------n----> State

State ---1------1-----> Country

Or in other words, I want an Address to have a Country and a State, and Country has a reference to its states (lazy loaded) and likewise a State has a reference to its Country.

It's likely I'm not explaining this very well at all (I have a habit of doing that ;) ).

If I just try to model the country and state relationship like below, Hibernate complains about the "states bag" since I'm not passing the right keys to create a "State". If I add countryCode, then I can't create the lazy loaded Country object on the State.

It's almost as if I need a way to specify in the bag's key area <column-many-to-one class="test.common.data.model.Country" name="country"/>...

Code:
    <!-- Country -->
    <class name="test.common.data.model.Country" schema="TEST" table="COUNTRY">
        <id column="COUNTRY_CODE" length="2" name="countryCode" type="string"> <!-- ISO-3166 code -->
            <generator class="assigned"/>
        </id>
        <property column="DESCRIPTION" length="200" name="name" type="string"/>
        <bag name="states" table="STATE" lazy="true">
            <key>
                <column name="COUNTRY_CODE"/>
                <column name="STATE_CODE"/>
            </key>
            <one-to-many class="test.common.data.model.State"/>
        </bag>
    </class>
    <!-- State -->
    <class name="test.common.data.model.State" schema="TEST" table="STATE">
        <composite-id>
            <key-many-to-one name="country" class="test.common.data.model.Country" column="COUNTRY_CODE"/>
            <key-property name="stateCode" column="STATE_CODE"/> <!-- ISO-3166-2 code -->
        </composite-id>
        <property column="DESCRIPTION" length="100" name="name" type="string"/>
    </class>


Please let me know if I can add more information!

Thanks again!

_________________
Phil Barnes


Top
 Profile  
 
 Post subject:
PostPosted: Tue Nov 11, 2003 12:43 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Is this what you want:

Code:
    <!-- Country -->
    <class name="test.common.data.model.Country" schema="TEST" table="COUNTRY">
        <id column="COUNTRY_CODE" length="2" name="countryCode" type="string"> <!-- ISO-3166 code -->
            <generator class="assigned"/>
        </id>
        <property column="DESCRIPTION" length="200" name="name" type="string"/>
        <bag name="states" table="STATE" lazy="true">
            <key>
                <column name="COUNTRY_CODE"/>
            </key>
            <one-to-many class="test.common.data.model.State"/>
        </bag>
    </class>
    <!-- State -->
    <class name="test.common.data.model.State" schema="TEST" table="STATE">
        <composite-id>
            <key-many-to-one name="country" class="test.common.data.model.Country" column="COUNTRY_CODE"/>
            <key-property name="stateCode" column="STATE_CODE"/> <!-- ISO-3166-2 code -->
        </composite-id>
        <property column="DESCRIPTION" length="100" name="name" type="string"/>
    </class>


Top
 Profile  
 
 Post subject: composite-id
PostPosted: Tue Nov 11, 2003 1:19 pm 
Newbie

Joined: Mon Nov 10, 2003 9:07 am
Posts: 5
Thanks Gavin!

That gets me halfway there. I guess I should have thought to try that. Thinking logically, I guess it makes sense -- COUNTRY_CODE "referencing" the same column on the STATE table that that creates a Country object for that column would in turn "automatically create" or reference a Country object. Did I miss somewhere in the docs or an example that mentions this?

Now, one last piece and I'm all set... and as I was typing this message, I think I've discovered a data constraint violation that resolves my last issue (a null STATE_CODE -- gotta love "converted" data without constraints ;) ).

Thanks again!

_________________
Phil Barnes


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 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.