-->
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.  [ 12 posts ] 
Author Message
 Post subject: Hibernate Collection Best Practices
PostPosted: Mon Apr 09, 2007 1:15 pm 
Newbie

Joined: Mon Apr 09, 2007 12:25 pm
Posts: 19
Location: Boston, MA
Hibernate version:
3.1.3

I think this question borders somewhere between Hibernate best practices and overall Collection selection and use. I have looked all over (documentation, forums, internet) for a clear-cut answer, but I still don't quite feel comfortable with a particular direction.

In this example I have two objects:

    ThingHolder
    Thing

      Let's say Thing has two properties:
        name (String)
        thingHolder (ThingHolder)
          In reality it would have many.

          ThingHolder also might have many properties, but in this example it has two:
            name (String)
            things (Collection of some sort)

          Currently I have ThingHolder and Thing coded as such:

          Code:
          public class Thing {
             protected String name;

             protected ThingHolder thingHolder;

             public String getName() {
                return this.name;
             }

             public void setName(String name) {
                this.name = name;
             }

             public ThingHolder getThingHolder() {
                return this.thingHolder;
             }

             public void setThingHolder(ThingHolder thingHolder) {
                this.thingHolder = thingHolder;
             }
          }

          public class ThingHolder {
             protected String name;

             protected List things;

             public String getName() {
                return this.name;
             }

             public void setName(String name) {
                this.name = name;
             }

             public List getThings() {
                return this.things;
             }

             public void setThings(List things) {
                this.things = things;
             }

          }


          As you can see I've selected List as my collection type. When I started mapping my ThingHolder and Thing via Hibernate I started seeing that Hibernate tends to prefer Sets vs Lists for this sort of thing (perhaps I'm wrong). A little more about ThingHolder and Thing:

            ThingHolders have many Things.

            The Things associated with a given ThingHolder would be unique as far as their name property goes (this makes me think I should be using a Set).

            A Thing will have one and only one ThingHolder.

            Things in a ThingHolder do have an order, more specifically, they are ordered by creation (perhaps there's a createdDate property in Thing somewhere (this made me think Set wasn't the right Collection for the job).

            The middle tier/web tier refer to the Things by index quite a bit. More specifically in the various org.springframework.validation.Validator validators, where you are required to call something like errors.pushNestedPath("things[" + i + "]") to call sub-validators. Also, there are quite alot of thisList.get(x) calls. This makes me think that something index-based (like a List) is appropriate.


Given all of this, am I using the correct Collection type and if so, what Hibernate collection type should I be using? List? Bag? IDBag? List seemed to work fine, but then when I started making a many-to-one mapping back from Thing to ThingHolder, Hibernate didn't seem to like it. Can you give me an example of what you'd imagine my mapping files to be?

Thanks,
Leo


Last edited by leojhartiv on Mon Apr 09, 2007 5:19 pm, edited 2 times in total.

Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 09, 2007 2:21 pm 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
Hibernate list mappings are only appropriate for collections of objects with a unique, sequential indexing property. If your mapping does not have an index property (sometimes referred to as 'sort' property), then do not use <list>. Another caveat: if you do have an index property, and it is not sequential, then you will have nulls in your list.

Since I see no possible index columns in the properties of your pojos (mapping files would help), I will assume that a set or bag mapping would be more appropriate, especially if you are iterating over the collection and performing operations on the elements.

Hibernate list mappings != java.util.List . There is an semantic association there, but they are not functionally equivalent.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 09, 2007 4:49 pm 
Newbie

Joined: Mon Apr 09, 2007 4:05 pm
Posts: 1
Location: Boston, MA
I'm having a similar issue where code was written for parent-child relationships where the parent object has a List of child objects.
When I tried to map this using Lists hibernate complained... so I switched them to Sets in the mapping file and in the classes themselves... but that caused a lot of code to break because they are using For loops to traverse the Lists... which won't work with Sets.

Is there some way to represent many-to-one relationships in the mapping file using Lists... or if not is there some easy way to convert Sets to Lists ?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 09, 2007 5:18 pm 
Newbie

Joined: Mon Apr 09, 2007 12:25 pm
Posts: 19
Location: Boston, MA
Thanks for your reply. Maybe I can clarify a bit more.

Thing is an entity in itself, as is ThingHolder. I would see this as being in a table like:

create table thing (
thing_holder_id number,
id number,
name varchar,
cre_d datetime)


create table thing_holder (
id number,
name varchar,
cre_d datetime)

Given a thing_holder, if I wanted to find its things:

select * from things where thing_holder_id = ?;

Given a thing, if I wanted to find its thing_holder:

select hld.*
from thing_holder hld
inner join thing tng on (hld.id = tng.thing_holder_id)
where tng.id = ?

What JAVA collection best fits the requirements I specified above (in the first post and this) and what might a Hibernate mapping file look like that would map to those collections (List->Bag, Set->Set, etc).

Thanks again,
Leo


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 09, 2007 7:46 pm 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
OIC

Lemme take a stab. We'll implement a bidirectional one-to-many associations using Sets.

Mappings:

Code:
<hibernate-mapping>
  <class name="eg.Thing">
        <id name="thingId" type="java.lang.Long">
            <column name="id"/>
            <generator class="assigned" />
        </id>
        <property name="name" type="string">
          <column name="name"/>
        </property>
        <property name="createDate" type="date">
          <column name="cre_d"/>
        </property>
        <many-to-one name="thingHolder" class="eg.ThingHolder">
          <column name="thing_holder_id"/>
        </many-to-one>
  </class>
</hibernate-mapping>

<hibernate-mapping>
  <class name="eg.ThingHolder">
        <id name="thingHolderId" type="java.lang.Long">
            <column name="thing_holder_id"/>
            <generator class="assigned" />
        </id>
        <property name="name" type="string">
          <column name="name"/>
        </property>
        <property name="createDate" type="date">
          <column name="cre_d"/>
        </property>
        <set name="things" inverse="true">
          <key>
            <column name="id"/>
          </key>
          <one-to-many class="eg.Thing"/>
        </set>
  </class>
</hibernate-mapping>


And the POJOs

Code:
public class Thing {
  private Long thingId;
  private String name;
  private Date createDate;
  private eg.ThingHolder thingHolder;

  // Getters and setters omitted for clarity
}

public class ThingHolder {
  private Long thingHolderId;
  private String name;
  private Set things = new HashSet();

  // Getters and setters omitted for clarity
  // With bidirection associations, some collection management routines are highly suggested
  // for example:
  public void addThing(Thing thing) {
    this.getThings.add(thing);
    thing.setThingHolder(this);
    // null-safety and exception checking omitted for clarity
  }
}


Finally, the queries

Code:
  // Things from thing_holder_id
  session.createQuery("from Things as thing where thing.holder.id=?");
  // There are many other ways of accomplishing this same query, this example uses implicit joining

  // ThingHolder from id
  session.createQuery("from ThingHolder as holder where holder.things.id=?");
  // I'd never do it this way, preferring to load the Thing instance and navigating to the ThingHolder via the getThingHolder() method


Sorry for any typos, etc. I don't have an IDE at the moment.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 10, 2007 9:07 am 
Newbie

Joined: Mon Apr 09, 2007 12:25 pm
Posts: 19
Location: Boston, MA
Thanks for your example and this does move towards my point/question. See how you used Sets there as a collection? It seems that Hibernate prefers the Set over List and other collection types as that's always in the examples provided; however, given the fact that I have the need throughout my application to refer to items in the collection by their index (myList.get(1), myList.get(2)) and that the items in my collection are ordered, is Set really the collection type that I want to use? Wouldn't an ArrayList (List) be a better fit? And if so, what should I map that ArrayList (List) to?

Thanks,
Leo


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 10, 2007 9:23 am 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
Well, the actual list mapping requires a single, integer column which dictates the list index. If you don't have a column that meets that requirement, then an indexed collection is not for you. IMHO, if you have to refer to a collection by index reference, you have an architectural issue. Of course, if you insist on accessing a collection that way, then keep using Set and use things.toArray()[0], things.toArray[1], etc...


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 10, 2007 9:35 am 
Newbie

Joined: Mon Apr 09, 2007 12:25 pm
Posts: 19
Location: Boston, MA
Right now we don't have any real tables designed (other than what I mocked up to you), but we can assume that both Thing and ThingHolder are entities and that they each have a single integer-based surrogate key.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 10, 2007 10:02 am 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
You're cool then! If you have the chance to define your tables, then by all means, put an index ("sort") column in them and use list mappings. Note that this is a totally separate column from any "key" column.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 10, 2007 12:18 pm 
Newbie

Joined: Mon Apr 09, 2007 12:25 pm
Posts: 19
Location: Boston, MA
I guess I don't get that. Why does it need to be totally separate? I mean, if the PK on a table uniquely defines an instance of an entity, why would you need a second "index" column?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 10, 2007 2:53 pm 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
As an example:

Thing table:
Code:
PK          NAME
--          ----
0           Name0
1           Name1
2           Name2
3           Name3
4           Name4


So, things are good! Your List looks like {0:Name0;1:Name1;2:Name2;3:Name3;4:Name4;5:Name5}

Lets delete #2. Now, your list looks like {0:Name0;1:Name1;null;3:Name3;4:Name4;5:Name5} . Well, that's not so good.

Instead of deleting #2, let's swap #3 & #4. Well, you can't do that, because you are violating the contract of a primary key.

I could go on, but essentially, a primary key != a list index. Not at all. A list index does not uniquely identify a record. A primary key does. The list index's only contract is to maintain an order of a collection.

I'm going to have to refer you to the documentation now,
http://www.hibernate.org/hib_docs/v3/re ... ollections
and specifically,
http://www.hibernate.org/hib_docs/v3/re ... ns-indexed


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 10, 2007 3:25 pm 
Newbie

Joined: Mon Apr 09, 2007 12:25 pm
Posts: 19
Location: Boston, MA
Thanks for all your replies. It looks like I need to go with sets for what I'm trying to do...


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