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就好