Hibernate的緩存和幾種特殊的映射
一. 理解對(duì)象的狀態(tài)
Hibernate中對(duì)象的幾種狀態(tài): 臨時(shí)狀態(tài),持久化狀態(tài),游離狀態(tài)
臨時(shí)狀態(tài): 直接new關(guān)鍵字創(chuàng)建的普通對(duì)象,不在session下管理,數(shù)據(jù)庫中沒有對(duì)應(yīng)的記錄,即普通的一個(gè)實(shí)例.
持久化狀態(tài): 當(dāng)調(diào)用session的save/saveOrUpdate/get/load/list等方法的時(shí)候,對(duì)象就是持久化狀態(tài),處于持久化狀態(tài)的對(duì)象,當(dāng)對(duì)對(duì)象的屬性進(jìn)行修改的時(shí)候,會(huì)反映到數(shù)據(jù)庫中.
特點(diǎn): 處于session的管理中,數(shù)據(jù)庫中有對(duì)應(yīng)的記錄.
游離狀態(tài): 不處于session的管理,數(shù)據(jù)庫有對(duì)應(yīng)的記錄,session關(guān)閉后,對(duì)象的狀態(tài)會(huì)不存在.
二. 一級(jí)緩存
使用緩存的目的: 減少對(duì)數(shù)據(jù)庫的訪問次數(shù),提升Hibernate的執(zhí)行效率,Hibernate中的緩存分為一級(jí)緩存和二級(jí)緩存.
一級(jí)緩存基本概念:
* Hibernate的一級(jí)緩存: 也叫做session的緩存,可以在session范圍內(nèi)減少數(shù)據(jù)庫的訪問次數(shù),只在session范圍內(nèi)有用,session關(guān)閉,一級(jí)緩存失效.
* 當(dāng)調(diào)用session的save/saveOrUpdate/get/load/list/iterator 等方法的時(shí)候,都會(huì)把對(duì)象放入session的緩存中.
* session的緩存由Hibernate維護(hù),用戶不能操作緩存的內(nèi)容,如果需要操作緩存的內(nèi)容,必須通過Hibernate提供的evict/clear方法操作.
特點(diǎn): 只在當(dāng)前session有效,作用時(shí)間斷,效果不是特別明顯.在短時(shí)間內(nèi)多處操作數(shù)據(jù)庫,效果比較明顯.
緩存相關(guān)的幾個(gè)方法:
session.flush() : 刷新緩存,讓一級(jí)緩存與數(shù)據(jù)庫同步
session.evict(obj) 清空一級(jí)緩存中指定的對(duì)象
session.clear() 清空一級(jí)緩存中的所有對(duì)象
使用場(chǎng)景: 在批量操作的時(shí)候
session.flush(); //先與數(shù)據(jù)庫同步
session.clear(); //再清空一級(jí)緩存內(nèi)容
注意點(diǎn):
不同的session不會(huì)共享緩存數(shù)據(jù). 比如Dept dept = session1.get(Dept.class,1);
session2.update(dept);//dept放入session2的緩存.
dept.setDeptName("name");//如果生成2條update SQL語句,說明不同的session使用不同的緩存區(qū),不能共享數(shù)據(jù).
list和iterator的區(qū)別:
list():會(huì)一次性把所有記錄都查詢出來,會(huì)放入緩存,但是不會(huì)從緩存中獲取數(shù)據(jù).
iterator: N+1次查詢,N表示總的記錄數(shù),即會(huì)先發(fā)送一條語句查詢所有的記錄的主鍵(1次)
再根據(jù)每一個(gè)主鍵去數(shù)據(jù)庫查詢(N)
會(huì)把數(shù)據(jù)放入緩存,也會(huì)從緩存中取數(shù)據(jù).
- Hibernate的一級(jí)緩存測(cè)試:
Javabean設(shè)計(jì)
Dept.java
package com.mrq.domain;
public class Dept {
private Integer deptId;
private String deptName;
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
@Override
public String toString() {
return "Dept [deptId=" + deptId + ", deptName=" + deptName + "]";
}
}
Dept.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.mrq.domain">
<class name="Dept" table="t_dept2">
<id name="deptId">
<generator class="native"></generator>
</id>
<property name="deptName" type="string"></property>
</class>
</hibernate-mapping>
- 緩存測(cè)試
@Test
public void testCache() throws Exception{
Session session = sf.openSession();
session.beginTransaction();
Dept dept = null;
dept = (Dept)session.get(Dept.class, 1);//向數(shù)據(jù)庫查詢
System.out.println(dept);
dept = (Dept)session.get(Dept.class, 1);//不查詢,使用session緩存
session.getTransaction().commit();
session.close();
}
運(yùn)行結(jié)果類似:
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=1, deptName=人事部]
從運(yùn)行的結(jié)果分析:這里使用了兩個(gè)get方法,但是只進(jìn)行了一次SQL查詢,說明第二次的get是從緩存中獲取
- flush方法同步數(shù)據(jù)
@Test
public void flush() throws Exception {
Session session = sf.openSession();
session.beginTransaction();
Dept dept = null;
dept = (Dept) session.get(Dept.class, 5);
dept.setDeptName("行政部");
// 刷新數(shù)據(jù),和數(shù)據(jù)庫同步
session.flush();
dept.setDeptName("研發(fā)部");
session.getTransaction().commit(); // 相當(dāng)于session.flush();
session.close();
}
結(jié)果如下:
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Hibernate: update t_dept2 set deptName=? where deptId=?
Hibernate: update t_dept2 set deptName=? where deptId=?
進(jìn)行了2次update,一次是flush,一次是commit操作.
- clear清除緩存
@Test
public void clear() throws Exception {
Session session = sf.openSession();
session.beginTransaction();
Dept dept = null;
//放入緩存
dept = (Dept) session.get(Dept.class, 5);
// 清除緩存對(duì)象
//session.clear(); // 全部清除
session.evict(dept);// 清除特定對(duì)象
dept = (Dept) session.get(Dept.class, 5);//重新從數(shù)據(jù)庫獲取對(duì)象
session.getTransaction().commit(); // session.flush();
session.close();
}
結(jié)果:
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
清除緩存后
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
進(jìn)行了2次select操作,一次是在清除緩存之后
- 不同的session不共享數(shù)據(jù)
@Test
public void sessionTest() throws Exception {
Session session1 = sf.openSession();
session1.beginTransaction();
Session session2 = sf.openSession();
session2.beginTransaction();
// dept放入session1中
Dept dept = (Dept) session1.get(Dept.class, 1);
// dept放入session2中
session2.update(dept);
//
dept.setDeptName("人事部");
session1.getTransaction().commit(); // session1.flush();
session1.close();
session2.getTransaction().commit(); // session2.flush();
session2.close();
}
運(yùn)行結(jié)果:
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Hibernate: update t_dept2 set deptName=? where deptId=?
Hibernate: update t_dept2 set deptName=? where deptId=?
生成2條update SQL語句,說明不同的session使用不同的緩存區(qū),不能共享數(shù)據(jù).
2.1 list和iterator的緩存
- list操作
@Test
public void list() {
Session session = sf.openSession();
session.beginTransaction();
//HQL查詢
Query query = session.createQuery("from Dept");
List<Dept> list = query.list();
System.out.println(list);
session.getTransaction().commit();
session.close();
}
運(yùn)行結(jié)果:
Hibernate: select dept0_.deptId as deptId0_, dept0_.deptName as deptName0_ from t_dept2 dept0_
[Dept [deptId=1, deptName=事業(yè)部], Dept [deptId=2, deptName=文化部1], Dept [deptId=3, deptName=文化部2], Dept [deptId=4, deptName=文化部3], Dept [deptId=5, deptName=研發(fā)部]]
可以看到,list操作會(huì)一次性的把數(shù)據(jù)讀取出來
- iterator操作
@Test
public void iterator() {
Session session = sf.openSession();
session.beginTransaction();
//HQL查詢
Query query = session.createQuery("from Dept");
Iterator<Dept> iterator = query.iterate();
while (iterator.hasNext()) {
Dept dept = (Dept) iterator.next();
System.out.println(dept);
}
session.getTransaction().commit();
session.close();
}
運(yùn)行結(jié)果:
Hibernate: select dept0_.deptId as col_0_0_ from t_dept2 dept0_ //執(zhí)行一次次數(shù)查詢操作
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=1, deptName=事業(yè)部]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=2, deptName=文化部1]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=3, deptName=文化部2]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=4, deptName=文化部3]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=5, deptName=研發(fā)部]
可以看到,iterator方法,會(huì)先進(jìn)行一次次數(shù)查詢,再進(jìn)行N次對(duì)象查詢.
- list和iterator的緩存測(cè)試
list:
Session session = sf.openSession();
session.beginTransaction();
Query query = session.createQuery("from Dept");
List<Dept> list = query.list();
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println("==============List====");
list = query.list();
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
session.getTransaction().commit();
session.close();
運(yùn)行結(jié)果1:
Hibernate: select dept0_.deptId as deptId0_, dept0_.deptName as deptName0_ from t_dept2 dept0_
Dept [deptId=1, deptName=事業(yè)部]
Dept [deptId=2, deptName=文化部1]
Dept [deptId=3, deptName=文化部2]
Dept [deptId=4, deptName=文化部3]
Dept [deptId=5, deptName=研發(fā)部]
==============List====
Hibernate: select dept0_.deptId as deptId0_, dept0_.deptName as deptName0_ from t_dept2 dept0_
Dept [deptId=1, deptName=事業(yè)部]
Dept [deptId=2, deptName=文化部1]
Dept [deptId=3, deptName=文化部2]
Dept [deptId=4, deptName=文化部3]
Dept [deptId=5, deptName=研發(fā)部]
從list方法運(yùn)行結(jié)果分析: 兩次list都進(jìn)行了同樣的向數(shù)據(jù)庫發(fā)送SQL語句進(jìn)行查詢.表示list不會(huì)從session中獲取數(shù)據(jù).
iterator:
/*
* Iterator
* */
Session session = sf.openSession();
session.beginTransaction();
Query query2 = session.createQuery("from Dept");
Iterator<Dept> iterator = query2.iterate();
while (iterator.hasNext()) {
Dept dept = (Dept) iterator.next();
System.out.println(dept);
}
System.out.println("====iterator===");
iterator = query2.iterate();
while (iterator.hasNext()) {
Dept dept = (Dept) iterator.next();
System.out.println(dept);
}
session.getTransaction().commit();
session.close();
運(yùn)行結(jié)果2:
Hibernate: select dept0_.deptId as col_0_0_ from t_dept2 dept0_
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=1, deptName=事業(yè)部]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=2, deptName=文化部1]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=3, deptName=文化部2]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=4, deptName=文化部3]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=5, deptName=研發(fā)部]
====iterator===
Hibernate: select dept0_.deptId as col_0_0_ from t_dept2 dept0_
Dept [deptId=1, deptName=事業(yè)部]
Dept [deptId=2, deptName=文化部1]
Dept [deptId=3, deptName=文化部2]
Dept [deptId=4, deptName=文化部3]
Dept [deptId=5, deptName=研發(fā)部]
iterator結(jié)果分析: 先進(jìn)行一次次數(shù)查詢,再執(zhí)行N次查詢對(duì)象. 第二次iterator查詢,只進(jìn)行了一次次數(shù)查詢,并不進(jìn)行對(duì)象查詢,而是直接從緩存中獲取.
下面的測(cè)試也證明:list獲取會(huì)把數(shù)據(jù)緩存到session,iterator操作也可以從list緩存的數(shù)據(jù)中獲取對(duì)象
@Test
public void list_iterator() throws Exception {
Session session = sf.openSession();
session.beginTransaction();
Query q = session.createQuery("from Dept");
//
List<Dept> list = q.list();
for (int i=0; i<list.size(); i++){
System.out.println(list.get(i));
}
//
Iterator<Dept> it = q.iterate();
while(it.hasNext()){
Dept user = it.next();
System.out.println(user);
}
session.getTransaction().commit();
session.close();
}
運(yùn)行結(jié)果如下
Hibernate: select dept0_.deptId as deptId0_, dept0_.deptName as deptName0_ from t_dept2 dept0_
Dept [deptId=1, deptName=事業(yè)部]
Dept [deptId=2, deptName=文化部1]
Dept [deptId=3, deptName=文化部2]
Dept [deptId=4, deptName=文化部3]
Dept [deptId=5, deptName=研發(fā)部]
Hibernate: select dept0_.deptId as col_0_0_ from t_dept2 dept0_
Dept [deptId=1, deptName=事業(yè)部]
Dept [deptId=2, deptName=文化部1]
Dept [deptId=3, deptName=文化部2]
Dept [deptId=4, deptName=文化部3]
Dept [deptId=5, deptName=研發(fā)部]
三. 懶加載
get方法和load方法的區(qū)別:
get: 及時(shí)加載,只要調(diào)用get方法立即向數(shù)據(jù)庫查詢
load. 默認(rèn)使用懶加載,當(dāng)用到數(shù)據(jù)的時(shí)候才向數(shù)據(jù)庫查詢
懶加載(lazy) :當(dāng)用到數(shù)據(jù)的時(shí)候才向數(shù)據(jù)庫查詢,是Hibernate的懶加載特性,主要的目的是提供程序的執(zhí)行相率
lazy的取值:
TRUE: 使用懶加載
FALSE: 關(guān)閉懶加載
extra: 在集合數(shù)據(jù)懶加載的時(shí)候提升效率,在真正使用數(shù)據(jù)的時(shí)候才向數(shù)據(jù)庫發(fā)送查詢SQL語句;如果調(diào)用集合的size()/isEmpty()方法,只是統(tǒng)計(jì),不真正查詢數(shù)據(jù)
? 懶加載異常
Session關(guān)閉后薪捍,不能使用懶加載數(shù)據(jù)照卦!
如果session關(guān)閉后,使用懶加載數(shù)據(jù)報(bào)錯(cuò):
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
如何解決session關(guān)閉后不能使用懶加載數(shù)據(jù)的問題姑廉?
// 方式1: 先使用一下數(shù)據(jù)
//dept.getDeptName();
// 方式2:強(qiáng)迫代理對(duì)象初始化
Hibernate.initialize(dept);
// 方式3:關(guān)閉懶加載
設(shè)置lazy=false;
// 方式4: 在使用數(shù)據(jù)之后,再關(guān)閉session壹哺!
四: 特別的多對(duì)一映射(一對(duì)一映射)
一對(duì)一映射的例子:身份證與用戶的關(guān)系
映射有兩種方式:基于外鍵映射(用戶使用外鍵,引用身份證ID)和基于主鍵映射(用戶ID左外身份證ID),兩種方式設(shè)計(jì)都差不多,主要在于映射文件的設(shè)計(jì)
4.1 基于外鍵的映射配置
javabean設(shè)計(jì).
User.java
package com.mrq.domain;
public class User {
private Integer userId;
private String userName;
private IdCard idCard;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public IdCard getIdCard() {
return idCard;
}
public void setIdCard(IdCard idCard) {
this.idCard = idCard;
}
}
身份證IdCard.java
package com.mrq.entity;
//身份證類
public class IdCard {
private String cardNum;//身份證主鍵
private String address;//身份證地址
private User user;//身份證與用戶一對(duì)一關(guān)系
public String getCardNum() {
return cardNum;
}
public void setCardNum(String cardNum) {
this.cardNum = cardNum;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
- 映射文件的配置要點(diǎn):
一對(duì)一映射,有外鍵方
unique="true" 給外鍵字段添加唯一約束
User.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.mrq.entity">
<class name="User" table="t_user2">
<id name="userId">
<generator class="native"></generator>
</id>
<property name="userName" type="string"></property>
<!--
一對(duì)一映射:沒有外鍵方
-->
<one-to-one name="idCard" class="IdCard"></one-to-one>
</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.mrq.entity">
<class name="IdCard" table="t_idCard">
<id name="cardNum">
<generator class="assigned"></generator>
</id>
<property name="address" type="string"></property>
<!--
一對(duì)一映射有外鍵方
unique="true":給外鍵字段添加唯一約束
-->
<many-to-one name="user" unique="true" column="user_id" class="User" cascade="save-update"></many-to-one>
</class>
</hibernate-mapping>
測(cè)試方法
//一對(duì)一映射:基于外鍵
@Test
public void testOne2One() {
Configuration configuration = new Configuration();
SessionFactory sf = configuration.configure().buildSessionFactory();
Session session = sf.openSession();
session.beginTransaction();
User user = new User();
user.setUserName("小明!");
IdCard idCard = new IdCard();
idCard.setAddress("山西長治");
idCard.setCardNum("8673287979Ax");
idCard.setUser(user);
session.save(idCard);
session.getTransaction().commit();
session.close();
}
4.2 基于主鍵的設(shè)計(jì)
一對(duì)一映射脆荷,有外鍵方
(基于主鍵的映射)
constrained="true" 指定在主鍵上添加外鍵約束
主鍵生成方式: foreign 即把別的表的主鍵作為當(dāng)前表的主鍵蛋逾;
property (關(guān)鍵字不能修改)指定引用的對(duì)
User bean保持不變
IdCard.java
package com.mrq.domain;
//身份證類
public class IdCard {
private Integer user_id;//主鍵
private String cardNum;//身份證主鍵
private String address;//身份證地址
private User user;//身份證與用戶一對(duì)一關(guān)系
public Integer getUser_id() {
return user_id;
}
public void setUser_id(Integer user_id) {
this.user_id = user_id;
}
public String getCardNum() {
return cardNum;
}
public void setCardNum(String cardNum) {
this.cardNum = cardNum;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
映射配置文件
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.mrq.domain">
<class name="IdCard" table="t_idCard2">
<id name="user_id">
<!--
id節(jié)點(diǎn)指定的是主鍵映射,即user_id是主鍵
主鍵生成方式:foreign,即把別的表的主鍵作為當(dāng)前表的主鍵:
property: 指定引用的對(duì)象,對(duì)象的全名
-->
<generator class="foreign">
<param name="property">user</param>
</generator>
</id>
<property name="address" type="string"></property>
<property name="cardNum" length="20"></property>
<!--
一對(duì)一映射有外鍵方
基于主鍵
constrained="true" 指定在主鍵上添加外鍵約束
-->
<one-to-one name="user" class="User" constrained="true" cascade="save-update"></one-to-one>
</class>
</hibernate-mapping>
測(cè)試:
//一對(duì)一映射,基于主鍵
@Test
public void testOne2OneOther() {
Configuration configuration = new Configuration();
SessionFactory sf = configuration.configure().buildSessionFactory();
Session session = sf.openSession();
session.beginTransaction();
User user = new User();
user.setUserName("嘻嘻");
IdCard idCard = new IdCard();
idCard.setAddress("山西長治");
idCard.setCardNum("SDASDS3233Ax");
idCard.setUser(user);
session.save(idCard);
session.getTransaction().commit();
session.close();
}
4.3 組合(組件)映射
- 簡(jiǎn)單理解類的關(guān)系:
類的關(guān)系:
組合關(guān)系:一個(gè)類中包含了另外一個(gè)類.這兩個(gè)類就是組合關(guān)系
比如: 汽車和車輪,禮物盒子和禮物
繼承關(guān)系: 一個(gè)類繼承另外一個(gè)類,即為繼承關(guān)系
組合映射的例子:
- 組件類和被包含的組件類,共同映射到一張表
汽車和車輪
Car.java
package com.mrq.component;
public class Car {
private Integer id;
private String name;
private Wheel wheel;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Wheel getWheel() {
return wheel;
}
public void setWheel(Wheel wheel) {
this.wheel = wheel;
}
@Override
public String toString() {
return "Car [id=" + id + ", name=" + name + ", wheel=" + wheel + "]";
}
}
wheel.java
package com.mrq.component;
public class Wheel {
private Integer count;
private Integer size;
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
public Integer getSize() {
return size;
}
public void setSize(Integer size) {
this.size = size;
}
@Override
public String toString() {
return "Wheel [count=" + count + ", size=" + size + "]";
}
}
- 組合關(guān)系映射的要點(diǎn): component節(jié)點(diǎn)的使用
Car.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.mrq.component">
<class name="Car" table="t_car">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name" type="string"></property>
<!--
組件映射
-->
<component name="wheel" class="Wheel">
<property name="size"></property>
<property name="count"></property>
</component>
</class>
</hibernate-mapping>
4.4 繼承映射的4種配置方式
- 方式一:簡(jiǎn)單繼承關(guān)系映射,直接寫父類的屬性,每個(gè)子類對(duì)應(yīng)一個(gè)表
Animal.java
package com.mrq.extends1;
public abstract class Animal {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Cat類繼承Animal類
package com.mrq.extends1;
public class Cat extends Animal{
private String catchMouse;
public String getCatchMouse() {
return catchMouse;
}
public void setCatchMouse(String catchMouse) {
this.catchMouse = catchMouse;
}
}
簡(jiǎn)單繼承的映射配置,和普通的類類似
<?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.mrq.extends1">
<class name="Cat" table="t_cat">
<!-- 簡(jiǎn)單繼承映射,父類屬性直接寫 -->
<id name="id">
<generator class="native"></generator>
</id>
<property name="name" type="string"></property>
<property name="catchMouse"></property>
</class>
</hibernate-mapping>
- 方式二:所有子類映射到一張表
什么情況用白对?
子類教多,且子類較為簡(jiǎn)單换怖,即只有個(gè)別屬性甩恼!
好處:因?yàn)槭褂靡粋€(gè)映射文件, 減少了映射文件的個(gè)數(shù)沉颂。
缺點(diǎn):(不符合數(shù)據(jù)庫設(shè)計(jì)原則)
一個(gè)映射文件: Animal.hbm.xml
通過鑒別器區(qū)分是哪個(gè)子類的信息
Animal.hbm.xml
- 關(guān)鍵節(jié)點(diǎn)discriminator和sub-class
<?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.mrq.extends2">
<class name="Animal" table="t_animal">
<!-- 簡(jiǎn)單繼承映射,父類屬性直接寫 -->
<id name="id">
<generator class="native"></generator>
</id>
<!-- 指定鑒別字段:區(qū)分不同的類 -->
<discriminator column="class_type"></discriminator>
<property name="name" type="string"></property>
<!--
子類貓:
使用subclass節(jié)點(diǎn)映射一個(gè)子類
要指定鑒別器字段的值,如果不設(shè)置,默認(rèn)為子類的全名
-->
<subclass name="Cat" discriminator-value="cat_">
<property name="catchMouse"></property>
</subclass>
<subclass name="Tiger" discriminator-value="tiger_">
<property name="eatSheep"></property>
</subclass>
</class>
</hibernate-mapping>
寫法較為簡(jiǎn)單:所有子類用一個(gè)映射文件条摸,且映射到一張表!
但數(shù)據(jù)庫設(shè)計(jì)不合理!
(不推薦用铸屉。)
- 方式三: 每一個(gè)類都映射一張表,包括父類
關(guān)鍵節(jié)點(diǎn):joined-subclass
Animal.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">
<!-- 繼承映射,每一個(gè)類都對(duì)應(yīng)一張表,父類也對(duì)應(yīng)一張表 -->
<hibernate-mapping package="com.mrq.extends3">
<class name="Animal" table="t_animal2">
<!-- 簡(jiǎn)單繼承映射,父類屬性直接寫 -->
<id name="id">
<generator class="native"></generator>
</id>
<property name="name" type="string"></property>
<!--
子類貓:
key:指定外鍵
-->
<joined-subclass name="Cat" table="t_cat2">
<key column="t_animal_id"></key>
<property name="catchMouse"></property>
</joined-subclass>
<!-- 子類:老虎 -->
<joined-subclass name="Tiger" table="t_tiger2">
<key column="t_animal_id"></key>
<property name="eatSheep"></property>
</joined-subclass>
</class>
</hibernate-mapping>
一個(gè)映射文件钉蒲,存儲(chǔ)所有的子類; 子類父類都對(duì)應(yīng)表彻坛;
缺點(diǎn):表結(jié)構(gòu)比較負(fù)責(zé)顷啼,插入一條子類信息,需要用2條sql: 往父類插入昌屉、往子類插入钙蒙!
- 方式四: 每個(gè)子類映射一張表, 父類不對(duì)應(yīng)表(推薦使用)
關(guān)鍵節(jié)點(diǎn):union-class,同時(shí)主鍵不能使用自增長的策略
Animal.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">
<!-- 繼承映射,每一個(gè)類都對(duì)應(yīng)一張表,父類不對(duì)應(yīng)表 -->
<hibernate-mapping package="com.mrq.extends4">
<!--
abstract="true"指定實(shí)體類不對(duì)應(yīng)表,數(shù)據(jù)庫不會(huì)生成對(duì)應(yīng)的表
-->
<class name="Animal" abstract="true">
<!-- 如果使用union-subclass節(jié)點(diǎn),主鍵生成策略不能自增長 -->
<id name="id">
<generator class="uuid"></generator>
</id>
<property name="name" type="string"></property>
<!--
子類貓:
union-subclass
table 對(duì)應(yīng)的表,表的主鍵為ID列
-->
<union-subclass name="Cat" table="t_cat4">
<property name="catchMouse"></property>
</union-subclass>
<!-- 子類:老虎 -->
<union-subclass name="Tiger" table="t_tiger4">
<property name="eatSheep"></property>
</union-subclass>
</class>
</hibernate-mapping>
所有的子類都寫到一個(gè)映射文件间驮;
父類不對(duì)應(yīng)表躬厌; 每個(gè)子類對(duì)應(yīng)一張表