-->
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.  [ 7 posts ] 
Author Message
 Post subject: Recursive one-to-many with composite-key
PostPosted: Thu Sep 08, 2005 10:06 am 
Newbie

Joined: Thu Sep 08, 2005 9:34 am
Posts: 8
Hi

I've got the following class which has a two relationships to itself.

many-to-one to its parent. parent
one-to-many to its children. parts

This is the cfg.xml file (which was created using the Hiberate tools)

Code:
<hibernate-mapping>
<!--
        Auto-generated mapping file from
        the hibernate.org cfg2hbm engine
-->
    <class name="Part" table="_part">
        <composite-id name="id" class="PartId">
            <key-property name="partNumber" type="string">
                <column name="partNumber" length="20" />
            </key-property>
            <key-property name="supplier" type="string">
                <column name="supplier" length="20" />
            </key-property>
        </composite-id>
        <many-to-one name="parent" class="Part">
            <column name="parent_PartNumber" length="20" />
            <column name="parent_Supplier" length="20" />
        </many-to-one>
        <property name="description" type="string">
            <column name="description" length="100" />
        </property>
         ...
         ...
        <set name="parts" inverse="true">
           <key>
      <column name="parent_PartNumber" length="20" />
           <column name="parent_Supplier" length="20" />
           </key>
           <one-to-many class="Part"/>
        </set>
    </class>
</hibernate-mapping>


What is basically happening when the Part class is created the many-to-one relationship parent is correctly created.

But for the parts (all the children for this part) the set is not populated.

Any help appriciated.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 08, 2005 4:26 pm 
Pro
Pro

Joined: Fri Sep 02, 2005 4:21 am
Posts: 206
Location: Vienna
Hi,

Could you provide a complete example of code illustrating your problem.

I tried to reproduce it - but everything was fine.

Erik


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 09, 2005 3:39 am 
Newbie

Joined: Thu Sep 08, 2005 9:34 am
Posts: 8
Hi Erik,

Here is the code. All I'm doing here is to populate a JTree with the Part class.

Code:
import javax.swing.tree.*;

import org.hibernate.Session;
import org.hibernate.Transaction;

public class RefreshTree implements Runnable{

   private DefaultTreeModel model;
   private PartId partId;
   private DefaultMutableTreeNode root;
   
   public RefreshTree(DefaultTreeModel model,PartId partId){
      this.model = model;
      this.partId = partId;
   }
   
   public void run(){
      try{
         Session session = HibernateUtil.currentSession();
         //Transaction tx = session.beginTransaction();
         //tx.rollback();
         
         Part myPart = new Part();
         session.load(myPart,partId);
         //tx.commit();
         //HibernateUtil.closeSession();
         viewPart(myPart,null);
      }
      catch (Exception e){
         root = new DefaultMutableTreeNode(e.toString());
      }
      model.setRoot(root);
   }
   
   private void viewPart(Part part,DefaultMutableTreeNode node){
      // Check if root
      if (node == null){
         root = new DefaultMutableTreeNode(getPartDescription(part));
         node = root;
      }
      else{
         node.add(new DefaultMutableTreeNode(getPartDescription(part)));
      }
      //Check Parent
      if (part.getParent() != null){
         node.add(new DefaultMutableTreeNode("Parent -> " + getPartDescription(part.getParent())));
      }
      
      //Children
      if (part.getParts() != null){
         while (part.getParts().iterator().hasNext()){
            Part childPart = (Part) part.getParts().iterator().next();
            viewPart(childPart,node);
         }
      }
   }
   
   private String getPartDescription(Part part){
      String tmp = "No Info ....";
      tmp = trimMe(part.getId().getPartNumber());
      tmp+= trimMe("," + part.getId().getSupplier());
      tmp+= "[" + trimMe(part.getDescription()) + "]";
      return tmp;
   }
   
   private String trimMe(String source){
      if (source != null){
         return source.trim();
      }
      return "";
   }
}


PartId is the composite-key class that was created by the Hibernate Tool. When the code is run, the getParts() always return with a size of 0. (Set size = 0).

Here is the code that uses this Runnable class.
Code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;

public class PartViewer extends JPanel{

   private JTextField txtPartNo = new JTextField();
   private JTextField txtSupplier = new JTextField();
   private JButton btnRefresh = new JButton("Refresh");
   private DefaultMutableTreeNode root = new DefaultMutableTreeNode("...");
   private DefaultTreeModel model = new DefaultTreeModel(root);
   
   public PartViewer(){
      this.setLayout(new BorderLayout());
      JPanel pnlPartSelection = new JPanel();
      pnlPartSelection.setLayout(new GridLayout(1,5));
      pnlPartSelection.add(new JLabel("Part No"));
      pnlPartSelection.add(txtPartNo);
      pnlPartSelection.add(new JLabel("Supplier"));
      pnlPartSelection.add(txtSupplier);
      pnlPartSelection.add(btnRefresh);
      add(pnlPartSelection,BorderLayout.NORTH);
      JTree tree = new JTree(model);
      add(new JScrollPane(tree),BorderLayout.CENTER);
      btnRefresh.addActionListener(new ActionListener(){
         public void actionPerformed(ActionEvent e){
            refreshPart();
         }
      });
   }
   
   
   public void refreshPart(){
      try{
         model.setRoot(new DefaultMutableTreeNode("Refreshing ... "));
         PartId id = new PartId();
         id.setPartNumber(txtPartNo.getText());
         id.setSupplier(txtSupplier.getText());

         new Thread(new RefreshTree(model,id)).start();
      }
      catch (Exception e){
         e.printStackTrace();
      }
   
   }
}


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 09, 2005 5:37 am 
Newbie

Joined: Mon May 16, 2005 8:25 am
Posts: 7
Location: Hertford, UK
I'm working with Carel on this issue. We have found that if we replace the table in the database with a table that uses technical keys (rather than composite keys) the same scenario works.

Are there any known issues with self join associations and composite keys ?

Mick


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 09, 2005 5:56 am 
Newbie

Joined: Thu Sep 08, 2005 9:34 am
Posts: 8
OK here is the complete code with xml.

Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
      "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
      "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.informix.jdbc.IfxDriver</property>
        <property name="hibernate.connection.password">******</property>
        <property name="hibernate.connection.url">jdbc:informix-sqli://****:****/rurl2_cadb00:INFORMIXSERVER=*****</property>
        <property name="hibernate.connection.username">*****</property>
        <property name="hibernate.dialect">org.hibernate.dialect.InformixDialect</property>
        <property name="hibernate.show_sql">true</property>
        <mapping resource="Part.hbm.xml"/>
    </session-factory>
</hibernate-configuration>


Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!--
        Auto-generated mapping file from
        the hibernate.org cfg2hbm engine
-->
    <class name="Part" table="_part">
        <composite-id name="id" class="PartId">
            <key-property name="partNumber" type="string">
                <column name="partNumber" length="20" />
            </key-property>
            <key-property name="supplier" type="string">
                <column name="supplier" length="20" />
            </key-property>
        </composite-id>
        <many-to-one name="parent" class="Part">
            <column name="parent_PartNumber" length="20" />
            <column name="parent_Supplier" length="20" />
        </many-to-one>
        <property name="description" type="string">
            <column name="description" length="100" />
        </property>
        <set name="parts" inverse="true">
           <key>
      <column name="parent_PartNumber" length="20" />
           <column name="parent_Supplier" length="20" />
           </key>
           <one-to-many class="Part"/>
        </set>
    </class>
</hibernate-mapping>


Code:
import java.awt.*;
import java.util.*;
import javax.swing.*;

public class TestProjectDAO extends JFrame{

   public static void main(String[] args) {
      TestProjectDAO pr = new TestProjectDAO();
      pr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      pr.setSize(400,300);
      pr.setExtendedState(JFrame.MAXIMIZED_BOTH);
      pr.getContentPane().setLayout(new BorderLayout());
      pr.getContentPane().add(new PartViewer(),BorderLayout.CENTER);
      pr.setVisible(true);
   }

}


Code:
import javax.swing.tree.*;

import org.hibernate.Session;
import org.hibernate.Transaction;

public class RefreshTree implements Runnable{

   private DefaultTreeModel model;
   private PartId partId;
   private DefaultMutableTreeNode root;
   
   public RefreshTree(DefaultTreeModel model,PartId partId){
      this.model = model;
      this.partId = partId;
   }
   
   public void run(){
      try{
         Session session = HibernateUtil.currentSession();
         //Transaction tx = session.beginTransaction();
         //tx.rollback();
         
         Part myPart = new Part();
         session.load(myPart,partId);
         //tx.commit();
         //HibernateUtil.closeSession();
         viewPart(myPart,null);
      }
      catch (Exception e){
         root = new DefaultMutableTreeNode(e.toString());
      }
      model.setRoot(root);
   }
   
   private void viewPart(Part part,DefaultMutableTreeNode node){
      // Check if root
      if (node == null){
         root = new DefaultMutableTreeNode(getPartDescription(part));
         node = root;
      }
      else{
         node.add(new DefaultMutableTreeNode(getPartDescription(part)));
      }
      //Check Parent
      if (part.getParent() != null){
         node.add(new DefaultMutableTreeNode("Parent -> " + getPartDescription(part.getParent())));
      }
       
      //Children
      if (part.getParts() != null){
         while (part.getParts().iterator().hasNext()){
            Part childPart = (Part) part.getParts().iterator().next();
            viewPart(childPart,node);
         }
      }
   }
   
   private String getPartDescription(Part part){
      String tmp = "No Info ....";
      tmp = trimMe(part.getId().getPartNumber());
      tmp+= trimMe("," + part.getId().getSupplier());
      tmp+= "[" + trimMe(part.getDescription()) + "]";
      return tmp;
   }
   
   private String trimMe(String source){
      if (source != null){
         return source.trim();
      }
      return "";
   }
}


Code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;

public class PartViewer extends JPanel{

   private JTextField txtPartNo = new JTextField("PARENT");
   private JTextField txtSupplier = new JTextField("AMCE");
   private JButton btnRefresh = new JButton("Refresh");
   private DefaultMutableTreeNode root = new DefaultMutableTreeNode("...");
   private DefaultTreeModel model = new DefaultTreeModel(root);
   
   public PartViewer(){
      this.setLayout(new BorderLayout());
      JPanel pnlPartSelection = new JPanel();
      pnlPartSelection.setLayout(new GridLayout(1,5));
      pnlPartSelection.add(new JLabel("Part No"));
      pnlPartSelection.add(txtPartNo);
      pnlPartSelection.add(new JLabel("Supplier"));
      pnlPartSelection.add(txtSupplier);
      pnlPartSelection.add(btnRefresh);
      add(pnlPartSelection,BorderLayout.NORTH);
      JTree tree = new JTree(model);
      add(new JScrollPane(tree),BorderLayout.CENTER);
      btnRefresh.addActionListener(new ActionListener(){
         public void actionPerformed(ActionEvent e){
            refreshPart();
         }
      });
   }
   
   
   public void refreshPart(){
      try{
         model.setRoot(new DefaultMutableTreeNode("Refreshing ... "));
         PartId id = new PartId();
         id.setPartNumber(txtPartNo.getText());
         id.setSupplier(txtSupplier.getText());

         new Thread(new RefreshTree(model,id)).start();
      }
      catch (Exception e){
         e.printStackTrace();
      }
   
   }
}

Code:
import java.util.*;

public class PartId  implements java.io.Serializable {

    // Fields   

     private String partNumber;
     private String supplier;


    // Constructors

    /** default constructor */
    public PartId() {
    }
   
    // Property accessors

    /**
     *
     */
    public String getPartNumber() {
        return this.partNumber;
    }
   
    public void setPartNumber(String partNumber) {
        this.partNumber = partNumber;
    }

    /**
     *
     */
    public String getSupplier() {
        return this.supplier;
    }
   
    public void setSupplier(String supplier) {
        this.supplier = supplier;
    }


   public boolean equals(Object other) {
         if ( (this == other ) ) return true;
       if ( (other == null ) ) return false;
       if ( !(other instanceof PartId) ) return false;
       PartId castOther = ( PartId ) other;
         
       return ( (this.getPartNumber()==castOther.getPartNumber()) || ( this.getPartNumber()!=null && castOther.getPartNumber()!=null && this.getPartNumber().equals(castOther.getPartNumber()) ) )
&& ( (this.getSupplier()==castOther.getSupplier()) || ( this.getSupplier()!=null && castOther.getSupplier()!=null && this.getSupplier().equals(castOther.getSupplier()) ) );
   }
   
   public int hashCode() {
         int result = 17;
         
         result = 37 * result + ( getPartNumber() == null ? 0 : this.getPartNumber().hashCode() );
         result = 37 * result + ( getSupplier() == null ? 0 : this.getSupplier().hashCode() );
         return result;
   }   


}

Code:
import java.util.*;

public class Part  implements java.io.Serializable {

    // Fields   

     private PartId id;
     private Part parent;
     private String partNumber;
     private String supplier;
     private String description;
     private Set parts;


    // Constructors

    /** default constructor */
    public Part() {
    }
   
    /** constructor with id */
    public Part(PartId id) {
        this.id = id;
    }
   
   
   

    // Property accessors

    /**
     *
     */
    public PartId getId() {
        return this.id;
    }
   
    public void setId(PartId id) {
        this.id = id;
    }

    /**
     *
     */
    public Part getParent() {
        return this.parent;
    }
   
    public void setParent(Part parent) {
        this.parent = parent;
    }

    /**
     *
     */
    public String getPartNumber() {
        return this.partNumber;
    }
   
    public void setPartNumber(String partNumber) {
        this.partNumber = partNumber;
    }

    /**
     *
     */
    public String getSupplier(){
        return this.supplier;
    }
   
    public void setSupplier(String supplier) {
        this.supplier = supplier;
    }


    /**
     *
     */
    public String getDescription(){
        return this.description;
    }
   
    public void setDescription(String description) {
        this.description = description;
    }

    /**
     *
     */
    public Set getParts() {
        return this.parts;
    }
   
    public void setParts(Set parts) {
        this.parts = parts;
    }
}


Code:
import org.hibernate.*;
import org.hibernate.cfg.*;
import org.apache.commons.logging.*;

public class HibernateUtil {

    private static Log log = LogFactory.getLog(HibernateUtil.class);

    private static final SessionFactory sessionFactory;

    static {
        try {
            // Create the SessionFactory
            sessionFactory = new Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            // Make sure you log the exception, as it might be swallowed
            log.error("Initial SessionFactory creation failed.", ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static final ThreadLocal session = new ThreadLocal();

    public static Session currentSession() {
        Session s = (Session) session.get();
        // Open a new Session, if this Thread has none yet
        if (s == null) {
            s = sessionFactory.openSession();
            session.set(s);
        }
        return s;
    }

    public static void closeSession() {
        Session s = (Session) session.get();
        if (s != null)
            s.close();
        session.set(null);
    }
}


DB2 SQL Scripts
Code:
CREATE TABLE _part  (
        PARTNUMBER CHAR(2) NOT NULL ,
        SUPPLIER CHAR(20) NOT NULL ,
        DESCRIPTION CHAR(100) ,
        PARENT_PARTNUMBER CHAR(20) ,
        PARENT_SUPPLIER CHAR(20) )  ;

ALTER TABLE PARTS
   ADD PRIMARY KEY
      (PARTNUMBER,
       SUPPLIER);


ALTER TABLE PARTS
   ADD CONSTRAINT PARENT_PARTS FOREIGN KEY
      (PARENT_PARTNUMBER,
       PARENT_SUPPLIER)
   REFERENCES _part
      (PARTNUMBER,
       SUPPLIER);

INSERT INTO _part VALUES ('PARENT', 'AMCE', 'Top Part', null, null);
INSERT INTO _part VALUES ('CHILD1', 'AMCE', 'cHILD 1', 'PARENT', 'AMCE');
INSERT INTO _part VALUES ('CHILD2', 'AMCE', 'CHILD 2', 'PARENT', 'AMCE');


Informix
Code:
CREATE TABLE _part  (
                  PARTNUMBER CHAR(20) NOT NULL ,
                  SUPPLIER CHAR(20) NOT NULL ,
                  DESCRIPTION CHAR(100) ,
                  PARENT_PARTNUMBER CHAR(20) ,
                  PARENT_SUPPLIER CHAR(20) ,

                  PRIMARY KEY (PARTNUMBER,SUPPLIER),
                  FOREIGN KEY (PARENT_PARTNUMBER,PARENT_SUPPLIER)
                     REFERENCES _part (PARTNUMBER,SUPPLIER));

INSERT INTO _part VALUES ('PARENT', 'AMCE', 'Top Part', null, null);
INSERT INTO _part VALUES ('CHILD1', 'AMCE', 'cHILD 1', 'PARENT', 'AMCE');
INSERT INTO _part VALUES ('CHILD2', 'AMCE', 'CHILD 2', 'PARENT', 'AMCE');


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 13, 2005 11:14 am 
Newbie

Joined: Thu Sep 08, 2005 9:34 am
Posts: 8
After spending two days debugging through the Hibernate source code, in particular to the following method

Code:
   public final void loadCollection(final SessionImplementor session,
                            final Serializable id,
                            final Type type)
         throws HibernateException {

      if ( log.isDebugEnabled() ) {
         log.debug(
               "loading collection: "+
               MessageHelper.collectionInfoString( getCollectionPersisters()[0], id, getFactory() )
         );
      }

      Serializable[] ids = new Serializable[]{id};
      try {
         doQueryAndInitializeNonLazyCollections(
               session,
               new QueryParameters( new Type[]{type}, ids, ids ),
               true
         );
      }
      catch ( SQLException sqle ) {
         throw JDBCExceptionHelper.convert(
                 factory.getSQLExceptionConverter(),
                 sqle,
                 "could not initialize a collection: " +
                 MessageHelper.collectionInfoString( getCollectionPersisters()[0], id, getFactory() ),
                 getSQLString()
         );
      }
   
      log.debug("done loading collection");

   }


The line of interrest here is:
Code:
         doQueryAndInitializeNonLazyCollections(
               session,
               new QueryParameters( new Type[]{type}, ids, ids ),
               true
         );


If one now looks at the method doQueryAndInitializeNonLazyCollections which looks like this

Code:
   private List doQueryAndInitializeNonLazyCollections(final SessionImplementor session,
                                          final QueryParameters queryParameters,
                                          final boolean returnProxies)
      throws HibernateException, SQLException {

      final PersistenceContext persistenceContext = session.getPersistenceContext();
      persistenceContext.beforeLoad();
      List result;
      try {
         result = doQuery( session, queryParameters, returnProxies );
      }
      finally {
         persistenceContext.afterLoad();
      }
      persistenceContext.initializeNonLazyCollections();
      return result;
   }


you will notice the this method returns a List. But the calling method doQueryAndInitializeNonLazyCollections does nothing with the returned List. And that is exactly the problem . Does anybody know where this Collection is returned to (Which member).? The Collection created in doQueryAndInitializeNonLazyCollections is correctly created. So I think to fix my problem, I need to know what happened to this List.

Thanks
Carel


Top
 Profile  
 
 Post subject:
PostPosted: Sun Sep 25, 2005 4:12 pm 
Newbie

Joined: Mon May 16, 2005 8:25 am
Posts: 7
Location: Hertford, UK
Finally I have found the answer to this one. Because our database table uses CHAR(10) fields in the composite key which are space padded, 2 versions of the object seem to be appearing internally inside hibernate.

Code:
      Parts myPart = new Parts();
      PartsId partKey = new PartsId();
      
      partKey.setPartnumber("PARENT");
      partKey.setSupplier  ("AMCE");      
      session.load(myPart, partKey);



Hibernate successfully retrieves the children and populates a collection with the child "Parts". But this is against an object with a key where the composite key fields have embedded spaces (trailing spaces even). The object we did a session.load, does not have the collection populated.

If you replace the code with

Code:
      Parts myPart = new Parts();
      PartsId partKey = new PartsId();
      
      partKey.setPartnumber("PARENT    ");
      partKey.setSupplier  ("AMCE      ");      
      session.load(myPart, partKey);




i.e. with the part number and supplier fields padded to CHAR(10), it all works fine.

I see from the JIRA Issue tracking that an alternative is to define a custom user type see HB-611. This seems to work fine too.

Alternatively if the myPart is loaded by using

Code:
      Parts myPart = new Parts();
      
      Query q = session.createQuery("select c from Parts as c where c.partid.partnumber = :PNO AND c.partid.supplier = :SUPPLIER");
      
      q.setString("PNO", "PARENT");
      q.setString("SUPPLIER", "AMCE");

      myPart = (Parts) q.uniqueResult();


This works too. I hope this helps someone else when they have the same mind block as we did!


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