hibernate從零開始到各種映射
ORM(對象/關系數據庫映射)
對象關系映射(Object Relational Mapping,簡稱ORM)是一種為了解決面向對象與關系數據庫存在的互不匹配的現象的技術。它完成了面向對象的編程語言到關系型數據庫的映射㈣。ORM框架可以看成應用程序和數據庫之間的橋梁,通過它,我們可以以面向對象的方式建模、操作,易用、易理解,同時也可以利用關系型數據庫系統對數據庫訪問操作的一些優勢。以面向對象的方式操作持久化對象,而ORM框架負責轉換成對應的SQL(結構化查詢語言)操作。
Hibernate概述
Hibernate是輕量級Java EE應用的持久層解決方案,是流行的ORM框架,它對JDBC進行了非常輕量級的對象封裝,使得Java程序員可以面向對象的方式來操縱數據庫。Hibernate不僅管理Java類到數據庫表的映射,還提供數據查詢和獲取數據的操作,可以減少人工使用SQL和JDBC處理數據的時間。
Hibernate體系結構
Hibernate實際上是一個提供數據庫服務的中間件。Hibernate的持久化解決方案將用戶從繁瑣的JDBC訪問中解脫出來,底層數據庫連接獲取,數據訪問的實現、事務控制都無須用戶關心,這種體系結構,將應用層從底層的JDBC/JTA API中抽象出來。Hibernate體系結構如圖(註:該圖來自於Hibernate官方參考文檔)所示。
Hibernate有5個核心的接口、類,如下:
(1)SessionFactory接口:它是單個數據庫映射關系經過編譯後的內存鏡像。所以,SessionFactory的初始化過程比較復雜,同時也耗費大量的資源。為了解決這個問題,Hibernate的設計人員對Hibernate采用了線程安全可以並發調用,實現SessionFactory的實例,多個線程可以並發調用,實現Hibernate實例共享。此外,它還是生成Session的工廠。
(2)Session接口:應用程序與持久儲存層之間交互操作的一個單線程對象。Session接口對象實例中提供了持久化操作相關的“增、刪、查、改"(CRUD)方面的操作。所有的持久化操作都是在Session的基礎上完成的。通常將每一個Session對象實例和一個數據庫事務綁定。
(3)事務Transaction接口:通過Transaction接口來達到允許應用系統通過一組一致的事務API來控制事務邊界,從而可以封裝系統底層的事務操作和訪問的接口。將應用系統中涉及事務功能實現代碼從底層的事務具體實現的技術細節中抽象出來,最終可以達到應用系統在不同的運行環境平臺和Java EE容器之間方便的移植。
(4)Query接口:Query接口能讓開發者方便的對數據庫表中的數據及與之對應的持久化對象進行查詢操作,利用它可以以面向對象的方式來實現對數據庫的各種查詢操作。
(5)Configuration:通過Configuration獲取目前的配置(如數據源、數據庫的URL、連接用戶名及密碼、數據庫方言等)並將這些配置加載到內存中,並預啟動Hibernate框架,最終達到創建SessionFactory對象的目的。
Hibernate Api
1 |-- Configuration 配置管理類對象 2 3 config.configure(); 加載主配置文件的方法(hibernate.cfg.xml) 4 5 默認加載src/hibernate.cfg.xml 6 7 config.configure(“cn/config/hibernate.cfg.xml”); 加載指定路徑下指定名稱的主配置文件 8 9 config.buildSessionFactory(); 創建session的工廠對象 10 11 12 13 |-- SessionFactory session的工廠(或者說代表了這個hibernate.cfg.xml配置文件) 14 15 sf.openSession(); 創建一個sesison對象 16 17 sf.getCurrentSession(); 創建session或取出session對象 18 19 20 21 |--Session session對象維護了一個連接(Connection), 代表了與數據庫連接的會話。 22 23 Hibernate最重要的對象: 只用使用hibernate與數據庫操作,都用到這個對象 24 25 session.beginTransaction(); 開啟一個事務; hibernate要求所有的與數據庫的操作必須有事務的環境,否則報錯! 26 27 28 29 |-- Transaction hibernate事務對象
更新:
session.save(obj); 保存一個對象
session.update(emp); 更新一個對象
session.saveOrUpdate(emp); 保存或者更新的方法:
沒有設置主鍵,執行保存;有設置主鍵,執行更新操作;如果設置主鍵不存在報錯!
主鍵查詢: session.get(Employee.class, 1); 主鍵查詢,及時加載,只要調用get方法立刻向數據庫查詢。
session.load(Employee.class, 1); 主鍵查詢,默認使用懶加載,當用到數據的時候才向數據庫查詢。
在這裏,我們順便談談懶加載(延遲加載)是什麽,
懶加載(lazy):當用到數據的時候才向數據庫查詢,這就是hibernate的懶加載特性。
目的:提供程序執行效率!
Hibernate跟數據庫打交道,那麽查詢數據是必不可少的。Hibernate提供了幾種查詢方式。
HQL查詢:
HQL: hibernate query language 即hibernate提供的面向對象的查詢語言
查詢的是對象以及對象的屬性。
區分大小寫。
Criteria查詢:
完全面向對象的查詢。
本地SQL查詢:
復雜的查詢,就要使用原生態的sql查詢,也可以,就是本地sql查詢的支持!
(缺點: 不能跨數據庫平臺!)
為什麽要用Hibernate:
1.對JDBC訪問數據庫的代碼做了封裝,大大簡化了數據訪問層繁瑣的重復性代碼。
2.Hibernate是一個基於JDBC的主流持久化框架,是一個優秀的ORM實現。他能很大程度的簡化DAO層的編碼工作
3.hibernate使用Java反射機制,而不是字節碼增強程序來實現透明性。
4.hibernate的性能非常好,因為它是個輕量級框架。映射的靈活性很出色。它支持各種關系數據庫,從一對一到多對多的各種復雜關系。
Hibernate運行過程:
1.通過Configuration().configure();讀取並解析hibernate.cfg.xml配置文件
2.由hibernate.cfg.xml中的<mappingresource="com/xx/User.hbm.xml"/>讀取並解析映射信息
3.通過config.buildSessionFactory();//創建SessionFactory
4.sessionFactory.openSession();//打開Sesssion
5.session.beginTransaction();//創建事務Transation
6.persistent operate持久化操作 //一般指Save這個方法
7.session.getTransaction().commit();//提交事務
8.關閉Session
9.關閉SesstionFactory
說完這個工作原理,我們來個Demo試下。
開發工具:myeclipse2014
操作系統:win10
數據庫:MySQL5.5
首先,在myeclipse2014下新建web項目。
導入hibernate jar包。步驟,右擊項目,選擇“Myeclipse”,再選擇Project Facets 【Capabilities】 ,再點擊“Install Hibernate Facet”。
進入該窗口。
點擊Next。
把紅色的勾去掉,再點擊Next。
填寫數據庫配置信息。填寫完再Finish即可。
新建實體類User
public class User {
private int id;
private String username;
private String password;
//省略get,set方法
}
User映射文件
<?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"> <hibernate-mapping package="com.sun.bean"> <!--類名,表名,數據庫名 --> <class name="User" table="user" catalog="mydb"> <id name="id" column="id"> <generator class="native" /> </id> <property name="username" column="username"/> <property name="password" column="password"/> </class> </hibernate-mapping>
Demo類
public class Demo { public static void main(String[] args) { // 生成Configuration對象 Configuration cfg = new Configuration(); // 讀取配置文件 cfg.configure(); // 通過cf對象生成SessionFactory對象 SessionFactory sf = cfg.buildSessionFactory(); // 通過SessionFactory得到session Session session = sf.openSession(); // 開啟事務 Transaction tx = session.beginTransaction(); // 生成user對象,並通過set方法賦值 User user1 = new User(); user1.setUsername("system"); user1.setPassword("111"); User user2 = new User(); user2.setUsername("java"); user2.setPassword("2222"); // 保存數據 session.save(user1); session.save(user2); // 提交事務 tx.commit(); } }
在hibernate.cfg.xml中我們可以配置數據庫連接參數
下面,我們講講hibernate.cfg.xml中關於數據庫表的配置參數;
#hibernate.hbm2ddl.auto create-drop 每次在創建sessionFactory時候執行創建表;
當調用sesisonFactory的close方法的時候,刪除表!
#hibernate.hbm2ddl.auto create 每次都重新建表; 如果表已經存在就先刪除再創建
#hibernate.hbm2ddl.auto update 如果表不存在就創建; 表存在就不創建;
#hibernate.hbm2ddl.auto validate (生成環境時候) 執行驗證: 當映射文件的內容與數據庫表結構不一樣的時候就報錯!
在hibernate中,提供SchemaExport 類,讓我們可以使用它根據hibernate.cfg.xml的配置來自動建表,代碼如下;
public class App_ddl {
// 自動建表
@Test
public void testCreate() throws Exception {
// 創建配置管理類對象
Configuration config = new Configuration();
// 加載主配置文件
config.configure();
// 創建工具類對象
SchemaExport export = new SchemaExport(config);
// 建表
// 第一個參數: 是否在控制臺打印建表語句
// 第二個參數: 是否執行腳本
export.create(true, true);
}
}
接下來我們,談談實體類映射文件XX.hbm.xml的配置
<?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"> <!-- 映射文件: 映射一個實體類對象; 描述一個對象最終實現可以直接保存對象數據到數據庫中。 --> <!-- package: 要映射的對象所在的包(可選,如果不指定,此文件所有的類都要指定全路徑) auto-import 默認為true, 在寫hql的時候自動導入包名 如果指定為false, 再寫hql的時候必須要寫上類的全名; 如:session.createQuery("from cn.itcast.c_hbm_config.Employee").list(); --> <hibernate-mapping package="com.sun.bean" auto-import="true"> <!-- class 映射某一個對象的(一般情況,一個對象寫一個映射文件,即一個class節點) name 指定要映射的對象的名稱 table 指定對象對應的表; 如果沒有指定表名,默認與對象名稱一樣 --> <class name="Employee" table="employee"> <!-- 主鍵 ,映射--> <id name="empId" column="id"> <!-- 主鍵的生成策略 identity 自增長(mysql,db2) sequence 自增長(序列), oracle中自增長是以序列方法實現 native 自增長【會根據底層數據庫自增長的方式選擇identity或sequence】 如果是mysql數據庫, 采用的自增長方式是identity 如果是oracle數據庫, 使用sequence序列的方式實現自增長 increment 自增長(會有並發訪問的問題,一般在服務器集群環境使用會存在問題。) assigned 指定主鍵生成策略為手動指定主鍵的值 uuid 指定uuid隨機生成的唯一的值 foreign (外鍵的方式, one-to-one講) --> <generator class="native"/> </id> <!-- 普通字段映射 property name 指定對象的屬性名稱 column 指定對象屬性對應的表的字段名稱,如果不寫默認與對象屬性一致。 length 指定字符的長度, 默認為255 type 指定映射表的字段的類型,如果不指定會匹配屬性的類型 java類型: 必須寫全名 hibernate類型: 直接寫類型,都是小寫 --> <property name="empName" column="empName" type="java.lang.String" length="20"></property> <property name="workDate" type="java.util.Date"></property> <!-- 如果列名稱為數據庫關鍵字,需要用反引號或改列名。 --> <property name="desc" column="`desc`" type="java.lang.String"></property> </class> </hibernate-mapping>
復合主鍵類
public class CompositeKeys implements Serializable{ private String userName; private String address; // 省略get/set方法 } public class User { // 名字跟地址,不會重復 private CompositeKeys keys; private int age; }
User.hbm.xml 映射文件
<?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="com.sun.bean" auto-import="true"> <class name="User"> <!-- 復合主鍵映射 --> <composite-id name="keys"> <key-property name="userName" type="string"></key-property> <key-property name="address" type="string"></key-property> </composite-id> <property name="age" type="int"></property> </class> </hibernate-mapping>
測試類Test
public class Test{ private static SessionFactory sf; static { // 創建sf對象 sf = new Configuration() .configure() .addClass(User.class) //(測試) 會自動加載映射文件:Employee.hbm.xml .buildSessionFactory(); } //1. 保存對象 @Test public void testSave() throws Exception { Session session = sf.openSession(); Transaction tx = session.beginTransaction(); // 對象 CompositeKeys keys = new CompositeKeys(); keys.setAddress("廣州東"); keys.setUserName("Jack"); User user = new User(); user.setAge(20); user.setKeys(keys); // 保存 session.save(user); tx.commit(); session.close(); } @Test public void testGet() throws Exception { Session session = sf.openSession(); Transaction tx = session.beginTransaction(); //構建主鍵再查詢 CompositeKeys keys = new CompositeKeys(); keys.setAddress("廣州東"); keys.setUserName("Jack"); // 主鍵查詢 User user = (User) session.get(User.class, keys); // 測試輸出 if (user != null){ System.out.println(user.getKeys().getUserName()); System.out.println(user.getKeys().getAddress()); System.out.println(user.getAge()); } tx.commit(); session.close(); } }
集合映射
user 類
/ javabean設計 public class User { private int userId; private String userName; // 一個用戶,對應的多個地址 private Set<String> address; private List<String> addressList = new ArrayList<String>(); //private String[] addressArray; // 映射方式和list一樣 <array name=""></array> private Map<String,String> addressMap = new HashMap<String, String>(); //省略get,set方法 }
User.hbm.xml文件
<?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="com.sun.bean"> <class name="User" table="t_user"> <id name="userId" column="id"> <generator class="native"></generator> </id> <property name="userName"></property> <!-- set集合屬性的映射 name 指定要映射的set集合的屬性/ table 集合屬性要映射到的表 key 指定集合表(t_address)的外鍵字段 element 指定集合表的其他字段 type 元素類型,一定要指定 --> <set name="address" table="t_address"> <key column="uid"></key> <element column="address" type="string"></element> </set> <!-- list集合映射 list-index 指定的是排序列的名稱 (因為要保證list集合的有序) --> <list name="addressList" table="t_addressList"> <key column="uid"></key> <list-index column="idx"></list-index> <element column="address" type="string"></element> </list> <!-- map集合的映射 key 指定外鍵字段 map-key 指定map的key element 指定map的value --> <map name="addressMap" table="t_addressMap"> <key column="uid"></key> <map-key column="shortName" type="string" ></map-key> <element column="address" type="string" ></element> </map> </class> </hibernate-mapping>
測試
[email protected] public void testSaveSet() throws Exception { Session session = sf.openSession(); session.beginTransaction(); //-- 保存 Set<String> addressSet = new HashSet<String>(); addressSet.add("廣州"); addressSet.add("深圳"); // 用戶對象 User user = new User(); user.setUserName("Jack"); user.setAddress(addressSet); // 保存 session.save(user); session.getTransaction().commit(); session.close(); } // 保存list/map @Test public void testSaveList() throws Exception { Session session = sf.openSession(); session.beginTransaction(); User user = new User(); user.setUserName("Tom"); // // 用戶對象 -- list // user.getAddressList().add("廣州"); // user.getAddressList().add("深圳"); // // 保存 // session.save(user); // 用戶對象 -- Map user.getAddressMap().put("A0001", "廣州"); user.getAddressMap().put("A0002", "深圳"); // 保存 session.save(user); session.getTransaction().commit(); session.close(); }
關聯映射
需求:部門與員工
public class Dept { private int deptId; private String deptName; // 【一對多】 部門對應的多個員工 private Set<Employee> emps = new HashSet<Employee>(); }
public class Employee { private int empId; private String empName; private double salary; // 【多對一】員工與部門 private Dept dept; }
映射文件
Dept.hbm.xml
<?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.b_one2Many"> <class name="Dept" table="t_dept"> <id name="deptId"> <generator class="native"></generator> </id> <property name="deptName" length="20"></property> <!-- 一對多關聯映射配置 (通過部門管理到員工) Dept 映射關鍵點: 1. 指定 映射的集合屬性: "emps" 2. 集合屬性對應的集合表: "t_employee" 3. 集合表的外鍵字段 "t_employee. dept_id" 4. 集合元素的類型 一對多外鍵在多的一方 --> <set name="emps"> <!-- table="t_employee" --> <key column="dept_id"></key> <one-to-many class="Employee"/> </set> </class> </hibernate-mapping>
Employee.hbm.xml
<?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.b_one2Many"> <class name="Employee" table="t_employee"> <id name="empId"> <generator class="native"></generator> </id> <property name="empName" length="20"></property> <property name="salary" type="double"></property> <!-- 多對一映射配置 Employee 映射關鍵點: 1. 映射的部門屬性 : dept 2. 映射的部門屬性,對應的外鍵字段: dept_id 3. 部門的類型 --> <many-to-one name="dept" column="dept_id" class="Dept"></many-to-one> </class> </hibernate-mapping>
測試類
public class App { private static SessionFactory sf; static { sf = new Configuration() .configure() .addClass(Dept.class) .addClass(Employee.class) // 測試時候使用 .buildSessionFactory(); } // 保存, 部門方 【一的一方法操作】 @Test public void save() { Session session = sf.openSession(); session.beginTransaction(); // 部門對象 Dept dept = new Dept(); dept.setDeptName("應用開發部"); // 員工對象 Employee emp_zs = new Employee(); emp_zs.setEmpName("張三"); Employee emp_ls = new Employee(); emp_ls.setEmpName("李四"); // 關系 dept.getEmps().add(emp_zs); dept.getEmps().add(emp_ls); // 保存 session.save(emp_zs); session.save(emp_ls); session.save(dept); // 保存部門,部門下所有的員工 session.getTransaction().commit(); session.close(); /* * 結果 * Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?) Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?) Hibernate: insert into t_dept (deptName) values (?) Hibernate: update t_employee set deptId=? where empId=? 維護員工引用的部門的id Hibernate: update t_employee set deptId=? where empId=? */ } // 【推薦】 保存, 部員方 【多的一方法操作】 @Test public void save2() { Session session = sf.openSession(); session.beginTransaction(); // 部門對象 Dept dept = new Dept(); dept.setDeptName("綜合部"); // 員工對象 Employee emp_zs = new Employee(); emp_zs.setEmpName("張三"); Employee emp_ls = new Employee(); emp_ls.setEmpName("李四"); // 關系 emp_zs.setDept(dept); emp_ls.setDept(dept); // 保存 session.save(dept); // 先保存一的方法 session.save(emp_zs); session.save(emp_ls);// 再保存多的一方,關系回自動維護(映射配置完) session.getTransaction().commit(); session.close(); /* * 結果 * Hibernate: insert into t_dept (deptName) values (?) Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?) Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?) 少生成2條update sql */ } }
總結:
在一對多與多對一的關聯關系中,保存數據最好的通過多的一方來維護關系,這樣可以減少update語句的生成,從而提高hibernate的執行效率!
多對多映射
需求:項目與開發人員
Project Developer
Developer 類
/** * */ public class Developer { private int d_id; private String d_name; // 開發人員,參數的多個項目 private Set<Project> projects = new HashSet<Project>(); }
Project 類
/** * * */ public class Project { private int prj_id; private String prj_name; // 項目下的多個員工 private Set<Developer> developers = new HashSet<Developer>();
映射文件
Project.hbm.xml
<?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.c_many2many"> <class name="Project" table="t_project"> <id name="prj_id"> <generator class="native"></generator> </id> <property name="prj_name" length="20"></property> <!-- 多對多映射: 1. 映射的集合屬性: “developers” 2. 集合屬性,對應的中間表: “t_relation” 3. 外鍵字段: prjId 4. 外鍵字段,對應的中間表字段: did 5. 集合屬性元素的類型 --> <set name="developers" table="t_relation" cascade="save-update"> <key column="prjId"></key> <many-to-many column="did" class="Developer"></many-to-many> </set> </class> </hibernate-mapping>
Developer.hbm.xml
<?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.c_many2many"> <class name="Developer" table="t_developer"> <id name="d_id"> <generator class="native"></generator> </id> <property name="d_name" length="20"></property> <!-- 多對多映射配置: 員工方 name 指定映射的集合屬性 table 集合屬性對應的中間表 key 指定中間表的外鍵字段(引用當前表t_developer主鍵的外鍵字段) many-to-many column 指定外鍵字段對應的項目字段 class 集合元素的類型 --> <set name="projects" table="t_relation"> <key column="did"></key> <many-to-many column="prjId" class="Project"></many-to-many> </set> </class> </hibernate-mapping>
一對一映射,可分為基於主鍵映射和基於外鍵映射。外鍵映射的原理其實就是特殊的多對一。
我們先看基於主鍵映射的吧。
需求: 人和身份證號;
Person類
public class Person { private int id; private String name; }
IDCard 類
public class IDCard { private int id; private String cardNo; // private Person person; }
Person.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"> <hibernate-mapping package="com.sun.bean"> <!--類名,表名,數據庫名 --> <class name="Person" table="person" catalog="mydb"> <id name="id" column="id"> <generator class="native" /> </id> <property name="name" column="name" /> </class> </hibernate-mapping>
IDCard.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"> <hibernate-mapping package="com.sun.bean"> <!--類名,表名,數據庫名 --> <class name="IDCard" table="idcard" catalog="mydb"> <!-主鍵,主鍵要- --> <id name="id" column="id"> <generator class="foreign"> <param name="property">person</param> </generator> </id> <property name="cardNo" column="cardNo" /> <!-- 對person一對一--> <one-to-one name="person" /> </class> </hibernate-mapping>
當我們寫好po後,我們利用工具SchemaExport 來自動建表。
person表
idcard 表
通過觀察這兩張表,我們會發現,person表的id是自動遞增的,而idcard表的id卻沒有。
於是我們得出了小結論,idcard表的id其實是跟著person表的id而變化的。
測試類
public class TestOneToOne { public static void main(String[] args) { // 生成Configuration對象 Configuration cfg = new Configuration(); // 讀取配置文件 cfg.configure(); // 通過cf對象生成SessionFactory對象 SessionFactory sf = cfg.buildSessionFactory(); // 通過SessionFactory得到session Session session = sf.openSession(); Transaction tx = session.beginTransaction(); Person p1 = new Person(); p1.setName("小張"); Person p2 = new Person(); p2.setName("小張"); IDCard card1 = new IDCard(); card1.setCardNo("545454545454"); IDCard card2 = new IDCard(); card2.setCardNo("8888888888888"); card1.setPerson(p1); card2.setPerson(p2); session.save(p1); session.save(p2); session.save(card1); session.save(card2); tx.commit(); session.close(); sf.close(); } }
基於外鍵映射
原理就是特殊的多對一。單向一對一必須通過實體類生成數據庫表的方式,會自動生成相關的約束,直接創建表無效。
Person 類
public class Person { private int id; private String name; }
IDCard 類
public class IDCard { private int id; private String cardNo; // private Person person; }
映射文件
IDCard.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"> <hibernate-mapping package="com.sun.bean"> <!--類名,表名,數據庫名 --> <class name="IDCard" table="idcard" catalog="mydb"> <id name="id" column="id"> <generator class="native"> </generator> </id> <property name="cardNo" column="cardNo" /> <!--對person一對一,需加上屬性 unique--> <many-to-one name="person" column="person_id" unique="true"/> </class> </hibernate-mapping>
Person.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"> <hibernate-mapping package="com.sun.bean"> <!--類名,表名,數據庫名 --> <class name="Person" table="person" catalog="mydb"> <id name="id" column="id"> <generator class="native" /> </id> <property name="name" column="name" /> </class> </hibernate-mapping>
組件映射
組合關系:一個類中包含了另外一個類。這2個類中就是組合關系。
需求: 汽車與車輪
Car類
//車
public class Car { private int id; private String name; // 車輪 private Wheel wheel; }
車輪類
// 車輪 public class Wheel { private int count; private int size; }
映射文件
<hibernate-mapping package="cn.itcast.d_component"> <class name="Car" table="t_car"> <id name="id"> <generator class="native"></generator> </id> <property name="name" length="20"></property> <!-- 組件映射 --> <component name="wheel"> <property name="size"></property> <property name="count"></property> </component> </class> </hibernate-mapping>
繼承映射
需求:豬、鳥、動物。
繼承映射有三種實現方式。
(1)所有子類映射到一張表 (1張表)
(2)每個類映射一張表(3張表)
(3)每個子類映射一張表, 父類不對應表(2張表)
(1)所有子類映射到一張表 (1張表)
Animal類
public class Animal { private int id; private String name; private String gender; //省略get,set方法 }
Pig 類
public class Pig extends Animal{ private int weight; }
Bird 類
public class Bird extends Animal{ private int height; }
映射文件
<hibernate-mapping package="com.sun.bean"> <class name="Animal" table="t_animal" lazy="false"> <id name="id"> <generator class="native"/> </id> <discriminator column="type" type="string"/> <property name="name"/> <property name="sex"/> <subclass name="Pig" discriminator-value="P"> <property name="weight"/> </subclass> <subclass name="Bird" discriminator-value="B"> <property name="height"/> </subclass> </class> </hibernate-mapping>
總結:
寫法較為簡單:所有子類用一個映射文件,且映射到一張表!
那麽怎麽區分哪個子類的信息在那張表呢?
答案是在表中添加一個字段,用這個字段的值來進行區分。
在父類中定義一個discriminator,即指定這個區分的字段的名稱和類型
如: <discriminator column="type" type="string"/>
(2)每個類映射一張表(3張表)
映射文件:
<hibernate-mapping package="com.sun.bean"> <class name="Animal" table="t_animal"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <property name="sex"/> <joined-subclass name="Pig" table="t_pig"> <key column="pid"/> <property name="weight"/> </joined-subclass> <joined-subclass name="Bird" table="t_bird"> <key column="bid"/> <property name="height"/> </joined-subclass> </class> </hibernate-mapping>
總結:在父類對應的數據庫表中,實際上會存儲所有的記錄,包括父類和子類的記錄;在子類對應的數據庫表中,這個表只定義了子類中所特有的屬性映射的字段。子類與父類,通過相同的主鍵值來關聯。
(3)每個子類映射一張表, 父類不對應表(2張表)
<hibernate-mapping package="com.sun.bean"> <class name="Animal" table="t_animal" abstract="true"> <id name="id"> <generator class="assigned"/> </id> <property name="name"/> <property name="sex"/> <union-subclass name="Pig" table="t_pig"> <property name="weight"/> </union-subclass> <union-subclass name="Bird" table="t_bird"> <property name="height"/> </union-subclass> </class> </hibernate-mapping>
<!--註意:主鍵不能是自增長!在保存對象的時候id不能重復,可使用uuid-->
總結:
所有的子類都寫到一個映射文件;
父類不對應表; 每個子類對應一張表,而且這個表的信息是完備的。
未完待續,還有下文~如有錯誤,請指正~
hibernate從零開始到各種映射