-->
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.  [ 11 posts ] 
Author Message
 Post subject: Mapping a composite key one-to-many relationship
PostPosted: Tue Aug 24, 2004 9:06 am 
Newbie

Joined: Tue Aug 24, 2004 8:41 am
Posts: 15
Location: Amersfoort, the Netherlands
Hibernate version 2.1

Name and version of the database you are using: MySQL 4.1.3

Hi everyone,

I need to know how to write a mapping for the following problem:

- Every product in our application has multiple title objects, one per locale. This is stored in a map.

- These title objects themselves contain a map, every key (TitleType) is an object, however, for simplicity, this could be seen as a string. The value is also a string.

- Besides this, title objects are not only in products, but also in other classes. The titles from product should be stored in productTitles, while tiles from another class should be stored in <class>Titles, etc...

This structure enables us to define mutiple 'titles' in mutliple languages.

An example database (ProductTitles) could be:

Code:
productID | Locale | TitleType | Value

1           en       title       Beatiful product
1           en       desc        This is a nice product
2           nl       desc        Dit is een leuk product


Creating a class that is a composite key was a thought, however, since title will be used a lot, it would be good to go easy on the key-objects, since many of these would have to be loaded into memory.

Can someone show me some hibernate mapping xml how this could be done. I figured I could need to build a component in a component or something, but its not getting any clearer for me.

Thanks in advance,
Vincent

Some code

product.hbm.xml
Code:
<hibernate-mapping package="test">

    <class name="Product">
        <id name="id" column="ProductID">
            <generator class="native"/>
        </id>
       
        <property name="code" length="16"/>
       
        <map name="titles">
<!--           ... help !!!!  -->
        </map>
        <property name="price" type="test.persistence.PriceCompositeUserType">
            <column name="priceValue" not-null="true"/>
            <column name="priceCurrency" not-null="true" length="3"/>
            <column name="priceQtyAmount" not-null="true"/>
            <column name="priceQtyUnit" not-null="true" length="16"/>
        </property>
    </class>
   
</hibernate-mapping>



Product.java
Code:
  ...
   private Map _titles;

    /**
     * Set the title map.
     *
     * See {@link TitleDescriptor}
     **/
    public void setTitles(Map titles) {
        _titles = titles;
    }
   
    /**
     * Get the title hashmap. By default a hashmap is returned, though
     *
     * See {@link TitleDescriptor}
     **/
    public Map getTitles() {
        return _titles;       
    }
...   

TitleDescriptor.java
Code:
..
public class TitleDescriptor {

   
    // Locale (language) in which the titles should be supplied in
    private final Locale _locale;
   
    // Mapping of title types to titles, i.e. Map <Title, String>
    private final Map _titles;
   
    /** Creates a new TitleDescriptor with the supplied Locale. */
    public TitleDescriptor(Locale locale) {
        _locale = locale;
        _titles = new HashMap();
    }
   
    /** Retrieves the Locale. */
    public Locale getLocale() {
        return _locale;
    }
       
    /** Returns the corresponding title for the supplied variant. */
    public String getTitle(TitleVariant variant) {
        return (String) _titles.get(variant);
    }
 
..   


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 24, 2004 9:50 am 
Regular
Regular

Joined: Wed Aug 18, 2004 5:16 am
Posts: 69
Location: Modena, Italy
If other objects can have a title why you eliminate the productID column from the titles table and map the association with a many-to-many?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 24, 2004 9:56 am 
Newbie

Joined: Tue Aug 24, 2004 8:41 am
Posts: 15
Location: Amersfoort, the Netherlands
Teg wrote:
If other objects can have a title why you eliminate the productID column from the titles table and map the association with a many-to-many?


Well, the architecture requires one entity class (Product, Order, etc...) to have a seperate title table (ProductTitle, OrderTitle), for speed and scalability. For now the product is pretty limited, but it should be able to handle hundreds of thousends of products, all with titles, maybe in multiple languages, and many thousends of orders.

So instead of the tables Product, Order and Title the best option seems to be Product, Order and ProductTitle and OrderTitle, in which both Title tables have the same class. It also seperates different titles for eachother.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 24, 2004 10:41 am 
Regular
Regular

Joined: Wed Aug 18, 2004 5:16 am
Posts: 69
Location: Modena, Italy
If you want a relation of the type:
Product has many Titles and Title has many Values
you have to create 3 tables, a Product table, a Title table bound to a Product and a Value table bound to a Title. This approach can easily mapped to a OO model.

I suggest to use always generated id instead of composit-id or assigned id so you can benefit the power of Hibernate. Otherwise you have to override equals, hashCode and manage save and update.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 24, 2004 11:13 am 
Newbie

Joined: Tue Aug 24, 2004 8:41 am
Posts: 15
Location: Amersfoort, the Netherlands
Teg wrote:
If you want a relation of the type:
Product has many Titles and Title has many Values
you have to create 3 tables, a Product table, a Title table bound to a Product and a Value table bound to a Title. This approach can easily mapped to a OO model.

I suggest to use always generated id instead of composit-id or assigned id so you can benefit the power of Hibernate. Otherwise you have to override equals, hashCode and manage save and update.


Overwring equals and hashCode is not a big problem. I think a link table would not really be nessesary, because the link table would (in my opinion) look like this:

Product_ProductTitle
Code:
ProdToProdTitleId | productID | Locale | Type

3                   2           en       desc
4                   2           nl       desc


And ProductTitle would look like this:
Code:
ProdToProdTitleId | Value

3                   Hello
4                   Hallo


To me that seems like redundence, because every (ProductID, Locale, Type) is unique. It would cost me an additional join and an additional table. Also additional complexity.

Thanks,
Vincent


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 24, 2004 11:43 am 
Regular
Regular

Joined: Wed Aug 18, 2004 5:16 am
Posts: 69
Location: Modena, Italy
Then try somthing like this:

Code:
<class name="Product">
        <id name="id" column="ProductID">
            <generator class="native"/>
        </id>
       
        <property name="code" length="16"/>
       
        <map name="titles" table="ProductTitles">
            <key column="productID"/>
            <index column="Locale" type="string"/>
            <composite-element class="TitleType">
                <property name="titleType" column="TitleType" type="string"/>
                <property name="value" column="Value" type="string"/>
            </composite-element>
        </map>
        <property name="price" type="test.persistence.PriceCompositeUserType">
            <column name="priceValue" not-null="true"/>
            <column name="priceCurrency" not-null="true" length="3"/>
            <column name="priceQtyAmount" not-null="true"/>
            <column name="priceQtyUnit" not-null="true" length="16"/>
        </property>
    </class>


I hope to have understood your case


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 25, 2004 3:33 am 
Newbie

Joined: Tue Aug 24, 2004 8:41 am
Posts: 15
Location: Amersfoort, the Netherlands
Teg wrote:
Then try somthing like this:

Code:
..
        <map name="titles" table="ProductTitles">
            <key column="productID"/>
            <index column="Locale" type="string"/>
            <composite-element class="TitleType">
                <property name="titleType" column="TitleType" type="string"/>
                <property name="value" column="Value" type="string"/>
            </composite-element>
        </map>
..
    </class>


I hope to have understood your case


Yes, thats getting somewhere. Now I need to map value too. I figured it would be something like this, except that I think it maps to another table, and I want everything in ProductTitles

Code:
<map name="titles" table="ProductTitles">
  <key column="productID"/>
  <index column="locale"/>
  <element class="TitleDescriptor">
     <map name="title">
         <index column="titleType" type="string"/>
          <!-- the value is a string -->
     </map>
   </element>
</map>


Any idea how to resolve the inner map to a composite element?

Thanks so far,
Vincent


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 25, 2004 4:22 am 
Regular
Regular

Joined: Wed Aug 18, 2004 5:16 am
Posts: 69
Location: Modena, Italy
This can help to load data:

Code:
public class Product {

    private Set titles = new HashSet(); // Set of ProductTitle
   
    public Product() {
    }
   
    public Set getTitles() {
        return titles;
    }
   
    public void setTitles(Set titles) {
        this.titles = titles;
    }
   
}

public class ProductTitle {
   
    private long productID;
    private String locale;
    private String type;
    private String value;
   
    public TitleDescription() {
    }

    public long getProductID() {
        return productID;
    }

    public void setProductID(long productID) {
        this.productID = productID;
    }
   
    public String getLocale() {
        return locale;
    }
   
    public void setLocale(String locale) {
        this.locale = locale;
    }
   
    public String getType() {
        return type;
    }
   
    public void setType(String type) {
        this.type = type;
    }
   
    public String getValue() {
        return value;
    }
   
    public void setValue(String value) {
        this.value = value;
    }
   
}

Code:
    <class name="Product" table="Products">
        <id name="id" column="ProductID">
            <generator class="native"/>
        </id>
       
        <property name="code" length="16"/>
       
        <set name="titles">
            <key column="productID"/>
            <one-to-many class="ProductTitle"/>
        </set>
       
        <property name="price" type="test.persistence.PriceCompositeUserType">
            <column name="priceValue" not-null="true"/>
            <column name="priceCurrency" not-null="true" length="3"/>
            <column name="priceQtyAmount" not-null="true"/>
            <column name="priceQtyUnit" not-null="true" length="16"/>
        </property>
    </class>

    <class name="ProductTitle" table="ProductTitles">
        <composite-id >
            <key-property name="productID"/>
            <key-property name="locale" column="Locale"/>
            <key-property name="type" column="TitleType"/>
        </composite-id>
       
        <property name="value" column="Value"/>
    </class>


Then you can create a method in Product to expose internal data as maps


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 25, 2004 4:47 am 
Newbie

Joined: Tue Aug 24, 2004 8:41 am
Posts: 15
Location: Amersfoort, the Netherlands
Hi Teg,

Thanks! I guess that would be a simpler solution. That might work too since there are at most 16 items in that set. There is just one thing though, besides a ProductTitle class there will also be an OrderTitle and probably some more.

These classes are all basically the same. It is possible to write one Title class yet create two different tables that both use the same class? I know I could create a 'TitleBase' and extend that into a ProductTitle and a OrderTitle, but those bodies would be empty since all the needed logic is in TitleBase. It would be simpler to create two class definitions using the same class, but I'm not sure whether that is possible.

Thanks so far,
Vincent


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 25, 2004 5:09 am 
Regular
Regular

Joined: Wed Aug 18, 2004 5:16 am
Posts: 69
Location: Modena, Italy
How can Hibernate know from which table load data if you map the same class on more tables?
In HQL you use the class name, not the table name, then I think this is not possible.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 25, 2004 5:40 am 
Newbie

Joined: Tue Aug 24, 2004 8:41 am
Posts: 15
Location: Amersfoort, the Netherlands
Teg wrote:
How can Hibernate know from which table load data if you map the same class on more tables?
In HQL you use the class name, not the table name, then I think this is not possible.


It can't. Only through the association. Product -> ProductTitle and Order -> OrderTitle. I guess I'll do the subclass thing.


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