-->
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.  [ 13 posts ] 
Author Message
 Post subject: Automatically saving parent-child maps?
PostPosted: Wed Mar 10, 2004 7:25 am 
Newbie

Joined: Mon Feb 09, 2004 5:49 am
Posts: 17
Hi there, I've been playing around with Hibernate just recently and have been dabbling in the common parent-child capabilities that Hibernate offers.

I've run through the Set examples successfully and have looked through the "Parent-Child" documentation and the "Inside inverse=true" examples, which have both been very useful!

I'm hoping to use Maps, however, and there aren't as many examples on it, unfortunately.

In "Set," the parent/child relationship is automatically saved when you cascade and do a parent.addChild(child), which is essentially the same as parent.getChildren().add(child). parent.getChildren() returns a Set in this case. Later, you save the parent and voila, the parent and children are saved appropriately.

I'd really like to have this same feature using Maps, however I'm running into a problem. My Map is a collection of key (the child's ID) and value (the child object). I can't do parent.addChild(child) as was done in the Set example because I'd have to do parent.getChildren().put(childID, child), and since this is a new child and hence not in the DB yet, I wouldn't have a childID.

I've read some other posts regarding Lists and such, but I'm afraid I may be getting confused. I'd appreciate your suggestions on if this auto-save-children via saving the parent can work using Maps.

Here are my files. I have a User, and a User can own a bunch of Topics. This is represented by a User class and a Topic class. The User class has a HashMap inside with key/values of (TopicID, Topic Object).


USER:

<hibernate-mapping package="com.xings.common">
<class name="com.xings.common.User" table="User">

<id name="_ID" column="u_id" type="long" unsaved-value="null">
<generator class="native"/>
</id>
<property name="_firstName" column="u_firstName" type="string"
length="25" not-null="false"/>
<property name="_lastName" column="u_lastName" type="string"
length="25" not-null="false"/>

<property name="_username" column="u_username" type="string"
length="25" not-null="true">
<meta attribute="finder-method">findByName</meta>
</property>

<property name="_dateAdded" column="u_dateAdded" type="date"
not-null="true"/>

<property name="_dateUpdated" column="u_dateUpdated" type="date"
not-null="true"/>

<!--
<set name="_topics" inverse="true" lazy="true" cascade="all-delete-orphan">
<key column="u_id"/>
<one-to-many class="com.xings.common.Topic"/>
</set>
-->
<map name="_topics" inverse="true" lazy="true" cascade="all-delete-orphan">
<key column="u_id"/>
<index column="t_id" type="long"/>
<one-to-many class="com.xings.common.Topic"/>
</map>
</class>
</hibernate-mapping>


TOPIC:

<hibernate-mapping package="com.xings.common">
<class name="com.xings.common.Topic" table="Topic">

<id name="_ID" column="t_id" type="long" unsaved-value="null">
<generator class="native"/>
</id>

<property name="_name" column="t_name" type="string"
length="63" not-null="true">
</property>
<!--
<property name="_ownerID" column="u_id" type="long" not-null="false"/>
-->
<property name="_dateAdded" column="t_dateAdded" type="date"
not-null="true"/>

<property name="_dateUpdated" column="t_dateUpdated" type="date"
not-null="true"/>

<many-to-one name="_owner" class="com.xings.common.User" column="u_id" not-null="true"/>

</class>
</hibernate-mapping>


CODE:

Topic topic1 = new Topic("July 4th Stuff");
topic1.set_dateAdded(d);
topic1.set_dateUpdated(d);

Topic topic2 = new Topic("Tahiti Trip Stuff");
topic2.set_dateAdded(d);
topic2.set_dateUpdated(d);

User user1 = new User("James", "Brown", "jamesbrown");
user1.set_dateAdded(d);
user1.set_dateUpdated(d);
user1.set_topics(new HashMap());

topic1.set_owner(user1);
topic2.set_owner(user1);

s.save(user1);
s.save(topic1);
s.save(topic2);
s.flush();


I currently have things working fine where I save User and Topic individually and all is mapped correctly in the DB. It's just that I have to explicitly save Topic first and set Topic's owner. Again, my question is to see if it's possible to skip the topic.setOwner(owner) part and just do user.addTopic(topic) and save in order to cascade-save everything automatically.

Ideally, something that looks like:


Topic topic1 = new Topic();
Topic topic2 = new Topic();

//assuming user1 is already loaded...
user1.addTopic(topic1);
user1.addTopic(topic2);
s.save(user1);

where it'll automatically save the new Topics and associate them with the appropriate User


Thank you very much!!


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 10, 2004 5:08 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
Unfortunatly, you cannot achieve what you what. You'll have to save children before retrieving their ids.

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 10, 2004 5:42 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
It's not even clear why in this case you are using a Map. If the only key in the Map is the object's ID itself, then you basically are only storing the objects themselves. So why not just have a Set of the objects? Your _topics collection *should* be just a Set.

Then, assuming your Topic objects *don't* define equals or hashCode, you can add Topics to your User's topic Set whether they have been persisted or not, and it will all just work.

In other words, why would you prefer user.getTopicMap().containsKey(myTopic.id()) over just plain user.getTopicSet().contains(myTopic)???

Cheers,
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 10, 2004 6:07 pm 
Newbie

Joined: Mon Feb 09, 2004 5:49 am
Posts: 17
Ah, darn. Thanks though, Emmanuel :)

Oh, as for using maps, that's definitely a good question. I'll constantly be looking grabbing specific Topics from a User at particular times. If I used a set, I would have to loop through all the Topics in order to find the one I wanted. Using a map, however, I can simply do getTopic(topicID). So instead of n iterations to find the Topic I was looking for, I'm guaranteed 1. I'll be using this in lookups, deleting certain topics, etc.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 10, 2004 6:10 pm 
Newbie

Joined: Mon Feb 09, 2004 5:49 am
Posts: 17
Well, just as a clarification, the pseudocode for the set would be:

//loop through each Topic in the set
//check TopicID vs. the one you're looking for
//if found, break and return current Topic
//else keep looking til the end

For a map, it'd be

//ask for Topic by ID
//return the Topic from the HashMap


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 10, 2004 6:18 pm 
Newbie

Joined: Mon Feb 09, 2004 5:49 am
Posts: 17
Actually, Hibernate Team, I suppose this is a good time to ask this.

Does this approach compliment the "lazy='true'" feature of Hibernate? I would ask the three scenarios to figure it out:

Using Maps:

1. If I do something like a session.load(user), and I was using a Map, would the entire map be loaded, or will each Topic be loaded individually when I specifically call for it? user.getTopic(topicID) for example?

2. What happens if I call something like user.getTopics()?

Using Sets:
2. How about for Sets?
a. Is the entire set loaded when I call session.load(user), or is it loaded when i do something like user.getTopics()?

Your clarification on this matter would certainly help a lot of optimizations! Thanks guys :)


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 10, 2004 6:19 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
There are multiple ways to skin this cat then:

1) You could filter the set.
Collection userTopic = session.filter(user.getTopicSet(), "where this.id = "+theTopicId);

2) You could load the Topic and then see if it's in the Set.
Topic topic = session.get(Topic.class, theTopicId);
boolean isUsersTopic = user.getTopicSet().contains(topic);

All kinds of ways to do it with Sets, and the second alternative even works around all issues of whether user or topic have been persisted yet....

Cheers!
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 10, 2004 6:21 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
cmubruin wrote:
Actually, Hibernate Team, I suppose this is a good time to ask this.

I'm not on the hibernate team but when has that ever stopped me? ;-)
cmubruin wrote:
1. If I do something like a session.load(user), and I was using a Map, would the entire map be loaded, or will each Topic be loaded individually when I specifically call for it? user.getTopic(topicID) for example?

Knowing Hibernate, I would assume it will load only the requested ID.
Quote:
2. What happens if I call something like user.getTopics()?

You'll get a map that has had nothing loaded for it yet.

cmubruin wrote:
2. How about for Sets?
a. Is the entire set loaded when I call session.load(user), or is it loaded when i do something like user.getTopics()?

Neither. When you call user.getTopics() you'll get a lazy set that will only load data when you start calling methods like contains() or iterator() on it.

Cheers!
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 10, 2004 6:33 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Actually, the answer to this depends upon wether it is a one-to-many or a many-to-many, and if a many-to-many, what the outer-join setting is. You need to experiment and observe the affect of different mappings.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 10, 2004 8:03 pm 
Newbie

Joined: Mon Feb 09, 2004 5:49 am
Posts: 17
Ah, thanks guys. I'll definitely experiment some more!

Hey Rob, thanks for the pointers. Actually, I was going to ask if you knew how Set implemented the "contains" method. Does it iterate through the Set? (From a purely java, not Hibernate standpoint)

Now with Hibernate involved, does mySet.contains(id) invisibly tell hibernate to call "SELECT topicID, topicName, etc FROM TOPICS" and determine whether it has any rows or not?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 10, 2004 8:08 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Quote:
I was going to ask if you knew how Set implemented the "contains" method. Does it iterate through the Set?


No, its a HashSet; it uses the hashcode.

Quote:
Now with Hibernate involved, does mySet.contains(id) invisibly tell hibernate to call "SELECT topicID, topicName, etc FROM TOPICS" and determine whether it has any rows or not?


I don't follow....


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 11, 2004 3:30 am 
Newbie

Joined: Mon Feb 09, 2004 5:49 am
Posts: 17
Ah, sorry Gavin. Let me try to clarify.

Well, as "Topic"s currently set to "lazy='true'", session.load(user) isn't going to load up the Set: "Topics"

But what happens in this code:

/*Populates the User object with properties but leaves
the "Topics" set unloaded because it's "lazy"
*/
session.load(user);

//check to see if the topic with the topicID is contained in this Set
user.getTopics().contains(topicID);

Are all topics of the User loaded? and then is the "contains()" operation performed on a fully filled Set?

Or does Hibernate query that Topic with that topicID without loading the Set? (And if it does query that Topic, and it exists, does that mean that the Set: "topics" now has one entry?)


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 11, 2004 4:50 am 
Hibernate Team
Hibernate Team

Joined: Tue Sep 09, 2003 2:10 pm
Posts: 3246
Location: Passau, Germany
The whole set gets loaded, doing one selects for every access would be imperformant and lead to n+1 problems.


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