-->
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.  [ 19 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: unidirectional 1-1 relationship.
PostPosted: Wed Nov 24, 2004 8:38 pm 
Beginner
Beginner

Joined: Thu Oct 14, 2004 10:53 pm
Posts: 45
In a uni-directional one-to-one relationship, where or how can I specify the foreign key column that one object/table refers to.

e.g. I have a Person object and an Address object. Person knows about address, but address need not know about Person, so the relationship is unidirectional.

e.g.
public class Person {
private Address address
public Address getAddress() { return address; }
....

public class Address {
private int streetNumber;
......
......

In my mapping file, under Person.hbm.xml how can I specify the foreign key column for the Address table? Currently I have in Patient.hbm.xml

<one-to-one
name="address"
class="Address"
cascade="all">
</one-to-one>

There isn't any way to specify where to put your foreign key. So when I save Patient and Address, the foreign key column in the Address table is not populated. BTW, I have made the personFK in Address table nullable.

I can solve my problem if the relationship was bidirectional, and specified many-to-one in the Address.hbm.xml but really I don't a bi-directional relationship in this case.

Can anyone help?


[b]Hibernate version:2.1.6[/b]

[b]Mapping documents:
Person.hbm.xml
.....
<one-to-one
name="address"
class="Address"
cascade="all">
</one-to-one>
.....
[/b]


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 25, 2004 4:48 am 
Newbie

Joined: Wed Nov 24, 2004 10:15 am
Posts: 7
Hi,

I have been posting with exactly the same problem for the last day or two.
I hope someone can assist.

I have turned the logging up, and have found the following.
When you leave the not-null parameter off, or set it to false then two statements are executed, and an update is done afterwards to update the key value.

If you specify the not-null parameter, then the key is not populated to the children before the insert, and an update is not done, so the exception get's thrown.

I have bought the book, and have started reading, but have not yet found a solution. I will monitor this thread, and hopefully someone will come to our aid.

My relationship is also unidirectional,

I think we may find a solution by adding the bi-directional part of the relationship as a field access declaration. I don't know how to do this with Xdoclet though. If you use xdoclet you actually have to create a property for the reverse relationship which you and I clearly do not want.

You can see by the log that it actually does do the cascade, but the key values are not transferred correctly.

2004-11-25 10:40:00,520 DEBUG [net.sf.hibernate.impl.SessionFactoryObjectFactory] JNDI lookup: hibernate/HibernateFactory
2004-11-25 10:40:00,520 DEBUG [net.sf.hibernate.impl.SessionFactoryObjectFactory] lookup: uid=8ae496f7006edfbb01006ee2ea170001
2004-11-25 10:40:00,520 DEBUG [net.sf.hibernate.impl.SessionImpl] opened session
2004-11-25 10:40:00,581 DEBUG [net.sf.hibernate.impl.SessionImpl] saving [com.palantir.db.SHStakeholder#<null>]
2004-11-25 10:40:00,581 DEBUG [net.sf.hibernate.impl.SessionImpl] executing insertions
2004-11-25 10:40:00,581 DEBUG [net.sf.hibernate.engine.Cascades] processing cascades for: com.palantir.db.SHStakeholder
2004-11-25 10:40:00,581 DEBUG [net.sf.hibernate.engine.Cascades] cascading to saveOrUpdate()
2004-11-25 10:40:00,581 DEBUG [net.sf.hibernate.impl.SessionImpl] saveOrUpdate() unsaved instance
2004-11-25 10:40:00,681 DEBUG [net.sf.hibernate.impl.SessionImpl] generated identifier: 653502
2004-11-25 10:40:00,681 DEBUG [net.sf.hibernate.impl.SessionImpl] saving [com.palantir.db.SLContact#653502]
2004-11-25 10:40:00,681 DEBUG [net.sf.hibernate.engine.Cascades] done processing cascades for: com.palantir.db.SHStakeholder
2004-11-25 10:40:00,721 ERROR [STDERR] net.sf.hibernate.PropertyValueException: not-null property references a null or transient value: com.palantir.db.SHStakeholder.SLContact


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 25, 2004 4:51 am 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Move the FK to the Person table (ADDRESS_ID) and use a <many-to-one> in Person.hbm.xml.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 25, 2004 5:16 am 
Newbie

Joined: Wed Nov 24, 2004 10:15 am
Posts: 7
Is there no other way of doing this?
Changing the database design to accomodate hibernate doesn't seem like a valid option to me.

The person foreign key belongs on address, not the other way around. You are suggesting changing the nature of the relationship.

There must be a simple way of getting hibernate to manage a stock standard Person / Address relationship in a unidirectional fashion. It must surely be the most basic thing to do, yet I am also struggling with it.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 25, 2004 2:45 pm 
Beginner
Beginner

Joined: Thu Oct 14, 2004 10:53 pm
Posts: 45
hi craig,

yeah I am also baffled by this. I can do this if and only if a reference to Person is declared in Address, hence the bi-directional nature. However I do not want to do this. And I don't wish to change the database design to do this, e.g. put addressFK in person table, when I wish to put personFK in the address table. Can anyone help here?

The DTD for a <one-to-one> defines a foreign-key attribute. It seems to be the thing to do in this case but I have no idea how this can be used here.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 25, 2004 2:47 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
I don't think what you are trying to do is possible (please note that a "real" one-to-one would be a shared primary key, the trick with the UNIQUE FK is a hack anyway). The foreign-key attribute is used to name the foreign key constraint generated by SchemaExport.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 22, 2004 12:04 pm 
Beginner
Beginner

Joined: Tue Oct 19, 2004 11:04 am
Posts: 22
Judging by what you said earlier:

Quote:
Person knows about address, but address need not know about Person, so the relationship is unidirectional.


And

Quote:
The person foreign key belongs on address, not the other way around. You are suggesting changing the nature of the relationship.


It seems that these two statements are contradictory. If an Address need not know about a Person, then why does the address table have a foreign key to the person table? Your relationship states that Person has an address. To me, it makes sense that the person table should have the foreign key relationship to the address table. If it were a bi-directional relationship and you had a constraint where an address can only be had by a single person, then the foreign key on the address table makes sense.

Just my two cents.


Top
 Profile  
 
 Post subject: Is there a solution yet?
PostPosted: Thu Jan 13, 2005 5:35 pm 
Pro
Pro

Joined: Wed Nov 05, 2003 7:22 pm
Posts: 211
Hi,

I'm struggling with the same issue for days. In my mind, trained by relation sql background, the simplest conceivable relation is expressed by the person table PK being referenced from the adress table as a foreign key. Perhaps I'm thinking to relational.

I've tried several things. The best I've achieved is when person is saved an update is called on a address, leading to an error because it doesn't exist.

It just seems impossible to get it done in Hibernate, or at least: impossible to get it done logically or simply. I can't even find a solution in Hibernate in Action.

I have been able to get a proper insert on address by:
* creating a Person property/column (leading to an unwanted extra column in the database that is the exact same as the foreign key and an unwanted reference from address to person)
* creating a person and an address.Set the address property in Person to the created address
* manually setting the person in address to reference Person
* saving both

What I want is:
* a Person class with a reference to an Address class
* an Address class without a reference to the Person class, except for a derived foreign key personID property
* Create a person, create an address. Set the address property in Person to the created address
* Save person and have address persisted automatically

Could someone give the definitive full monty on this (including mappings)?

Kind regards,

Marc


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 13, 2005 5:46 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
<one-to-one name="address" cascade="all" property-ref="personId"/>

There is an example of this in "basic O/R mapping".


Top
 Profile  
 
 Post subject: What if person is a subclass of the class holding personID
PostPosted: Thu Jan 13, 2005 6:10 pm 
Pro
Pro

Joined: Wed Nov 05, 2003 7:22 pm
Posts: 211
In my case the Person class, is a sub-class of a parent which contains the actual ID property. I assume this is the reason I get:
net.sf.hibernate.MappingException: property-ref not found: personId in class: Person

Any suggestions?

Kind regards,

Marc


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 13, 2005 6:37 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
I have no idea, since you showed no mapping.

there must be a:

<property name="personId" unique="true"/>


Top
 Profile  
 
 Post subject: Mappings
PostPosted: Thu Jan 13, 2005 6:50 pm 
Pro
Pro

Joined: Wed Nov 05, 2003 7:22 pm
Posts: 211
Hi,

I'm going to step out of this conceptual person address thing and show you the actual mappings of the real world problem I have. I would seem to have the unique Id.

The idea is:
* User is parent of Subscriber
* One Profile is uniquely referenced by Subscriber

User.hbm.xml

<class
name="User"
table="Users"
discriminator-value="U"
>
<id
name="userID"
column="UserID"
type="long"
unsaved-value="null"
>
<generator class="native">
</generator>
</id>

<discriminator
column="Subclass"
type="char"
/>

<subclass
name="Subscriber"
discriminator-value="S"
>

<one-to-one
name="profile"
class="Profile"
cascade="all"
outer-join="false"
constrained="false"
/>


Profile.hbm.xml

<class
name="Profile"
table="Profiles"
proxy="Profile"
>

<id
name="subscriberID"
column="SubscriberID"
type="long"
>
<generator class="foreign">
<param name="property">subscriber</param>
</generator>
</id>

<one-to-one
name="subscriber"
class="Subscriber"
cascade="all"
outer-join="auto"
constrained="false"
property-ref="userID"
/>


Code

User user = new User();
Profile profile = new Profile();
profile.setSubscriber(user);
user.setProfile(profile);

tx = session.beginTransaction();
session.save(user);
tx.commit();


Thanks!

Marc


Top
 Profile  
 
 Post subject: Getting an update in stead of an insert
PostPosted: Sun Jan 16, 2005 11:03 am 
Pro
Pro

Joined: Wed Nov 05, 2003 7:22 pm
Posts: 211
Ok, I've changed it to the following and am getting an update on the profile in stead of an insert.

User.hbm.xml

<class
name="User"
table="Users"
discriminator-value="U"
>
<id
name="userID"
column="UserID"
type="long"
unsaved-value="null"
>
<generator class="native">
</generator>
</id>

<discriminator
column="Subclass"
type="char"
/>

<subclass
name="Subscriber"
discriminator-value="S"
>

<one-to-one
name="profile"
class="Profile"
cascade="all"
outer-join="auto"
constrained="false"
property-ref="subscriber"
/>


Profile.hbm.xml

<class
name="Profile"
table="Profiles"
proxy="Profile"
>

<id
name="subscriberID"
column="SubscriberID"
type="long"
unsaved-value="null"
>
<generator class="foreign">
<param name="property">subscriber</param>
</generator>
</id>

<many-to-one
name="subscriber"
class="Subscriber"
cascade="none"
outer-join="auto"
constrained="false"
unique="true"
not-null="false"
column="SubscriberID"
/>


Any help? This is driving me up the wall.

Kind regards,

Marc


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jan 16, 2005 12:52 pm 
CGLIB Developer
CGLIB Developer

Joined: Thu Aug 28, 2003 1:44 pm
Posts: 1217
Location: Vilnius, Lithuania
Classic way is transform conceptual 1-1 relationship to no phisical relationship, move all fields to the same table and map address as component. Declare all nullable fields last in table definition, this is workaround to save disk space if one of entities is "optional". Create Views if you need to have this concept in DB too.


Top
 Profile  
 
 Post subject: That's not what I want
PostPosted: Sun Jan 16, 2005 6:57 pm 
Pro
Pro

Joined: Wed Nov 05, 2003 7:22 pm
Posts: 211
I do want seperate tables. I don't want mega bloated tables with loads of columns. My profile class has about 15 properties.

Surely, what I want to do is doable. I feel like this is Hibernate 101, but somehow it seems incredibly hard to get it done. Anyway, the update in stead of insert seems to occur because the generated id on subscriber does not get assigned to the profile class.

My debug out put is as follows (edited for clarity purpose):
Code:
processing cascades for: Subscriber
done processing cascades for: Subscriber
id unsaved-value strategy NULL
id unsaved-value strategy NULL
Inserting entity: Subscriber (native id)
about to open: 0 open PreparedStatements, 0 open ResultSets
insert into Users (dateAdded, online, hits,Subclass) values (?, ?, ?,'S')
Hibernate: insert into Users (dateAdded, online, hits,Subclass) values (?, ?, ?, 'S')
preparing statement
Dehydrating entity: [Subscriber#<null>]
binding '2005-01-16 16:08:09' to parameter: 1
binding 'false' to parameter: 2
binding '0' to parameter: 3
Natively generated identity: 2
done closing: 0 open PreparedStatements, 0 open ResultSets
closing statement
processing cascades for: Subscriber
cascading to saveOrUpdate()
id unsaved-value strategy NULL
saveOrUpdate() previously saved instance with id: 0
updating [nl.msw.dates4free.business.entities.profile.Profile#0]
done processing cascades for: Subscriber
commit
flushing session
processing cascades for: Subscriber
cascading to saveOrUpdate()
saveOrUpdate() persistent instance
done processing cascades for: Subscriber
Flushing entities and processing referenced collections
Updating entity: [Profile#0]
Processing unreferenced collections
Scheduling collection removes/(re)creates/updates
Flushed: 0 insertions, 1 updates, 0 deletions to 2 objects
Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
listing entities:
Profile{eyeColor=null, religion=null, subscriberID=0, length=0}
Subscriber{dateAdded=2005-01-16 16:08:09, photos=null, userID=2, hits=0, profile=Profile#0}
executing flush
Updating entity: [Profile#0]
about to open: 0 open PreparedStatements, 0 open ResultSets
update Profiles set hit=?, kids=?, kidshome=?, length=?,subscriberID=? where SubscriberID=?
Hibernate: update Profiles set hit=?, kids=?, kidshome=?, length=?, subscriberID=? where SubscriberID=?
preparing statement
Dehydrating entity: [Profile#0]
binding '0' to parameter: 1
binding 'false' to parameter: 2
binding 'false' to parameter: 3
binding '0' to parameter: 4
binding '0' to parameter: 7
Adding to batch
Executing batch size: 1
done closing: 0 open PreparedStatements, 0 open ResultSets



Kind regards,

Marc[/code]


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 19 posts ]  Go to page 1, 2  Next

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.