-->
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.  [ 5 posts ] 
Author Message
 Post subject: composite-id tag generates parse error
PostPosted: Mon Oct 29, 2007 1:18 pm 
Newbie

Joined: Mon Oct 29, 2007 12:24 pm
Posts: 10
I have a schema in sqlserver that utilizes a composite key, so I'd like to use the composite-id tag in my .hbm.xml mapping file, but it generates a parse error.

Here is my .hbm.xml file, which is going against the northwind db in sqlserver 2000:

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>

    <class name="OrderDetail" table="Order Details">

        <composite-id name="comp_id" class = "java.lang.String">
      <key-property name="orderid" column="OrderID"/>
      <key-property name="productid" column="ProductID"/>
   </composite-id>
        <property name="unitprice" column="UnitPrice" type="big_decimal" />
   <property name="qty" column="Quantity" type="short" />
   <property name="discount" column="Discount" />
    </class>
</hibernate-mapping>


The generated error is:

org.hibernate.InvalidMappingException: could not parse mapping document


Top
 Profile  
 
 Post subject:
PostPosted: Mon Oct 29, 2007 5:23 pm 
Newbie

Joined: Mon Oct 29, 2007 12:24 pm
Posts: 10
Well, here is the solution.

First off, although I don't believe it is explicitly stated in the docs (I could be wrong, but I didn't see it), you MUST have a name and a class declared for the composite-id.

Secondly, that class that is declared in the composite-id tag MUST have get and set methods for any key-properties you declare in the composite-id.

Thirdly (and this IS in the docs), your class must implement java.io.Serializable.

This means that you will need to create a custom class for any composite id you wish to use in your mappings, but if you follow these rules, your mappings should work.

Here is my new .hbm.xml file

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>
    <class name="OrderDetail" table="Order Details">

        <composite-id name="id" class = "OrderDetailID">
             <key-property name="orderid" column="OrderID"/>
             <key-property name="productid" column="ProductID"/>
        </composite-id>
        <property name="unitprice" column="UnitPrice" type="big_decimal" />
        <property name="qty" column="Quantity" type="short" />
        <property name="discount" column="Discount" />
    </class>
</hibernate-mapping>


In the mapping above, OrderDetailID is my custom class which has get and set methods for orderid and productid. Here is the class code:

Code:
public class OrderDetail {
   
    /** Creates a new instance of OrderDetail */
    public OrderDetail() {
    }
    public OrderDetailID getId() {
        return new OrderDetailID();
    }
    public void setId(OrderDetailID value) {
       
    }
    public double getUnitprice() {
        return 1.00;
    }
    public void setUnitprice(double value) {
       
    }
    public short getQty() {
        return 1;
    }
    public void setQty(short value) {
       
    }
    public double getDiscount() {
        return 1.00;
    }
    public void setDiscount(double value) {
       
    }
   
}


You'll notice that this is an incredibly sloppy bit of code, but I just wanted my initialization to work. In a working environment I would implement the get and set functions properly, and put the class into a proper namespace, but this is just to show you the naming convention and the datatype relationships between the class and the mapping.

Also, in a working version of the class, you should implement the compare and hash methods as specified in the docs here:

http://www.hibernate.org/hib_docs/reference/en/html/components.html

Anyhow, I hope this helps anyone stuck trying to do the same thing I did.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Oct 30, 2007 9:02 am 
Beginner
Beginner

Joined: Tue Oct 30, 2007 7:57 am
Posts: 47
The problem in your original mapping file, is that you put a name and a class for the composite-id, which you shoul not (unless you want a sepparate class to keep the id). For example, you may use:

Code:
    <class name="ModelOption" table="MODEL_OPTION">
      <composite-id>
         <key-property name="option" type="string" column="OPTION_ID"/>
         <key-property name="model" type="string" column="MODEL_ID"/>
      </composite-id>
      <property name="required" column="REQUIRED" type="boolean" not-null="true"/>
    </class>


Top
 Profile  
 
 Post subject:
PostPosted: Tue Oct 30, 2007 7:34 pm 
Newbie

Joined: Mon Oct 29, 2007 12:24 pm
Posts: 10
Rober2D2 wrote:
The problem in your original mapping file, is that you put a name and a class for the composite-id, which you shoul not (unless you want a separate class to keep the id).


I don't think that is true. I only posted one of the mapping files I tried. I tried many combinations, including the method that you suggest. I am certain that, per my post above, you MUST have both a name AND a class attribute. Is the model you posted something that you have actually gotten to work?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 31, 2007 8:46 am 
Beginner
Beginner

Joined: Tue Oct 30, 2007 7:57 am
Posts: 47
xgeoff wrote:
Rober2D2 wrote:
The problem in your original mapping file, is that you put a name and a class for the composite-id, which you shoul not (unless you want a separate class to keep the id).


I don't think that is true. I only posted one of the mapping files I tried. I tried many combinations, including the method that you suggest. I am certain that, per my post above, you MUST have both a name AND a class attribute. Is the model you posted something that you have actually gotten to work?


In that example I am using a key-many-to-one, because it is a relations table. Below, I put a working example, but before that, something I forgot about your code. If you use this:

Code:
<composite-id name="comp_id" class = "java.lang.String">


,you are telling hibernate to look for a field named comp_id, which is of the class java.lang.String, which has two fields inside (Hibernate can't find the field comp_id inside your class, so there is a parse error)

Apart from that, is a good idea to have a separate class for a composite primary key, but this is a design decision.


The working example.

Mapping file:
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 package="example">
   
    <class name="CompositeClass" table="COMPOSITE_CLASS">
      <composite-id>
         <key-property name="key1" type="string" column="KEY_1"/>
         <key-property name="key2" type="string" column="KEY_2"/>
      </composite-id>
      <property name="data" column="DATA" type="string"/>
    </class>   

</hibernate-mapping>


The java class. Notice that it implements Serializable and overrides equals and hashCode. That is necessary for composite-id.

Code:
package example;

import java.io.Serializable;

public class CompositeClass implements Serializable {

   private String key1;
   private String key2;
   private String data;
   /**
    * @return the key1
    */
   public String getKey1() {
      return key1;
   }
   /**
    * @param key1 the key1 to set
    */
   public void setKey1(String key1) {
      this.key1 = key1;
   }
   /**
    * @return the key2
    */
   public String getKey2() {
      return key2;
   }
   /**
    * @param key2 the key2 to set
    */
   public void setKey2(String key2) {
      this.key2 = key2;
   }
   /**
    * @return the data
    */
   public String getData() {
      return data;
   }
   /**
    * @param data the data to set
    */
   public void setData(String data) {
      this.data = data;
   }
   
   @Override
   public boolean equals(Object object) {
      if (object == null) return false;
      if (!(object instanceof CompositeClass)) return false;
      CompositeClass compositeClass = (CompositeClass) object;
      return this.toString().equals(compositeClass.toString());
   }
   /* (non-Javadoc)
    * @see java.lang.Object#toString()
    */
   @Override
   public String toString() {
      return this.key1 + "-" + this.key2;
   }
   /* (non-Javadoc)
    * @see java.lang.Object#hashCode()
    */
   @Override
   public int hashCode() {
      String cadena = this.toString();
      return cadena.hashCode();
   }   
   
}


The java script:

Code:

package example;

import org.hibernate.Session;
import util.HibernateUtil;

public class Main {

   /**
    * @param args
    */
   public static void main(String[] args) {
      Session sesion = HibernateUtil.getSessionFactory().openSession();      
      sesion.beginTransaction();

      CompositeClass compositeClass = new CompositeClass();
      compositeClass.setKey1("Key1");
      compositeClass.setKey2("Key2");
      compositeClass.setData("Data");
      sesion.save(compositeClass);
      
      sesion.getTransaction().commit();
      sesion.close();

   }

}


And the hibernate.cfg.xml file

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>

        <!-- Database connection settings -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/example</property>
        <property name="connection.username">root</property>
        <property name="connection.password">password</property>

        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">1</property>

        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>

        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>

        <!-- Disable the second-level cache  -->
        <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>

        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>

        <!-- Drop and re-create the database schema on startup -->
        <property name="hbm2ddl.auto">create</property>
          
   <mapping resource="example/CompositeClass.hbm.xml"/>      

    </session-factory>

</hibernate-configuration>


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