Eclipse快速上手Hibernate--4. 繼承映射(1)

Eclipse快速上手Hibernate--4. 繼承映射(1)<script language="javascript" type="text/javascript"> document.title="Eclipse快速上手Hibernate--4. 繼承映射(1) - "+document.title </script>
   前面的《Eclipse快速上手Hibernate--1. 入門實例 》等三篇文章已經談了Hibernate的入門以及利用工具創建的方法。這篇文章主要說說在Hibernate中的繼承映射。相關配置請參考前三篇文章
 
   如果程序中的對象含有繼承的關係,在Hibernate中有以下三種策略將這種關係映射到數據表上:
· 每個類層次結構一個表(table per class hierarchy)
· 每個子類一個表(table per subclass)
· 每個具體類一個表(table per concrete class)(有一些限制)
 
   每個類層次結構一個表的方式是將所有繼承同一父類別的對象儲存在同一個表格中,爲了做到這一點,需要在表格中使用識別字段來表示某一列(row)是屬於某個子類別或父類別,在這個主題中我們將先說明這個方法。
 
1. 創建項目
 
·  新建一個Java項目:InheritanceMapping,注意選中“創建單獨的源文件夾和輸出文件夾”,同時添加“用戶庫”:hibernate。 
 
 
2. 編寫類文件
 
·  新建一個類,包名:javamxj.inheritance.one,類名:Animal。然後在生成的代碼中添加變量,再利用“生成 Getter 和 Setter”,具體方式同《Eclipse快速上手Hibernate--1. 入門實例 》文章中的編輯User.java的方式一樣。

Animal.java

/*
 * Hibernate - 繼承映射(每個類層次一個表)
 * 創建日期 2005-4-9
 * @author javamxj(分享java快樂)
 * @link  Blog: htpp://javamxj.mblogger.cn  
 *              htpp://blog.csdn.net/javamxj/ 
 */
package javamxj.inheritance.one;

/**
 * @hibernate.class 
 *   table="Animal" 
 *   discriminator-value="Animal"
 * @hibernate.discriminator 
 *   column="ANIMAL_TYPE" 
 *   type="string" 
 *   length = "10"
 */
public abstract class Animal {
	private Long id;

	private String name;

	/**
	 * @hibernate.id 
	 *   column="ID" 
	 *   generator-class="hilo" 
	 *   unsaved-value="null"
	 */
	public Long getId() {
		return id;
	}

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

	/**
	 * @hibernate.property 
	 *   length = "24"
	 */
	public String getName() {
		return name;
	}

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

	public abstract void makeSound();
}
 ·  這個類是父類,值得注意是在類層次標記中添加了一個discriminator標記,並用它定義了一個字段“ANIMAL_TYPE”,這個字段就是用來識別某一列(row)是屬於某個子類別或父類別的。
 
 
·  子類Cat.java
Cat.java
package javamxj.inheritance.one;

/**
 * @hibernate.subclass 
 *   discriminator-value="Cat"
 */
public class Cat extends Animal {
	private String FurColor;

	public void makeSound() {
		System.out.println("喵喵");
	}

	/**
	 * @hibernate.property 
	 *   length = "24"
	 */
	public String getFurColor() {
		return FurColor;
	}

	public void setFurColor(String furColor) {
		FurColor = furColor;
	}
}
 
·  子類Dog.java
Dog.java
package javamxj.inheritance.one;

/**
 * @hibernate.subclass 
 *   discriminator-value="Dog"
 */
public class Dog extends Animal {
	private String category;

	public void makeSound() {
		System.out.println("汪汪");
	}

	/**
	 * @hibernate.property 
	 *   length = "24"
	 */
	public String getCategory() {
		return category;
	}

	public void setCategory(String category) {
		this.category = category;
	}
}
·  這兩個子類都很簡單,注意添加的hibernate.subclass的標記,指定其識別字段。
 
 
3. 構建文件Build.xml
 
·  在項目根目錄下建立一個build.xml,要注意的是環境變量和數據庫的設置要符合自己的實際配置,這裏庫文件目錄的設置是"D:/java/Hibernate/lib",參考文章《Eclipse快速上手Hibernate--1. 入門實例》中的設置。
·  在MySQL中需要先建立一個HibernateTest數據庫,爲了解決中文問題,使用了GBK編碼,注意“&amp;amp;”,這是由於XDoclet生成Hibernate配置文件時,會丟失一個“amp;”字符串(一個小Bug)。
·  這裏我用build.xml中的“hibernatedoclet”任務直接生成“hibernate.cfg.xml”配置文件。

build.xml

<?xml version="1.0" encoding="GBK"?>
<project name="Hibernate中的繼承映射" default="help" basedir=".">

	<!-- ******  環境設置,可以根據自己的實際配置自行更改 ***** -->
	<!-- 源文件目錄, 可以通過 項目->屬性->Java構建路徑 更改 -->
	<property name="src.dir" value="./src" />
	<!-- 輸出的class文件目錄,可以通過 項目->屬性->Java構建路徑 更改 -->
	<property name="class.dir" value="./bin" />
	<!-- 庫文件目錄  -->
	<property name="lib.dir" value="D:/java/Hibernate/lib" />
	
	<!-- ******  數據庫設置,可以根據自己的實際配置自行更改 ***** -->	
	<property name="hibernate.dialect" value="net.sf.hibernate.dialect.MySQLDialect"></property>
	<property name="hibernate.driver" value="com.mysql.jdbc.Driver"></property>
	<!--    &amp;amp;    -->
	<property name="hibernate.jdbc.url" 
		value="jdbc:mysql://localhost:3306/HibernateTest?useUnicode=true&amp;amp;characterEncoding=GBK">
	</property>
	<property name="hibernate.username" value="root"></property>
	<property name="hibernate.password" value="javamxj"></property>
	<property name="hibernate.show.sql" value="true"></property>
	
	

	<!-- 定義類路徑 -->
	<path id="project.class.path">
		<fileset dir="${lib.dir}">
			<include name="*.jar"/>
		</fileset>
		<pathelement location="${class.dir}" />
	</path>

	<!-- ************************************************************** -->
	<!-- 使用說明 -->
	<!-- ************************************************************** -->
	<target name="help">
		<echo message="利用工具開發Hibernate" />
		<echo message="-----------------------------------" />
		<echo message="" />
		<echo message="提供以下任務:" />
		<echo message="" />		
		<echo message="generate-hbm      --> 運行HibernateDoclet,生成 Hibernate 類的映射文件" />
		<echo message="schemaexportt     --> 運行SchemaExport,利用 hbm.xml 文件生成數據表" />
		<echo message="" />
	</target>



	<!-- ************************************************************** -->
	<!-- HibernateDoclet 任務 -->
	<!-- ************************************************************** -->
	<target name="generate-hbm" >
		<echo message="運行HibernateDoclet,生成 Hibernate 類的映射文件"/>

		<taskdef name="hibernatedoclet" 
			classname="xdoclet.modules.hibernate.HibernateDocletTask" 
			classpathref="project.class.path">
		</taskdef>

		<hibernatedoclet destdir="${src.dir}" 
			excludedtags="@version,@author,@todo" force="true" encoding="GBK" 
			verbose="true">

			<fileset dir="${src.dir}">
				<include name="**/*.java"/>
			</fileset>

			<hibernate version="2.0" xmlencoding="GBK" />	
			
			<!--   生成配置文件       -->
			<hibernatecfg
				dialect="${hibernate.dialect}"
				driver="${hibernate.driver}" 
				jdbcUrl="${hibernate.jdbc.url}" 
				userName="${hibernate.username}" 
				password="${hibernate.password}"
				showSql="${hibernate.show.sql}"
				xmlencoding="GBK"
			/>			
			
		</hibernatedoclet>		
	</target>


	<!-- ************************************************************** -->
	<!-- SchemaExport 任務 -->
	<!-- ************************************************************** -->
	<target name="schemaexport">
		<echo message="運行SchemaExport,利用 hbm.xml 文件生成數據表"/>

		<taskdef name="schemaexport" 
			classname="net.sf.hibernate.tool.hbm2ddl.SchemaExportTask" 
			classpathref="project.class.path">
		</taskdef>

		<schemaexport config="${src.dir}/hibernate.cfg.xml" quiet="no" 
			text="no" drop="no" output="schema-export.sql">
		</schemaexport>
	</target>

</project>
 
 
· 好了,只要這四個文件就夠了,其它的會自動生成的。整個項目的結構如下:
 
 
 
4. 運行任務
 
·  雙擊“generate-hbm”任務,會發現在包中多了一個Animal.hbm.xml文件,在src目錄下會多了一個hibernate.cfg.xml文件,如果沒有,按F5鍵刷新一下(這裏建議打開Eclipse的“首選項”對話框,在“工作臺”中勾選“自動刷新工作空間”和“在構建之前自動保存”這兩項,這樣以後不用每次都刷新了)。

Animal.hbm.xml

<?xml version="1.0" encoding="GBK"?>

<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 2.0//EN" 
    "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping
>
    <class
        name="javamxj.inheritance.one.Animal"
        table="Animal"
        dynamic-update="false"
        dynamic-insert="false"
        select-before-update="false"
        optimistic-lock="version"
        discriminator-value="Animal"
    >

        <id
            name="id"
            column="ID"
            type="java.lang.Long"
            unsaved-value="null"
        >
            <generator class="hilo">
              <!--  
                  To add non XDoclet generator parameters, create a file named 
                  hibernate-generator-params-Animal.xml 
                  containing the additional parameters and place it in your merge dir. 
              --> 
            </generator>
        </id>

        <discriminator
            column="ANIMAL_TYPE"
            type="string"
            length="10"
        />

        <property
            name="name"
            type="java.lang.String"
            update="true"
            insert="true"
            access="property"
            column="name"
            length="24"
        />

        <!--
            To add non XDoclet property mappings, create a file named
                hibernate-properties-Animal.xml
            containing the additional properties and place it in your merge dir.
        -->
        <subclass
            name="javamxj.inheritance.one.Cat"
            dynamic-update="false"
            dynamic-insert="false"
            discriminator-value="Cat"
        >

        <property
            name="furColor"
            type="java.lang.String"
            update="true"
            insert="true"
            access="property"
            column="furColor"
            length="24"
        />

	    <!--
            	To add non XDoclet property mappings, create a file named
                hibernate-properties-Cat.xml
		containing the additional properties and place it in your merge dir.
	    -->

        </subclass>
        <subclass
            name="javamxj.inheritance.one.Dog"
            dynamic-update="false"
            dynamic-insert="false"
            discriminator-value="Dog"
        >

        <property
            name="category"
            type="java.lang.String"
            update="true"
            insert="true"
            access="property"
            column="category"
            length="24"
        />

	    <!--
            	To add non XDoclet property mappings, create a file named
                hibernate-properties-Dog.xml
		containing the additional properties and place it in your merge dir.
	    -->

        </subclass>

    </class>

</hibernate-mapping>
 
 
hibernate.cfg.xml
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-configuration 
  PUBLIC "-//Hibernate/Hibernate Configuration DTD 2.0//EN" 
  "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">

<!-- Generated file - Do not edit! -->

<hibernate-configuration>

	<!-- a SessionFactory instance listed as /jndi/name -->
	<session-factory>

		<!-- properties -->
		<property name="dialect">net.sf.hibernate.dialect.MySQLDialect</property>
		<property name="show_sql">true</property>
		<property name="use_outer_join">false</property>
		<property name="connection.username">root</property>
		<property name="connection.password">javamxj</property>
		<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
	           <property name="connection.url">jdbc:mysql://localhost:3306/HibernateTest?useUnicode=true&amp;characterEncoding=GBK</property>

		<!-- mapping files -->
		<mapping resource="javamxj/inheritance/one/Animal.hbm.xml"/>
	</session-factory>

</hibernate-configuration>
 
·  雙擊“schemaexport”任務,在項目根目錄下,會產生一個“schema-export.sql”文件。
schema-export.sql
drop table if exists Animal
drop table if exists hibernate_unique_key
create table Animal (
   ID bigint not null,
   ANIMAL_TYPE varchar(10) not null,
   name varchar(24),
   furColor varchar(24),
   category varchar(24),
   primary key (ID)
)
create table hibernate_unique_key (
    next_hi integer
)
insert into hibernate_unique_key values ( 0 )
 
·  切換到數據庫中,會發現已經自動產生了數據表animal(表hibernate_unique_key是由於採用hilo主鍵策略生成的)。
 
 
5. 測試程序
 
·  好了,在包javamxj.inheritance.one下新建一個Demo.java類,很簡單,前半部分是添加數據,後半部分是簡單的測試。

Demo.java

package javamxj.inheritance.one;

import java.util.Iterator;
import java.util.List;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.Transaction;
import net.sf.hibernate.cfg.Configuration;

public class Demo {
	public static void main(String[] args) {
		try {
			new Demo();
		} catch (HibernateException he) {
			he.printStackTrace();
		}
	}

	public Demo() throws HibernateException {
		
		SessionFactory sf = new Configuration().configure()
				.buildSessionFactory();

		Session sess = sf.openSession();
		Transaction tx = null;
		try {
			tx = sess.beginTransaction();

			Cat cat = new Cat();
			cat.setName("小白");
			cat.setFurColor("白色");
			sess.save(cat);

			Dog dog = new Dog();
			dog.setName("小黑");
			dog.setCategory("京巴狗");
			sess.save(dog);

			tx.commit();
		} catch (HibernateException e) {
			if (tx != null)
				tx.rollback();
			throw e;
		} finally {
			sess.close();
		}

		sess = sf.openSession();
		tx = null;
		try {
			tx = sess.beginTransaction();
			List animals = sess.find("from " + Animal.class.getName());
			for (Iterator it = animals.iterator(); it.hasNext();) {
				Animal animal = (Animal) it.next();
				System.out.println("動物 '" + animal.getName()
						+ "' 所在類是: " + animal.getClass().getName());
				System.out.print("發出叫聲: ");
				animal.makeSound();

			}

			tx.commit();
		} catch (HibernateException e) {
			if (tx != null)
				tx.rollback();
			throw e;
		} finally {
			sess.close();
		}
	}
}
 
 
·  運行這個類,控制檯輸出如下:
 
·  同時,數據表中生成如下數據:
注意其中爲“NULL”的部分。
 
·  最後的項目結構如下:
 
 
小結:  
● 優點:
· 實現簡單。
· 支持多態——對象角色發生變化,或存在多重角色時。
· 報表操作實現簡單:表中包含了所有信息。

● 缺點:
· 增加類層次中的耦合。類層次中任何類的屬性的增加都會導致表的變更;某個子類屬性的修改會影響到整個
層次結構,而不僅僅是該子類。
· 浪費了一些數據庫空間。浪費空間的多少取決於繼承層次的深度。層次越深,不同的屬性越多,屬性的全集就越大,也就越浪費空間。
· 可能需要指明具體的角色。
 
 
參考:
· HIBERNATE - 符合Java習慣的關係數據庫持久化(第8章)
 
  下篇文章會談談每個子類一個表的策略。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章