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.  [ 10 posts ] 
Author Message
 Post subject: joined-subclass and many-to-many associations
PostPosted: Tue Jun 12, 2007 4:26 pm 
Newbie

Joined: Tue Jun 12, 2007 4:09 pm
Posts: 5
I have an interface called "Folder", which is implemented by two subclasses: "FolderA" and "FolderB". FolderA contains a set of any number of FolderBs, and FolderB contains a set of any number of FolderAs. I have another class, "FileCabinet", which contains a Set of folders of either type - this is why I'm using explicit inheritance in the first place.

I need to persist this to the database as a many-to-many relationship. I tried to do this by using joined-subclass (only relevant parts shown):

<class name="Folder" table="FOLDER" >
<id name="id" column="FOLDER_ID">
<generator class="native" />
</id>

<joined-subclass name="FolderA" table="FOLDER_A">
<key column="FOLDER_ID"/>
<set name="otherFolders" inverse="true" table="FOLDER_JOIN">
<key column="FOLDER_ID" not-null="true" />
<many-to-many class="FolderB" column="FOLDER_ID" />
</set>
</joined-subclass>

<joined-subclass name="FolderB" table="INVENTORY_FOLDER">
<key column="FOLDER_ID"/>
<set name="otherFolders" table="FOLDER_JOIN">
<key column="FOLDER_ID" not-null="true" />
<many-to-many class="FolderA" column="FOLDER_ID" />
</set>
</joined-subclass>
</class>

The problem is, the join table created for the two sets gives me a duplicate column exception, since it tries to create one table with two FOLDER_ID columns. Is there any way to change the name of the column in the join table?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jun 13, 2007 10:05 am 
Newbie

Joined: Wed Feb 14, 2007 11:18 am
Posts: 10
It is difficult to answer this without knowing what the differences are between FolderA and FolderB. It does not look like you are actually subclassing FolderB from FolderA or vice versa (implementing an interface is not inheritance), so <joined-subclass> is not appropriate.

You need something like this in each mapped class (assumes a Set property to hold the collections), and since you are describing a many-to-many relation between FolderA and FolderB, you may need a join table consisting of at least a folderA_id and folderB_id column which are foreign keys to the respective tables (and you have not disclosed whether you are using a table-per-class-hierarchy or table-per-subclass scheme. The following assumes the latter:


Code:
<hibernate-mapping>
<class name="FolderA" table="FolderA(?)"
<id name="id" column="FOLDER_ID">
<generator class="native" />
</id>
//====properties of FolderA class====

<set name="folderBs" table="<join-table>" cascade="save-update" >
   <key>
      <column name="folderA_id" />
   </key>
   <many-to-many class="FolderB" column="folderB_id" />
</set>
</hibernate-mapping>



It looks like the FileCabinet to Folder[A][B] is one-to-one?

Look at section 6 of the on-line documentation. http://www.hibernate.org/hib_docs/v3/reference/en/html/collections.html

_________________
Tom McC.
"better to journey than to arrive"


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jun 13, 2007 10:18 am 
Newbie

Joined: Tue Jun 12, 2007 4:09 pm
Posts: 5
Yes, I'm sorry, I believe that I simplified too much in asking my question. Here's the real setup:

Doing an inventory of a computer network, and allowing the user to organize things into folder. I've got the following objects: Folder, Computer, Datastore. A folder can contain computers, datastores, or other folders. Computers and datastores have a many-to-many relationships because a computer may use any number of datastores, which may themselves be used by any number of computers.

The problem is in the Folder object - I need it to contain a polymorphic Set containing any of computers, datastores, or folders. I want to persist this to the database.

My solution was to make them all inherit from "InventoryItem", and write the <Set> element in the Folder to refer to the InventoryItem class heirarchy. I was using the table-per-subclass paradigm (hence the joined-subclass). I don't want to put them all in a single table - there will be other InventoryItems later, and my database would end up being a single gigantic table, which smells... However, ran into the problem of Computer referring to Datastore - because their primary keys are the same, I can't create a join table using the <Set> element.


Top
 Profile  
 
 Post subject: Solution to the problem
PostPosted: Fri Jun 15, 2007 1:13 pm 
Newbie

Joined: Tue Jun 12, 2007 4:09 pm
Posts: 5
I ended up getting around the problem of polymorphic child sets by using the rather undocumented <many-to-any> association. I realize this has certain disadvantages (no foreign key constraint), but I don't know of a better way around it.

For anyone who's interested in doing the same, the following are the relevant sections of the hibernate mapping files. Folder's "parent" is an <any> relationship since a Folder may be contained by another Folder, or by another datatype which I haven't mentioned here for simplicity's sake.


Code:
<class name="Folder" table="FOLDERS">
  <id name="id" column="FOLDER_ID" unsaved-value="-1">
   <generator class="native" />
  </id>
      
  <any name="parent" id-type="long" >
   <column name="PARENT_TYPE" />
   <column name="PARENT_ID" />
  </any>
      
  <set name="children" cascade="all,delete-orphan" table="FOLDER_CHILD_ASSOC" inverse="true">
   <key column="FOLDER_ID" />
   <many-to-any id-type="long">
    <column name="CHILD_TYPE" not-null="true"/>
    <column name="CHILD_ID" not-null="true"/>
   </many-to-any>
  </set>
</class>

<class name="Computer" table="COMPUTERS">
  <id name="id" column="COMPUTER_ID" unsaved-value="-1">
   <generator class="native" />
  </id>
      
  <many-to-one name="parentFolder" class="Folder" column="PARENT_FOLDER_ID" unique="true" />
      
  <set name="datastores" inverse="true" table="COMPUTER_DATASTORE">
   <key column="COMPUTER_ID" />
   <many-to-many class="Datastore" column="DATASTORE_ID" />
  </set>
</class>

<class name="Datastore" table="DATASTORES">
  <id name="id" column="DATASTORE_ID" unsaved-value="-1">
   <generator class="native" />
  </id>
      
  <many-to-one name="parentFolder" class="Folder" column="PARENT_FOLDER_ID" unique="true" />
            
  <set name="computers" table="COMPUTER_DATASTORE">
   <key column="DATASTORE_ID" />
   <many-to-many class="Computer" column="COMPUTER_ID" />
  </set>
</class>


Top
 Profile  
 
 Post subject: Re:
PostPosted: Fri Jun 15, 2007 6:24 pm 
Senior
Senior

Joined: Tue Jun 12, 2007 4:49 pm
Posts: 127
Location: India
Try this:


classes:
Code:
public interface IClass {
   
   
   public long getId();
   
   public void setId(long id);

}

public class ClassA implements IClass{
   
   private long id;
   
   private Set<IClass> classBs;

   

   public long getId() {
      return id;
   }

   public void setId(long id) {
      this.id = id;
   }

   public Set<IClass> getClassBs() {
      return classBs;
   }

   public void setClassBs(Set<IClass> classBs) {
      this.classBs = classBs;
   }

}

public class ClassB implements IClass{
   
   private long id;
   
   private ClassA classA;

   public ClassA getClassA() {
      return classA;
   }

   public void setClassA(ClassA classA) {
      this.classA = classA;
   }

   public long getId() {
      return id;
   }

   public void setId(long id) {
      this.id = id;
   }
   

}


mapping:
Code:
<class name="IClass">
      <id name="id">
         <generator class="increment" />
      </id>


      <joined-subclass name="ClassA" extends="IClass">

         <key column="id" />

         <set name="classBs" cascade="save-update">
            <key column="parent" />
            <one-to-many class="IClass" />
         </set>

      </joined-subclass>

      <joined-subclass name="ClassB">
         <key column="id" />

      </joined-subclass>
   </class>

Test:
Code:

      Session session = factory.openSession();
      ClassA a = new ClassA();
      
      session.save(a);
      
      session.flush();
      
      session.close();
      
      session = factory.openSession();
      
      a = (ClassA)session.load(ClassA.class, a.getId());
      
      ClassB b = new ClassB();
      
      a.getClassBs().add(b);
      
      b = new ClassB();
      
      a.getClassBs().add(b);
      
      session.update(a);
      
      session.flush();
      
      System.out.println(a.getClassBs().size());
      
      session.close();
      
   


*Please do rate if this helps*

Regards,
Jitendra


Top
 Profile  
 
 Post subject: Re:
PostPosted: Fri Jun 15, 2007 6:24 pm 
Senior
Senior

Joined: Tue Jun 12, 2007 4:49 pm
Posts: 127
Location: India
cleaning repeat posts


Last edited by jits_1998 on Fri Jun 15, 2007 6:31 pm, edited 2 times in total.

Top
 Profile  
 
 Post subject: Re:
PostPosted: Fri Jun 15, 2007 6:27 pm 
Senior
Senior

Joined: Tue Jun 12, 2007 4:49 pm
Posts: 127
Location: India
cleaning repeat posts. Sorry!


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jun 16, 2007 12:47 pm 
Newbie

Joined: Tue Jun 12, 2007 4:09 pm
Posts: 5
The whole point of my question was the polymorphic many-to-many relationship between two entities. Your one-to-many relationship example really doesn't help with that situation - the problem was in the column name collision in a joint table due to the many-to-many.

Thanks for trying, but I've posted the solution I'm using above.


Top
 Profile  
 
 Post subject: Re:
PostPosted: Sat Jun 16, 2007 4:55 pm 
Senior
Senior

Joined: Tue Jun 12, 2007 4:49 pm
Posts: 127
Location: India
If you computer, folder etc implement a single interface then in your many-to-many or many-to-one references you can use the interface instead of concrete class.

If you treat them differently in code, hibernate will treat them differently by adding different columns for various inheritances.

Regards,
Jitendra


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jun 17, 2007 7:11 am 
Newbie

Joined: Tue Jun 12, 2007 4:09 pm
Posts: 5
Listen, I appreciate your attempt to help, but I don't think you read the original question. I understand how the basic inheritance model in hibernate works - it's not difficult. My original mapping contains all the concepts you've posted.

The problem is that the <many-to-many> relationships creates a join table that contains the primary key of each side of the relationship. Even when I change the <key column="">, the created table still has that primary key in there, it just seemed to create a new column. Because of the <joined-subclass> relationship, the primary keys columns are named the same, and I received a naming conflict in the join table since.

Implicit polymorphism and the many-to-any seem to work fine.


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