10 16Hibernate之容器映射技術

Hibernate的開發之中主要分爲兩種模式:一種是基於*.hbm.xml文件的、另外一種是基於Annotation的配置,容器映射技術最早的時候出現在Hibernate時期,但是後來由於JPA的標準裏面沒有定義容器映射技術的相關操作,所以現在的開發之中已經很少再去使用容器映射。

Hibernate中主要分爲如下三種容器映射:
(1)Set容器映射:不能夠保存重複數據;
(2)List容器映射:可以保存重複數據,但是它爲了區分唯一,需要設置一個單獨的索引編號;
(3)Map容器映射:是保存key和value兩個數據。
所有的容器映射技術都要求其在兩張數據表中進行操作。

1 Set容器映射

Set容器映射主要是可以保存簡單的一對多關係,例如:在之前曾經實現過這樣一個功能:利用轉換器實現了一個用戶可以保存有多個email地址的信息,但是之前的做法比較複雜,而如果使用Set容器映射就可以利用兩張表的關係來描述。
範例:數據庫腳本

-- 刪除數據表
DROP TABLE IF EXISTS member;
DROP TABLE IF EXISTS email;
-- 創建數據表
CREATE TABLE member(
	mid VARCHAR(50),
	mname VARCHAR(50),
	CONSTRAINT pk_mid PRIMARY KEY(mid)
);
CREATE TABLE email(
	mid VARCHAR(50),
	etitle VARCHAR(50),
	CONSTRAINT fk_email_member FOREIGN KEY(mid) REFERENCES member(mid) ON DELETE CASCADE
);

由於一個用戶可以有多個email地址,所以在email表中定義外鍵的時候並沒有設置唯一約束。
範例:修改Member.java類

package org.lks.pojo;

import java.util.HashSet;
import java.util.Set;


@SuppressWarnings("serial")
public class Member implements java.io.Serializable {

	// Fields

	private String mid;
	private String mname;
	private Set<String> emails = new HashSet<String>();

	// Constructors

	/** default constructor */
	public Member() {
	}

	/** minimal constructor */
	public Member(String mid) {
		this.mid = mid;
	}

	// Property accessors

	public String getMid() {
		return this.mid;
	}

	public void setMid(String mid) {
		this.mid = mid;
	}

	public String getMname() {
		return this.mname;
	}

	public void setMname(String mname) {
		this.mname = mname;
	}

	public Set<String> getEmails() {
		return this.emails;
	}

	public void setEmails(Set<String> emails) {
		this.emails = emails;
	}

}

一個用戶有多個email地址,但是由於此時的email只有唯一的一個字段(不去考慮mid這個外鍵的關係)。

範例:手工維護Member.hbm.xml文件

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- 
    Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
    <class name="org.lks.pojo.Member" table="member" catalog="hedb">
        <id name="mid" type="java.lang.String">
            <column name="mid" length="50" />
            <generator class="assigned"></generator>
        </id>
        <property name="mname" type="java.lang.String">
            <column name="mname" length="50" />
        </property>
        <!-- 容器映射之中emails這個集合的操作對應的是email數據表 -->
        <set name="emails" table="email">
            <key><!-- email與member表的關聯靠的是mid這個外鍵 -->
                <column name="mid" length="50" />
            </key>
            <!-- 在進行集合操作的時候email表裏面對應的title屬性操作 -->
            <element type="java.lang.String" column="etitle"/>
        </set>
    </class>
</hibernate-mapping>

隨後如果進行保存或者更新等操作的時候會自動將Set集合中的數據同步到email表中的etitle字段中,同時外鍵數據會自動進行維護。
範例:增加數據

package org.lks.test;

import org.lks.dbc.HibernateSessionFactory;
import org.lks.pojo.Member;

public class TestMemberInsert {
	public static void main(String[] args) {
		Member vo = new Member();
		vo.setMid("3161301220");
		vo.setMname("lks");
		vo.getEmails().add("[email protected]");
		vo.getEmails().add("[email protected]");
		vo.getEmails().add("[email protected]");
		vo.getEmails().add("[email protected]");
		HibernateSessionFactory.getSession().save(vo);
		HibernateSessionFactory.getSession().beginTransaction().commit();
		HibernateSessionFactory.closeSession();
		System.exit(0);
	}
}

Hibernate: 
    insert 
    into
        hedb.member
        (mname, mid) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        email
        (mid, etitle) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        email
        (mid, etitle) 
    values
        (?, ?)

此時發現在進行數據增加的過程之中,用戶實際上只是進行了Member對象的操作,但是同時會根據自身的配置文件自動進行email數據表的操作,同時這個外鍵關係也自動設置上。
範例:數據更新——不會去考慮持久態下的數據更新

package org.lks.test;

import org.lks.dbc.HibernateSessionFactory;
import org.lks.pojo.Member;

public class TestMemberInsert {
	public static void main(String[] args) {
		Member vo = new Member();
		vo.setMid("3161301220");
		vo.setMname("lks");
		vo.getEmails().add("[email protected]");
		vo.getEmails().add("[email protected]");
		vo.getEmails().add("[email protected]");
		vo.getEmails().add("[email protected]");
		HibernateSessionFactory.getSession().update(vo);
		HibernateSessionFactory.getSession().beginTransaction().commit();
		HibernateSessionFactory.closeSession();
		System.exit(0);
	}
}

Hibernate: 
    update
        hedb.member 
    set
        mname=? 
    where
        mid=?
Hibernate: 
    delete 
    from
        email 
    where
        mid=?
Hibernate: 
    insert 
    into
        email
        (mid, etitle) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        email
        (mid, etitle) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        email
        (mid, etitle) 
    values
        (?, ?)

首先在此處先刪除掉了email表中已有的對應數據,而後重新進行數據的保存操作。

範例:數據刪除

package org.lks.test;

import org.lks.dbc.HibernateSessionFactory;
import org.lks.pojo.Member;

public class TestMemberInsert {
	public static void main(String[] args) {
		Member vo = new Member();
		vo.setMid("3161301220");
		HibernateSessionFactory.getSession().delete(vo);
		HibernateSessionFactory.getSession().beginTransaction().commit();
		HibernateSessionFactory.closeSession();
		System.exit(0);
	}
}

Hibernate: 
    select
        member_.mid,
        member_.mname as mname2_0_ 
    from
        hedb.member member_ 
    where
        member_.mid=?
Hibernate: 
    delete 
    from
        email 
    where
        mid=?
Hibernate: 
    delete 
    from
        hedb.member 
    where
        mid=?

如果現在使用delete()方法進行數據刪除的話,那麼會首先查詢出所有的數據,而後執行兩個刪除,一個是刪除email子表數據,另外一個刪除對應的member的父表數據。但是從實際的角度來看,如果真進行數據的刪除肯定使用Query接口完成,但是如果使用Query接口又會如何?
範例:數據刪除

package org.lks.test;

import org.hibernate.Query;
import org.lks.dbc.HibernateSessionFactory;

public class TestMemberDeleteQuery {
	public static void main(String[] args) {
		Query query = HibernateSessionFactory.getSession().createQuery("DELETE FROM Member WHERE mid=?");
		query.setParameter(0, "3161301220");
		query.executeUpdate();
		HibernateSessionFactory.closeSession();
		System.exit(0);
	}
}

Hibernate: 
    delete 
    from
        hedb.member 
    where
        mid=?

此時的程序只發出了刪除member表數據的一個刪除指令,這樣如果要想刪除子表,就只能夠依靠外鍵的級聯刪除完成了。

比較:關於delete()與Query刪除的區別?
(1)delete()刪除的時候會考慮到數據關係的維護,會首先發出查詢,而後先刪除子表再刪除父表,但是這樣的刪除太麻煩;
(2)Query刪除只是刪除一張表數據,不關心數據關聯的維護。

2 List容器映射

List容器映射與Set容器映射的最大區別只有一個:List中可以保存有重複數據,而Set不允許有重複。但是千萬要記住一點,List之所以能有重複,是因爲List裏面會自動維護一個索引關係,所以List集合裏面纔可以使用get()根據索引取出數據。
範例:數據庫創建腳本

-- 刪除數據表
DROP TABLE IF EXISTS member;
DROP TABLE IF EXISTS email;
-- 創建數據表
CREATE TABLE member(
	mid VARCHAR(50),
	mname VARCHAR(50),
	CONSTRAINT pk_mid PRIMARY KEY(mid)
);
CREATE TABLE email(
	mid VARCHAR(50),
	foot INT,
	etitle VARCHAR(50),
	CONSTRAINT fk_email_member FOREIGN KEY(mid) REFERENCES member(mid) ON DELETE CASCADE
);

此時的foot字段描述的是List集合中的索引數值。
(1)索引【foot】 = 0、內容 = [email protected]
(2)索引 【foot】= 1、內容 = [email protected]
範例:修改Member.java類

package org.lks.pojo;

import java.util.ArrayList;
import java.util.List;


@SuppressWarnings("serial")
public class Member implements java.io.Serializable {

	// Fields

	private String mid;
	private String mname;
	private List<String> emails = new ArrayList<String>();
	
	public List<String> getEmails() {
		return this.emails;
	}

	public void setEmails(List<String> emails) {
		this.emails = emails;
	}

}

範例:修改Member.hbm.xml文件

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- 
    Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
    <class name="org.lks.pojo.Member" table="member" catalog="hedb">
        <id name="mid" type="java.lang.String">
            <column name="mid" length="50" />
            <generator class="assigned"></generator>
        </id>
        <property name="mname" type="java.lang.String">
            <column name="mname" length="50" />
        </property>
        <list name="emails" table="email" catalog="hedb">
        	<key><!-- email與member表的關聯靠的是mid這個外鍵 -->
                <column name="mid" length="50" />
            </key>
            <!-- 負責處理數據的索引數值 -->
            <index column="foot"/>
            <element type="java.lang.String" column="etitle"/>
        </list>
    </class>
</hibernate-mapping>

範例:增加操作

package org.lks.test;

import org.lks.dbc.HibernateSessionFactory;
import org.lks.pojo.Member;

public class TestMemberInsert {
	public static void main(String[] args) {
		Member vo = new Member();
		vo.setMid("3161301220");
		vo.setMname("lks");
		vo.getEmails().add("[email protected]");
		vo.getEmails().add("[email protected]");
		vo.getEmails().add("[email protected]");
		vo.getEmails().add("[email protected]");
		HibernateSessionFactory.getSession().save(vo);
		HibernateSessionFactory.getSession().beginTransaction().commit();
		HibernateSessionFactory.closeSession();
		System.exit(0);
	}
}

Hibernate: 
    insert 
    into
        hedb.member
        (mname, mid) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        hedb.email
        (mid, foot, etitle) 
    values
        (?, ?, ?)
Hibernate: 
    insert 
    into
        hedb.email
        (mid, foot, etitle) 
    values
        (?, ?, ?)
Hibernate: 
    insert 
    into
        hedb.email
        (mid, foot, etitle) 
    values
        (?, ?, ?)
Hibernate: 
    insert 
    into
        hedb.email
        (mid, foot, etitle) 
    values
        (?, ?, ?)

此時重複的數據的確進行了有效的保存,但是發現不同數據之中的foot字段的內容是不同的。

3 Map容器映射

既然說到了Map映射,那麼很明顯,與List或Set最大的不同一定是在子表裏面要保存有key和value兩個數據,所以首先必須對數據表結構進行修改。
範例:數據庫腳本

-- 刪除數據表
DROP TABLE IF EXISTS member;
DROP TABLE IF EXISTS email;
-- 創建數據表
CREATE TABLE member(
	mid VARCHAR(50),
	mname VARCHAR(50),
	CONSTRAINT pk_mid PRIMARY KEY(mid)
);
CREATE TABLE email(
	mid VARCHAR(50),
	etitle VARCHAR(50),
	ealias VARCHAR(50),
	CONSTRAINT fk_email_member FOREIGN KEY(mid) REFERENCES member(mid) ON DELETE CASCADE
);

每一個email地址都做一個備註的操作信息。
範例:進行Member.java的修改

package org.lks.pojo;

import java.util.HashMap;
import java.util.Map;


@SuppressWarnings("serial")
public class Member implements java.io.Serializable {

	// Fields

	private String mid;
	private String mname;
	private Map<String,String> emails = new HashMap<String,String>();
	
	public Map<String,String> getEmails() {
		return this.emails;
	}

	public void setEmails(Map<String,String> emails) {
		this.emails = emails;
	}

}

隨後現在還需要找到Member.hbm.xml文件進行對應的修改操作。
範例:修改Member.hbm.xml文件

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- 
    Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
    <class name="org.lks.pojo.Member" table="member" catalog="hedb">
        <id name="mid" type="java.lang.String">
            <column name="mid" length="50" />
            <generator class="assigned"></generator>
        </id>
        <property name="mname" type="java.lang.String">
            <column name="mname" length="50" />
        </property>
        <map name="emails" table="email" catalog="hedb">
        	<key>
        		<column name="mid" length="50"/>
        	</key>
        	<!-- 描述Map集合中的key對應的數據關係 -->
        	<map-key type="java.lang.String" column="etitle"/>
        	<!-- 描述Map集合中的value對應的數據關係 -->
        	<element type="java.lang.String" column="ealias"/>
        </map>
    </class>
</hibernate-mapping>

範例:測試數據增加

package org.lks.test;

import org.lks.dbc.HibernateSessionFactory;
import org.lks.pojo.Member;

public class TestMemberInsert {
	public static void main(String[] args) {
		Member vo = new Member();
		vo.setMid("3161301220");
		vo.setMname("lks");
		vo.getEmails().put("[email protected]","工作郵箱1");
		vo.getEmails().put("[email protected]","工作郵箱2");
		vo.getEmails().put("[email protected]","個人郵箱");
		vo.getEmails().put("[email protected]","家庭郵箱");
		HibernateSessionFactory.getSession().save(vo);
		HibernateSessionFactory.getSession().beginTransaction().commit();
		HibernateSessionFactory.closeSession();
		System.exit(0);
	}
}

Hibernate: 
    insert 
    into
        hedb.member
        (mname, mid) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        hedb.email
        (mid, etitle, ealias) 
    values
        (?, ?, ?)
Hibernate: 
    insert 
    into
        hedb.email
        (mid, etitle, ealias) 
    values
        (?, ?, ?)

在進行email數據保存的過程之中,也是自動維護了key(etitle列)與value(ealias列)的數據。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章