Dashboard > OpenSource Project > ... > Hibernate 3入門 > Bag
OpenSource Project Log In   View a printable version of the current page.
Bag
Added by zach14c, last edited by zach14c on Sep 01, 2005
Labels: 
(None)

Bag是集合,與Set不同的是,Bag允許重複的元素,在Java的標準API中並沒有提供Bag容器,Hibernate提供自己的Bag實現,允許您將List映射為Bag。

您可以如下定義User類別,其中的List成員將被用作Bag來使用,而不管物件在List容器中的順序:

User.java
package onlyfun.caterpillar;

import java.util.List;

public class User {
    private Integer id;
    private String name;
    private List items;

    // 必須要有一個預設的建構方法
    // 以使得Hibernate可以使用Constructor.newInstance()建立物件
    public User() {
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List getItems() {
        return items;
    }

    public void setItems(List items) {
        this.items = items;
    }

    public void addItem(String item) {
        items.add(item);
    }
    
    public void removeItem(String name) {
        items.remove(name);
    }
}

最簡單的Bag映射是使用<bag>標籤,在這之前,假設您如下建立表格:

CREATE TABLE user (
    id INT(11) NOT NULL auto_increment PRIMARY KEY,
    name VARCHAR(100) NOT NULL default ''
);

CREATE TABLE item (
    id INT(11) NOT NULL,
    name VARCHAR(100) NOT NULL
);

接著定義映射文件,如下所示:

User.hbm.xml
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE hibernate-mapping 
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 

<hibernate-mapping> 
    <class name="onlyfun.caterpillar.User" table="user"> 

        <id name="id" column="id" type="java.lang.Integer"> 
            <generator class="native"/> 
        </id> 

        <property name="name" 
                  column="name" 
                  type="java.lang.String"/> 
       
        <bag name="items" table="item"> 
            <key column="id"/> 
            <element column="name" type="java.lang.String"/> 
        </bag> 
        
    </class> 
</hibernate-mapping>

假設您如下儲存物件:

User user1 = new User();
user1.setItems(new ArrayList());
user1.setName("caterpillar");
user1.addItem("Java Gossip");
user1.addItem("Java Gossip");
user1.addItem("Caxxx A80");
        
User user2 = new User();
user2.setItems(new ArrayList());
user2.setName("momor");
user2.addItem("Snoppy world");        
        
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(user1);
session.save(user2);
tx.commit();
session.close();

則資料庫中會有如下的資料:

mysql> select * from user;
+----+-------------+
| id | name        |
+----+-------------+
|  1 | caterpillar |
|  2 | momor       |
+----+-------------+
2 rows in set (0.00 sec)

mysql> select * from item;
+----+-------------------+
| id | name              |
+----+-------------------+
|  1 | Java Gossip       |
|  1 | Java Gossip       |
|  1 | Caxxx A80         |
|  2 | Snoppy world      |
+----+-------------------+
4 rows in set (0.00 sec)

您可以如下更新資料:

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
User user = (User) session.load(User.class, new Integer(1));
user.removeItem("Java Gossip");
tx.commit();
session.close();

然而注意觀察在更新資料時所使用的SQL:

Hibernate: delete from item where id=?
Hibernate: insert into item (id, name) values (?, ?)
Hibernate: insert into item (id, name) values (?, ?)

由於Bag的資料允許重複,當必須更新資料時,無法確定要更新的是哪一筆資料,因而採取的方式是刪除集合物件對應的所有資料,然後重新將集合物件中的資料寫入資料庫,顯然的這種作法相當的沒有效率。

作為Bag的一種擴充,Hibernate提供idbag,藉由在定義Bag映射時加上"collection-id",讓Hibernate可以直接確定所要更新的資料,提高資料庫操作的效率,您可以先如下建立表格:

CREATE TABLE user (
    id INT(11) NOT NULL auto_increment PRIMARY KEY,
    name VARCHAR(100) NOT NULL default ''
);

CREATE TABLE item (
    cid CHAR(32) NOT NULL,
    id INT(11) NOT NULL,
    name VARCHAR(100) NOT NULL
);

其中item表格的cid就用於資料更新時定位之用,接著在映射文件中使用<idbag>標籤加以定義:

User.hbm.xml
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE hibernate-mapping 
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 

<hibernate-mapping> 
    <class name="onlyfun.caterpillar.User" table="user"> 

        <id name="id" column="id" type="java.lang.Integer"> 
            <generator class="native"/> 
        </id> 

        <property name="name" 
                  column="name" 
                  type="java.lang.String"/> 
       
        <idbag name="items" table="item"> 
            <collection-id column="cid" type="java.lang.String">
                <generator class="uuid.hex"/>
            </collection-id>
            <key column="id"/>            
            <element column="name" type="java.lang.String"/> 
        </idbag> 
        
    </class> 

</hibernate-mapping>

使用上面用過的程式片段來儲存物件的話,資料庫中會有如下的資料:

mysql> select * from user;
+----+-------------+
| id | name        |
+----+-------------+
|  1 | caterpillar |
|  2 | momor       |
+----+-------------+
2 rows in set (0.00 sec)

mysql> select * from item;
+----------------------------------+------+--------------------+
| cid                              | id   | name               |
+----------------------------------+------+--------------------+
| 297eba61056726030105672605df0001 |  1   | Java Gossip        |
| 297eba61056726030105672605df0002 |  1   | Java Gossip        |
| 297eba61056726030105672605df0003 |  1   | Caxxx A80          |
| 297eba61056726030105672605df0004 |  2   | Snoppy world       |
+----------------------------------+------+--------------------+
4 rows in set (0.00 sec)

如果使用上面提到過的程式片段來更新物件的話,則實際上Hibernate會使用以下的SQL來進行更新:

Hibernate: delete from item where cid=?

這一次並不是整個刪除集合中的資料,而是直接藉由cid來確定所要更新的資料,比起只使用Bag,idbag的效率好了許多。

Site powered by a free Open Source Project / Non-profit License (more) of Confluence - the Enterprise wiki.
Learn more or evaluate Confluence for your organisation.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.1.5a Build:#411 Mar 16, 2006) - Bug/feature request - Contact Administrators