-->
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.  [ 23 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Does one-to-one with foreign generator require bidirectional
PostPosted: Tue Sep 30, 2003 9:39 am 
Newbie

Joined: Tue Sep 30, 2003 9:27 am
Posts: 12
Hi,

I established a one-to-one mapping between a JHouse and a JRoof.

As I wanted the id of the JHouse to be automatically generated, and the id of the JRoof to take on the value of the JHouse, I followed the FAQ which said I need something like this for JRoof:

Code:
   
      <id name="id"  column="id" type="java.lang.Long"
            unsaved-value="null" >
            <generator class="foreign">
                  <param name="property">parent</param>
            </generator>
        </id>


This isn't really documented in the documentation though. When I ran it the first time it said something about unmapped property "parent", so I went and modified my JRoof class to have a
Code:
        <one-to-one
            name="parent"
            class="org.appfuse.persistence.JHouse"
            cascade="none"
            outer-join="auto"
            constrained="false"
        />


So now my JHouse and my JRoof have a one-to-one mapping (one in the JHouse for "getRoof/setRoof" which is what I want, and one in JRoof for "getParent/setParent" which I don't really want.

This all works, and the correct tables seem to be updated, however my code looks like quite ugly in the sense that I have to do roof.setParent() and house.setRoof(). Is this correct:

Code:
      JHouse jh = new JHouse();
      jh.setAddress("Address1");
                JRoof jr = new JRoof();
                jr.setRoofType("Slate");

                jr.setParent(jh);
                jh.setJRoof(jr);

            session.save(jh);


So my question is: Am I doing it right? If so, it may be worth documenting the fact that you will need to make the one-to-one bidirectional, and have the extra sets, if you want your id to take on the value of the parent.

[I don't really understand what I would need to set the parent - afterall the id's are shared, and so it should be able to figure it out?]

Thanks
J


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 30, 2003 10:21 am 
Senior
Senior

Joined: Tue Sep 23, 2003 8:18 am
Posts: 137
Location: Johannesburg, South Africa
Hey Jeff, here's how I do it, and it works.

This first bit is my "child" mapping - note the foreign generator.
Code:
<class name="MatterImpl" table="Matter">
    <id name="XMLID" column="XMLID" unsaved-value="0">
        <generator class="foreign"/>
    </id>
    <one-to-one name="XML" class="XMLImpl" cascade="all"/>
...
</class>


This second bit, is my "parent" mapping.

Code:
<class name="XMLImpl" table="XML">
    <id name="XMLID" column="XMLID" unsaved-value="0">
        <generator class="identity"/>
    </id>
    <one-to-one name="Matter" class="MatterImpl"/>
...
</class>


Hope this helps.

It's probably not perfect, but for me, it works perfectly as every single table in my database has that XMLID as either FK or PK, due to the ancient design of the DTD that I have to translate. :P In fact...because of that design, I have to have PK (1-1) PK (1-1) PK (1-M) FK in one area... :/
Although, it does speed up things nicely in some queries. :)

-G


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 30, 2003 10:48 am 
Newbie

Joined: Tue Sep 30, 2003 9:27 am
Posts: 12
Hi Brannor,

You have just described exactly what I described.
ie. I have a parent with a one-to-one, and a dependent with a one-to-one as well (ie. you have made it bidirectional)

I guess this also forces you, in your insert code, to do something like this:

Code:
matter.setXML(xml);
xml.setMatter(matter);


ie. do you have to do two "set's" for it to work?

Regards,
Jeff


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 30, 2003 10:49 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Of course!

Why would you not?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 30, 2003 11:15 am 
Newbie

Joined: Tue Sep 30, 2003 9:27 am
Posts: 12
OK, so it seems that if you don't want auto-generated ID's, then you aren't required to have your one-to-one relationships bidirectional. If you do want auto-generated ID's, then you are required to have bidirectional relationships. It is the asymmetry that disturbs me - choosing a particular ID generation scheme forces me to use bidirection instead of single direction.

"Why would you not?"

Well, because my architecture doesn't require the Roof to be able to find the House, and so I don't want to force an additional "setHouse" method on my Roof class just because of the way I generate the ID's.

I would have thought that under the correct conditions (a roof must have a house, and only one house, and vice versa), that I wouldn't have to call "roof.setParent(house)" because when Hibernate persists everything it has enough information to be able to figure out the roof's parent (house is pointing to it, I do session.save(house), and so it should be able to carry the correct ID accross to the roof.

Of course, I am a novice so there may be a flaw in my thinking....

Regards,
Jeff


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 30, 2003 11:24 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
This kind of thinking is common in people coming from CMP. The difference with Hibernate is that we support detached objects. ie. POJOs are not assumed to be always under the control of the container. So Hibernate associations are not "managed"! All associations are inherently unidirectional. It is a different model, and sometimes you need to add properties where you might prefer not to. We are basically limited by the JVM here.

But I still don't understand. Why not just define the <foreign> generation strategy on the other end of the association?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 30, 2003 11:26 am 
Senior
Senior

Joined: Tue Sep 23, 2003 8:18 am
Posts: 137
Location: Johannesburg, South Africa
Firstly...ignore Gavin. :D I think he sometimes forgets that this is the BEGINNERS forum and sometimes comes across a little aggro.

<cowers from Gavin>

Secondly. I somewhat disagree with Gavin. (a first)
I never once use child.setParent() anywhere in my code for 1-to-1's.
I only ever set the parent in my (1-to-M) collections (as I iterate them), never in my 1-to-1's.

At first I was thinking that Gavin was right completely right, as I use JAXB to unmarshal an XML file, and that creates the structure for me. However, I have recently started generating XML files, and I checked there, I have this:

Code:
  outXML.setMatter(matter);

And nowhere do I do a
Code:
  matter.setXML(outXML);

I simply create a new instance of Matter, and do sets of all the normal properties, then assign it to the XML.

That said, I found that for all relationships, when marshalling with JAXB (I posted this elsewhere), it is a good idea to do:
Code:
matter.setXML(null);

to break an infinite loop when writing to file.

Anyway, in the end, for 1-to-1's, although you don't actually have to set the child.setParent() when processing, you actually do, as Gavin kind of said, have to have the properties in both objects for Hibernate to work properly. :)

Summary:
--------------------------------------------------------------------------------------
Yes, you do need the get/set in both, and yes it can look messy, but if you code properly, you yourself only have to actually do one of the sets. It's all part of the the whole O/R Mapping world.
--------------------------------------------------------------------------------------

-G


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 30, 2003 11:36 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Quote:
Firstly...ignore Gavin. :D I think he sometimes forgets that this is the BEGINNERS forum and sometimes comes across a little aggro.


Heh, don't mind me - I'm a rude bastard when I'm short on coca cola...

I've just spent 5 hours pair-programming the damn book. With no coke. Grumpy. Grrr....


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 30, 2003 11:45 am 
Newbie

Joined: Tue Sep 30, 2003 9:27 am
Posts: 12
OK then, I guess I'm doing the right thing. Thanks for the pointers. I have to add the bidirection, and don't mind doing it.

However, note the following:
a) Gavin you said:
Quote:
But I still don't understand. Why not just define the <foreign> generation strategy on the other end of the association?

I hope it's just cos you are sleepy and that I haven't misunderstood, but if you look at the first piece of code in my first post, you will see that this is exactly what I did.

b) Brannor, you said you don't have to do set's on both sides.
I can under no circumstances get it to work if I don't do sets on both
sides, which seems to correspond to what Gavin is saying. So I'm not sure what magic you are doing :)

Regards,
J


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 30, 2003 11:49 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Quote:
I hope it's just cos you are sleepy and that I haven't misunderstood, but if you look at the first piece of code in my first post, you will see that this is exactly what I did.


I think you misunderstood. The "foreign" generator must be on a side of the relationship which has a "parent" property. So if you have a unidirectional association, you have two choices

(1) make it bidirectional
(2) swap the id generation strategies


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 30, 2003 1:03 pm 
Newbie

Joined: Tue Sep 30, 2003 9:27 am
Posts: 12
Thank you both. The last suggestion by Gavin was the one that did it. I now have no bidirection, and autogenerated id's. The roof gets its ID from "native" and the house from "foreign".

Note that the FAQ also gets it the wrong way around. According to Gavin's last suggestion (which works), the "parent" has to have the "foreign". The FAQ says:

Code:
<generator class="foreign">
     <param name="property">parent</param>
</generator>


which is just plain confusing because the property has to be "child" (in my case Roof) (cos you put the foreign in the parent).

(Brannor, it also correlates with what you said, except your example needs to swap the labels of "child" and "parent".).

Thanks again - now to find out how to make XDoclet include the "<param name="property">JRoof</param>"....


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 01, 2003 1:42 am 
Senior
Senior

Joined: Tue Sep 23, 2003 8:18 am
Posts: 137
Location: Johannesburg, South Africa
Hey Jeff, if you see my short section of mapping, you'll see my parent uses the "Identity" generator, which is auto-assigned by MS SQL if you set your primary key field to "Identity". :)

Also, my parent is definately XML, and child is Matter. And I only ever do a matter.setXML(xml);.

Glad you've got yours working. Mine is...a rather unique case...as I said.

I get sent an XML file that was written to a DTD using <field name ="">VALUE</field>. Not only that, but thanks to multiple vendor software packages, they decided to order this randomly. AND, there are 1-M relationships in there too... which meant I had to use the <xsd:all> tags, and create bridging 1-1 tables JUST so I can have 1-M's in my JAXB...

Anyway...let me not get started.

<tosses Gavin a coke and wanders off.>

-G


Top
 Profile  
 
 Post subject:
PostPosted: Thu Oct 02, 2003 2:56 am 
Senior
Senior

Joined: Tue Sep 23, 2003 8:18 am
Posts: 137
Location: Johannesburg, South Africa
Hey, it's me again...

Well, after I started the next bit of my development...generating my own structures and then some interesting things started happening...

In the end, I have found that Jeff was correct. I had to move my cascade - the reason it was working before, was that there were always children of the child, and their cascade worked upwards and saved the child...

I modified my foreign (child) table's generator to include the param tag. But, when everything is nicely setup, I get the following error:
Code:
net.sf.hibernate.MappingException: No persister for: java.lang.Long

when I do this:
Code:
session.saveOrUpdate(xml);

I make sure that the TransactionGroup is set in the XML and vice versa.

Here are the important bits of my mappings.
Code:
<class name="cdl.impl.XMLImpl" table="XML">
    <id name="XMLID" column="XMLID" type ="long" unsaved-value="0">
        <generator class="identity"/>
    </id>
    <one-to-one name="TransactionGroup" class="cdl.impl.TransactionGroupImpl" cascade="all"/>

and,
Code:
<class name="cdl.impl.TransactionGroupImpl" table="TransactionGroup">
    <id name="XMLID" column="XMLID" type ="long" unsaved-value="0">
        <generator class="foreign">
            <param name="property">XMLID</param>
        </generator>
    </id>
    <one-to-one name="XMLID" class="cdl.impl.XMLImpl" constrained="true"/>


Is there something wrong in my Mappings? I'm guessing at the fact that it has something to do with my primary keys being "long", and Hibernate wanting to save new TransactionGroups with a java.lang.Long id?

<places a coke on the Altar-of-Gavin and cowers in the corner.>

:)

-G


Top
 Profile  
 
 Post subject:
PostPosted: Thu Oct 02, 2003 4:47 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Show me the whole stack trace. This exception can crop up in some funny situations. (It can be quite misleading.)


Top
 Profile  
 
 Post subject:
PostPosted: Thu Oct 02, 2003 6:44 am 
Senior
Senior

Joined: Tue Sep 23, 2003 8:18 am
Posts: 137
Location: Johannesburg, South Africa
Code:
net.sf.hibernate.MappingException: No persister for: java.lang.Long
        at net.sf.hibernate.impl.SessionFactoryImpl.getPersister(SessionFactoryImpl.java:426)
        at net.sf.hibernate.impl.SessionImpl.getPersister(SessionImpl.java:2248)
        at net.sf.hibernate.impl.SessionImpl.getPersister(SessionImpl.java:2255)
        at net.sf.hibernate.impl.SessionImpl.save(SessionImpl.java:601)
        at net.sf.hibernate.id.ForeignGenerator.generate(ForeignGenerator.java:35)
        at net.sf.hibernate.impl.SessionImpl.save(SessionImpl.java:601)
        at net.sf.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:1187)
        at net.sf.hibernate.engine.Cascades$4.cascade(Cascades.java:77)
        at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:234)
        at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:302)
        at net.sf.hibernate.impl.SessionImpl.doSave(SessionImpl.java:741)
        at net.sf.hibernate.impl.SessionImpl.save(SessionImpl.java:607)
        at net.sf.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:1187)
        at XSDMain.doTransactions(XSDMain.java:119)
        at XSDMain.saveXML(XSDMain.java:47)
        at Descrepancy.processDescrepancy(Descrepancy.java:681)
        at XSDMain.xsd(XSDMain.java:1179)
        at XSDMain.xsd(XSDMain.java:1148)
        at Transform.xmlTransform(Transform.java:42)
        at ProcessXML.storeXML(ProcessXML.java:65)
        at ProcessXML.main(ProcessXML.java:97)

At present I have to use the type="long" as I'm using JAXB to generate my classes and it doesn't naturally support java.lang.Long types. But, I'm fiddling away with creating an xsd:Long to see if that will make it work. :)

-G


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