-->
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: Mapping a Map
PostPosted: Fri Feb 03, 2006 6:31 am 
Newbie

Joined: Thu Feb 02, 2006 11:55 am
Posts: 4
Hello All,

I've been trying to map a Map. My own code did not work, so I've decided to use and example from the reference docs (http://www.hibernate.org/hib_docs/annot ... ollections)

However, I did not manage to get that that to work either (obviously, I had to fill in the classes, but I think I did not modify any of the persistence annotations). The 'Software' object is persisted, none of the 'Versions' are. I do not see any place in the table where the key of 'myVersion' (a String) would be saved.

Any ideas?

TIA,
Kofa

DB structure:
Code:
+----------------+
| Tables_in_test |
+----------------+
| software       |
| tbl_version    |
+----------------+
mysql> desc software;
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | bigint(20)   |      | PRI | NULL    | auto_increment |
| productName | varchar(255) | YES  |     | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+
mysql> desc tbl_version;
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | bigint(20)   |      | PRI | NULL    | auto_increment |
| codeName    | varchar(255) | YES  |     | NULL    |                |
| software_id | bigint(20)   | YES  | MUL | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+


Source:
Code:
@Entity
public class Software
{
    private Long myId;
    @Id @GeneratedValue (strategy=GenerationType.AUTO)
    public Long getId() {return myId;}
    private void setId(Long id) {myId = id;}

    Map<String, Version> myVersions;
    private String myProductName;
   
    @OneToMany(mappedBy="software")
    @MapKey(name="codeName")
    public Map<String, Version> getVersions() {return myVersions;}
    private void setVersions(Map<String, Version> versions) {myVersions = versions;}
   
    private Software() {super();}
    public Software(String productName)
    {
        myProductName = productName;
        myVersions = new HashMap<String, Version>();
    }
    public String getProductName() {return myProductName;}
    private void setProductName(String productName) {myProductName = productName;}
    public void addVersion(String code, Version v) {myVersions.put(code, v);}
}

@Entity
@Table(name="tbl_version")
public class Version
{
    private Long myId;
    @Id @GeneratedValue (strategy=GenerationType.AUTO)
    public Long getId() {return myId;}
    private void setId(Long id) {myId = id;}
    private String myCodeName;
    public String getCodeName() {return myCodeName;}
    public void setCodeName(String codeName) {myCodeName = codeName;}

    private Software mySoftware;

    @ManyToOne
    public Software getSoftware() {return mySoftware;}
    public void setSoftware(Software software) {mySoftware = software;}
   
    private Version() {super();}
    public Version(Software software, String codeName)
    {
        setSoftware(software); setCodeName(codeName);
    }
}


Hibernate version:
Hibernate 3.1.2 with Annotations 3.1beta-8

Code between sessionFactory.openSession() and session.close():
Code:
        Transaction tx = session.beginTransaction();
        session.persist(s);
        tx.commit();

:-)

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

The generated SQL (show_sql=true):
11:13:20,700 DEBUG SchemaExport:296 - drop table if exists Software
11:13:20,716 DEBUG SchemaExport:296 - drop table if exists tbl_version
11:13:20,716 DEBUG SchemaExport:296 - create table Software (id bigint not null auto_increment, productName varchar(255), primary key (id)) type=MyISAM
11:13:20,716 DEBUG SchemaExport:296 - create table tbl_version (id bigint not null auto_increment, codeName varchar(255), software_id bigint, primary key (id)) type=MyISAM
11:13:20,732 DEBUG SchemaExport:296 - alter table tbl_version add index FK2A21E55761CD8148 (software_id), add constraint FK2A21E55761CD8148 foreign key (software_id) references Software (id)
11:13:20,778 INFO SchemaExport:202 - schema export complete
Hibernate: /* insert hu.kofa.Software */ insert into Software (productName) values (?)
Code:


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 08, 2006 1:59 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
use cascade

_________________
Emmanuel


Top
 Profile  
 
 Post subject: Cascade
PostPosted: Wed Feb 08, 2006 3:19 pm 
Newbie

Joined: Thu Feb 02, 2006 11:55 am
Posts: 4
Emmanuel,

thanks for the reply. I've modified my code as follows:
Code:
    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER, mappedBy="software")
    @MapKey(name="codeName")
    public Map<String, Version> getVersions() {return myVersions;}


The CREATE TABLE SQL is now:
Code:
19:49:01,430 DEBUG SchemaExport:296 - create table Software (id bigint not null auto_increment, productName varchar(255), primary key (id)) type=MyISAM
19:49:01,493 DEBUG SchemaExport:296 - create table tbl_version (id bigint not null auto_increment, codeName varchar(255), software_id bigint, primary key (id)) type=MyISAM
19:49:01,508 DEBUG SchemaExport:296 - alter table tbl_version add index FK2A21E55761CD8148 (software_id), add constraint FK2A21E55761CD8148 foreign key (software_id) references Software (id)


So the database structure still does not contain the key of the map, only the value. Using 'cascade' resulted in the Version object actually being saved, but (as far as I can see) it's persisted as a bag.

I'm a bit confused about mapping a map, even when reading the XML-style configuration mapping. MapKey and map-key are supposed to specify the key column; however, MapKey expects the field specified by its 'name' parameter to be an attribute of the map's value class (with getter and setter methods) - shouldn't the name parameter be used to create a column in the value class's table (OneToMany) or join table and store the map's key value there?

Of course I may well be misunderstanding this whole thing, I'm new to O/R mapping.

TIA,
Kofa


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 09, 2006 9:39 am 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
reread the doc, the mapkey annotation use the codeName property present in Version as its key, so no need for extra column

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 09, 2006 10:21 am 
Newbie

Joined: Thu Feb 02, 2006 11:55 am
Posts: 4
Emmanuel,

thanks for the reply.

emmanuel wrote:
reread the doc, the mapkey annotation use the codeName property present in Version as its key, so no need for extra column


I don't see where the key of the map, a String, is stored. 'codeName' is a property of Version, but it's not the key of the map. (I specified 'codeName' as the MapKey as that's what the docs had.)

Suppose I have:
Code:
        SessionFactory sessions = createFactory();
   
        Software s = new Software("super software");
        s.addVersion("1.0", new Version(s, "buggy"));
        s.addVersion("1.1", new Version(s, "better"));
        Session session = sessions.openSession();
        Transaction tx = session.beginTransaction();

        session.persist(s);
       
        tx.commit();
        session.close();


I will not see "1.0" and "1.1" that I used as keys anywhere in my tables:
Code:
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| software       |
| tbl_version    |
+----------------+

mysql> select * from software;
+----+----------------+
| id | productName    |
+----+----------------+
|  1 | super software |
+----+----------------+
1 row in set (0.33 sec)

mysql> select * from tbl_version;
+----+----------+-------------+
| id | codeName | software_id |
+----+----------+-------------+
|  1 | better   |           1 |
|  2 | buggy    |           1 |
+----+----------+-------------+


What I see is an unordered collection, not a map:
there's 'super software', ID=1, that has version 'better' (ID=1) and version 'buggy' (ID=2).

Please let me know what I am missing.

TIA,
Kofa


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 10, 2006 6:50 am 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
The column codeName is used, there is not extra column. The key is actually a read only information since it is already set by targerEntity.setMapKeyTarget()

I'll update the doc to be clearer

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 13, 2006 5:24 am 
Newbie

Joined: Thu Feb 02, 2006 11:55 am
Posts: 4
This is what I'd like to achieve:
Code:
mysql> select * from software;
+----+----------------+
| id | productName    |
+----+----------------+
|  1 | super software |
+----+----------------+

mysql> select * from version;
+----+----------+
| id | codeName |
+----+----------+
|  1 | better   |
|  2 | buggy    |
+----+----------+

mysql> select * from software_version;
+-------------+------------+---------+
| software_id | version_id | version |
+-------------+------------+---------+
|           1 |          1 | 1.1     |
|           1 |          2 | 1.0     |
+-------------+------------+---------+


As you can see, the 'codeName' property of Version is not part of the map, and (more importantly) the "release numbers" (1.0, 1.1) used as the map's keys are stored in the database.

This is how I do it without annotations:
Code:
<hibernate-mapping package="hu.kofa">

    <class name="Software" table="software">
        <id name="id" type="long">
            <generator class="native" />
        </id>
        <property name="productName" />

        <map name="versions" table="software_version" cascade="all" lazy="false">
            <key column="software_id" />
            <index column="version" type="string" />
            <many-to-many column="version_id" class="Version" />
        </map>

    </class>

    <class name="Version" table="version">
        <id name="id" type="long">
            <generator class="native" />
        </id>
        <property name="codeName" />
    </class>

</hibernate-mapping>


How do I do this with annotations? Is it possible at all?

TIA,
Kofa


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 16, 2006 10:19 am 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
This is not currrently possible

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 17, 2006 2:42 pm 
Regular
Regular

Joined: Sat Nov 19, 2005 2:46 pm
Posts: 69
emmanuel wrote:
This is not currrently possible


Emmanuel: This is exactly what I need to know! (I'd rate your post if I could :-)
I've been trying to change my User to Role many-to-many join mapping from a Set to a Map, where "role_key" is a column in the table "role" but does not exist in the join table "user_role":
from:
Code:
<set name="roles" table="user_role" cascade="all">
  <key column="user_id"/>
  <many-to-many class="security.objects.Role" column="role_id"/>
</set>

to:
Code:
<map name="roles" table="user_role" cascade="all">
  <key column="user_id"/>
  <index column="role_key" type="string" />
  <many-to-many class="security.objects.Role" column="role_id" outer-join="auto"/>
</map>


Will it be possible in some future release?

Thank you,

_________________
Stewart
London, UK


Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 20, 2006 10:59 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
at some point, but I do have other priorities reagrding EJB3 compliance

_________________
Emmanuel


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.