1. 程式人生 > 其它 >Hibernate【對映】續篇

Hibernate【對映】續篇

元件對映

Java主要的類主要有兩種方式

  • 組合關係,組合關係對應的就是元件對映
  • 繼承關係,繼承關係對應的就是繼承對映

元件對映實際上就是將組合關係的資料對映成一張表,元件類和被包含的元件類對映成一張表

有的時候,兩個類的關係明顯不是繼承關係,但兩個類的親密程度很高,在一個類裡邊需要用到另外一個類…那麼就在類中定義一個變數來維護另一個類的關係,這種就叫組合關係!

需求:汽車和輪子。汽車需要用到輪子,但是輪子的爸爸不可能是汽車吧?

設計資料庫

這裡寫圖片描述

設計實體

Wheel.java

public class Wheel {



private int  count;

private int size;



public int getCount() {

return count;

}



public void setCount(int count) {

this.count = count;

}



public int getSize() {

return size;

}



public void setSize(int size) {

this.size = size;

}

}

Car.java,使用變數維護Wheel

package zhongfucheng.aa;



/**

* Created by ozc on 2017/5/7.

*/

public class Car {



private int id;

private String name;

private Wheel wheel;



public Wheel getWheel() {

return wheel;

}



public void setWheel(Wheel wheel) {

this.wheel = wheel;

}



public int getId() {

return id;

}



public void setId(int id) {

this.id = id;

}



public String getName() {

return name;

}



public void setName(String name) {

this.name = name;

}

}

對映表

使用了一個新標籤<component>,元件對映標籤。

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">



<hibernate-mapping package="zhongfucheng.aa" >



<class name="Car" table="Car" >



<!--對映主鍵-->

<id name="id" column="id">

<generator class="native"></generator>

</id>



<!--對映普通欄位-->

<property name="name" column="name" ></property>





<!--

對映元件欄位

-->

<component name="wheel">

<property name="count"></property>

<property name="size"></property>

</component>



</class>



</hibernate-mapping>

測試

package zhongfucheng.aa;



import org.hibernate.SessionFactory;

import org.hibernate.Transaction;

import org.hibernate.cfg.Configuration;

import org.hibernate.classic.Session;



/**

* Created by ozc on 2017/5/6.

*/

public class App5 {

public static void main(String[] args) {





//建立物件

Wheel wheel = new Wheel();

Car car = new Car();



//設定屬性

wheel.setCount(43);

wheel.setSize(22);

car.setName("寶馬");



//維護關係

car.setWheel(wheel);

//獲取載入配置管理類

Configuration configuration = new Configuration();





configuration.configure().addClass(Car.class);



//建立Session工廠物件

SessionFactory factory = configuration.buildSessionFactory();



//得到Session物件

Session session = factory.openSession();



//使用Hibernate操作資料庫,都要開啟事務,得到事務物件

Transaction transaction = session.getTransaction();



//開啟事務

transaction.begin();



session.save(car);



//提交事務

transaction.commit();



//關閉Session

session.close();





}

}

這裡寫圖片描述

傳統方式繼承

需求:動物、貓、猴子。貓繼承著動物

傳統方式繼承的特點就是:有多少個子類就寫多少個配置檔案.

表結構

我們的表應該是這樣的:id和name從Animal中繼承,catchMouse是子類的具體行為。

這裡寫圖片描述

實體

Animal.java

package zhongfucheng.aa;



// 動物類

public abstract class Animal {



private int id;

private String name;

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

}

Cat.java繼承著Animail

package zhongfucheng.aa;



public class Cat extends Animal{



// 抓老鼠

private String catchMouse;



public String getCatchMouse() {

return catchMouse;

}



public void setCatchMouse(String catchMouse) {

this.catchMouse = catchMouse;

}

}

對映檔案

簡單繼承的對映檔案很好寫,在屬性上,直接寫父類的屬性就可以了。

但是也有致命的缺點:如果子類有很多,就需要寫很多的配置檔案

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">



<hibernate-mapping package="zhongfucheng.aa" >



<class name="Cat" table="cat" >



<!--對映主鍵-->

<id name="id" column="id">

<generator class="native"></generator>

</id>



<!--

對映普通欄位

父類的屬性直接引用就行了,比如name屬性,直接寫就行了!

-->

<property name="name" column="name" ></property>

<property name="catchMouse" column="catchMouse" ></property>





</class>



</hibernate-mapping>

測試

package zhongfucheng.aa;



import org.hibernate.SessionFactory;

import org.hibernate.Transaction;

import org.hibernate.cfg.Configuration;

import org.hibernate.classic.Session;



public class App5 {

public static void main(String[] args) {





//建立物件

Cat cat = new Cat();



//設定屬性



cat.setName("大花貓");

cat.setCatchMouse("捉大老鼠");



//獲取載入配置管理類

Configuration configuration = new Configuration();





configuration.configure().addClass(Cat.class);



//建立Session工廠物件

SessionFactory factory = configuration.buildSessionFactory();



//得到Session物件

Session session = factory.openSession();



//使用Hibernate操作資料庫,都要開啟事務,得到事務物件

Transaction transaction = session.getTransaction();



//開啟事務

transaction.begin();



session.save(cat);

//如果取資料時候Animal父類接收的話,需要給出Anmail的全名





//提交事務

transaction.commit();



//關閉Session

session.close();





}

}

這裡寫圖片描述


把所有子類對映成一張表

前面我們採用的是:每個子類都需要寫成一個配置檔案,對映成一張表

如果子類的結構很簡單,只比父類多幾個屬性。就像上面的例子…我們可以將所有的子類都對映成一張表中

但是呢,這樣是不符合資料庫設計規範的…..因為表中的資料可能是貓,可能是猴子…這明顯是不合適的…

由於表中可能存在貓,存在猴子,為了區分是什麼型別的。我們需要使用鑑別器

我們瞭解一下…

資料表

這裡寫圖片描述

實體

實體和上面雷同,只多了一個猴子的實體表

Monkey.java

public class Monkey extends Animal {

// 吃香蕉

private String eatBanana;



public String getEatBanana() {

return eatBanana;

}



public void setEatBanana(String eatBanana) {

this.eatBanana = eatBanana;

}

}

對映檔案

使用了subClass這個節點和鑑別器

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">



<!--

繼承對映, 所有的子類都對映到一張表

-->

<hibernate-mapping package="cn.itcast.e_extends2">

<class name="Animal" table="t_animal">

<id name="id">

<generator class="native"></generator>

</id>

<!-- 指定鑑別器欄位(區分不同的子類) -->

<discriminator column="type_"></discriminator>

<property name="name"></property>

<!--

子類:貓

每個子類都用subclass節點對映

注意:一定要指定鑑別器欄位,否則報錯!

鑑別器欄位:作用是在資料庫中區別每一個子類的資訊, 就是一個列

discriminator-value="cat_"

指定鑑別器欄位,即type_欄位的值

如果不指定,預設為當前子類的全名

-->

<subclass name="Cat" discriminator-value="cat_">

<property name="catchMouse"></property>

</subclass>

<!--

子類:猴子

-->

<subclass name="Monkey" discriminator-value="monkey_">

     <property name="eatBanana"></property>

</subclass>

</class>



</hibernate-mapping>

測試

載入的是Animal父類的對映檔案。儲存的是cat和monkey。

package zhongfucheng.aa;



import org.hibernate.SessionFactory;

import org.hibernate.Transaction;

import org.hibernate.cfg.Configuration;

import org.hibernate.classic.Session;



public class App5 {

public static void main(String[] args) {





//建立物件

Cat cat = new Cat();

Monkey monkey = new Monkey();



//設定屬性

cat.setName("大花貓");

cat.setCatchMouse("小老鼠");

monkey.setEatBanana("吃香蕉");

monkey.setName("大猴子");



//獲取載入配置管理類

Configuration configuration = new Configuration();



//載入Animal的對映檔案!

configuration.configure().addClass(Animal.class);



//建立Session工廠物件

SessionFactory factory = configuration.buildSessionFactory();



//得到Session物件

Session session = factory.openSession();



//使用Hibernate操作資料庫,都要開啟事務,得到事務物件

Transaction transaction = session.getTransaction();



//開啟事務

transaction.begin();



session.save(cat);

session.save(monkey);



//提交事務

transaction.commit();



//關閉Session

session.close();





}

}

這裡寫圖片描述


每個類對映一張表(3張表)

父類和子類都各對應一張表。那麼就有三張表了

這種結構看起來是完全面向物件,但是表之間的結構會很複雜,插入一條子類的資訊,需要兩條SQL

資料表設計

這裡寫圖片描述

對映檔案

使用到了<joined-subclass >這個節點

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">





<hibernate-mapping package="zhongfucheng.aa">



<class name="Animal" table="t_animal">

<id name="id">

<generator class="native"></generator>

</id>



<property name="name"></property>





<!--

Animal下的子類對映成一張表

指定子類的型別,對應的表

指定子類的外來鍵欄位【需要對應Animal】

指定子類的普通屬性

-->

<joined-subclass name="Cat" table="cat_">

<!--key對應的是外來鍵欄位-->

<key column="animal_id"></key>

<property name="catchMouse"></property>

</joined-subclass>



<joined-subclass name="Monkey" table="monkey_">

<!--key對應的是外來鍵欄位-->

<key column="animal_id"></key>

<property name="eatBanana"></property>

</joined-subclass>





</class>





</hibernate-mapping>

測試

package zhongfucheng.aa;



import org.hibernate.SessionFactory;

import org.hibernate.Transaction;

import org.hibernate.cfg.Configuration;

import org.hibernate.classic.Session;



public class App5 {

public static void main(String[] args) {





//建立物件

Cat cat = new Cat();

Monkey monkey = new Monkey();



//設定屬性

cat.setName("大花貓");

cat.setCatchMouse("小老鼠");

monkey.setEatBanana("吃香蕉");

monkey.setName("大猴子");



//獲取載入配置管理類

Configuration configuration = new Configuration();



//載入類對應的對映檔案!

configuration.configure().addClass(Animal.class);



//建立Session工廠物件

SessionFactory factory = configuration.buildSessionFactory();



//得到Session物件

Session session = factory.openSession();



//使用Hibernate操作資料庫,都要開啟事務,得到事務物件

Transaction transaction = session.getTransaction();



//開啟事務

transaction.begin();



session.save(cat);

session.save(monkey);



//提交事務

transaction.commit();



//關閉Session

session.close();





}

}

每儲存一個子類物件需要兩條SQL語句!

這裡寫圖片描述


(推薦)每個子類對映一張表, 父類不對應表(2張表)

  • 使用過了一張表儲存所有子類的資料,這不符合資料庫設計規範
  • 每個子類、父類都擁有一張表..表結構太過於繁瑣..新增資訊時,過多的SQL

我們即將使用的是:每個子類對映成一張表,父類不對應表…這和我們傳統方式繼承是一樣的。只不過在hbm.xml檔案中使用了<union-subclass>這個節點,由於有了這個節點,我們就不需要每個子類都寫一個配置檔案了。

資料庫表設計

這裡寫圖片描述

對映檔案

  • 想要父類不對映成資料庫表,只要在class中配置為abstract即可
  • 使用了union-subclass節點,主鍵就不能採用自動增長策略了。我們改成UUID即可。當然啦,對應的實體id型別要改成String
<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">





<hibernate-mapping package="zhongfucheng.aa">





<!--

想要父類不對映成表,設定成abstract

-->

<class name="Animal" abstract="true">



<!--

如果使用了union-subclass節點,那麼主鍵生成策略不能是自增長,我們改成uuid即可

-->

<id name="id">

<generator class="uuid"></generator>

</id>



<property name="name"></property>





<!--

將子類的資訊都對映成一張表

給出屬性的名稱

屬性對應的資料庫表

普通欄位

-->

<union-subclass name="Cat" table="cat_">

<property name="catchMouse"></property>

</union-subclass>



<union-subclass name="Monkey" table="monkey_">

<property name="eatBanana"></property>

</union-subclass>





</class>





</hibernate-mapping>

測試

package zhongfucheng.aa;



import org.hibernate.SessionFactory;

import org.hibernate.Transaction;

import org.hibernate.cfg.Configuration;

import org.hibernate.classic.Session;



public class App5 {

public static void main(String[] args) {





//建立物件

Cat cat = new Cat();

Monkey monkey = new Monkey();



//設定屬性

cat.setName("大花貓");

cat.setCatchMouse("小老鼠");

monkey.setEatBanana("吃香蕉");

monkey.setName("大猴子");



//獲取載入配置管理類

Configuration configuration = new Configuration();



//載入類對應的對映檔案!

configuration.configure().addClass(Animal.class);



//建立Session工廠物件

SessionFactory factory = configuration.buildSessionFactory();



//得到Session物件

Session session = factory.openSession();



//使用Hibernate操作資料庫,都要開啟事務,得到事務物件

Transaction transaction = session.getTransaction();



//開啟事務

transaction.begin();



session.save(cat);

session.save(monkey);



//提交事務

transaction.commit();



//關閉Session

session.close();





}

}

這裡寫圖片描述

元件對映和繼承對映總結

由於我們的傳統繼承對映每個子類都對應一個配置檔案,這樣十分麻煩。因此.hbm.xml就給出了幾個節點供我們使用,分別有以下的情況:

  • 子類父類共有一張表subclass
  • 不符合資料庫設計規範
  • 需要使用鑑別器
  • 子類、父類都有自己的表joined-subclass,那麼就是三張表
  • 表的結構太過繁瑣
  • 插入資料時要生成SQL至少就要兩條
  • 子類擁有自己的表、父類不對應表【推薦】union-subclass
  • 父類不對應表要使用abstract來修飾
  • 主鍵的id不能使用自增長策略,修改成UUID就好了。對應的JavaBean的id設定成String就好