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列)的数据。

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