Hibernate知識點總結大全
day1
一. hibernate數據持久化組件
對象持久化(Object Persistence):把數據保存在永久存儲介質中(數據庫)
1.為什么要持久化:
a.內存是暫時存儲設備,斷電后數據易丟失
b.網絡傳輸無法傳輸內存中的對象,需要將對象外化
c.內存中數據查詢,組織不方便
d.內存只能存儲少量數據
2.怎樣持久化
a.對象序列化 --> 二進制流
合并存儲,粒度大,無規律
不支持檢索
只適合少數個別對象的序列化
b.用JDBC/EJB/ORM 將數據存入數據庫
用JDBC:(Java DB Connection)
優點:底層開發,控制力強(細); 效率最高; 標準的(SQL)JDBC,有可移植性
缺點:過于復雜; 代碼量大; 可維護性差(代碼重用性低);
用EJB:(Entity Java Bean)
優點:直接自動生成JDBC代碼; 持久對象(PO)的狀態由服務器管理; 聲明式的事務
缺點:功能不全(特殊的組件,不能做繼承關系); EJB容器是侵入性容器,失去OO的優點; 調試更復雜
用ORM:(object relation mapping)對象關系映射
優點:自動生成JDBC(代碼量下降); 使用(plain oldest java object---pojo),非侵入型; 提供狀態管理; 難度下降,不需要容器
缺點:由于開源, 文檔少; bug多; 技術支持差
結論: 用java開發-->必須將數據持久化-->用數據庫持久化-->須用ORM-->需要用Hibernate
二. Hibernate
1.POJO類
plain oldest java object,就是標準的Java Bean。
2.Hibernate
Hibernate -->一個可以自動的根據xml完成對象關系映射,并持久化到數據庫的開源組件。其底層也是由JDBC實現的。hibernate是通過xml文件的配置,對數據庫的底層的方言,以及數據庫連接所需的信息,以及連接數據庫的驅動。
hibernate的系統配置文件
hibernate.cfg.xml -->與數據庫建立連接(一般放在項目的根目錄下)
XXXX.hbm.xml-->用來建立類與表之間的映射關系(一般將映射類的xml文件和實體類放在一起)
3.Hibernate API
Configuragion 讀配置文件(默認名:hibernate.cfg.xml) (org.hibernate.cfg.Configuration)
生成SessionFactory:SessionFactory 重量級的對象, 線程安全的 (org.hibernate.SessionFactory),生成Session .
Session,相當于JDBC中的Connection (org.hibernate.Session),輕量級的對象,線程不安全(原則上一個線程一個Session,不要放在并發的環境中)
生成Transaction
Transaction 管理事務的對象 (org.hibernate.Transaction)
Query 查詢對象,提供面向對象的查詢語言(HQL)
4.使用hibernate編程步驟
1,配置環境,加載hibernate的jar文件,以及連接數據庫連接使用的jar文件,并配置CLASSPATH環境變量。
2,寫hibernate所需的配置文件,hibernate.cfg.xml ,XXX.hbm.xml
3,寫POJO類
4,調用hibernate API。
1)使用Configuration對象的buildSessionFactory()方法創建SessionFactory對象
2)使用SessionFactory對象openSession()方法創建Session對象。
3)使用Session的相應方法來操作數據庫,將對象信息持久化到數據庫。
5. HQL語句
Query q = s.createQuery("from Account where actNo=:actNo");
//from 類名 where 屬性名=:shuxingming
q.setLong("actNo",12345);//設置屬性名,并賦值
q.uniqueResult()//獲得匹配HQL的唯一屬性
附: 1.環境配置:
a. 類庫
*** 導入hibernate 庫文件 ***
1、在eclipse中建立一個java工程,如:hbn
2、在工程hbn的屬性--Build Path中選擇Add Libraries
3、在Add Libraries窗口中選擇User Library點next按鈕
4、在User Library窗口中點User Libraries ...按鈕
5、在User Libraries窗口中,點new按鈕
6、在New user library窗口中,填寫一個User libary name(如:hbnlib,注意不要選擇System libary 復選框),點OK按鈕回到User Liberies窗口
7、在User Liberaries窗口中選擇剛剛創建的hbnlib,點Add jars按鈕
8、在打開的文件選擇窗口中找到解壓好的hibernate的庫文件,全部選中,點"打開"按鈕,返回User Libraries窗口
9、在User Libraries窗口中點OK按鈕,返回User Library 窗口;再點Finish按鈕,結束操作
b. 驅動
c. 映射文件/配置文件 的模板
d. Eclips導入2個DTD文件
*** 導入hibernate 庫文件 ***
1、在eclipse中建立一個java工程,如:hbn
2、在工程hbn的屬性--Build Path中選擇Add Libraries
3、在Add Libraries窗口中選擇User Library點next按鈕
4、在User Library窗口中點User Libraries ...按鈕
5、在User Libraries窗口中,點new按鈕
6、在New user library窗口中,填寫一個User libary name(如:hbnlib,注意不要選擇System libary 復選框),點OK按鈕回到User Liberies窗口
7、在User Liberaries窗口中選擇剛剛創建的hbnlib,點Add jars按鈕
8、在打開的文件選擇窗口中找到解壓好的hibernate的庫文件,全部選中,點"打開"按鈕,返回User Libraries窗口
9、在User Libraries窗口中點OK按鈕,返回User Library 窗口;再點Finish按鈕,結束操作
2. hibernate.cfg.xml的寫法
<?xml version="1.0"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<!-- 與數據庫建立連接 -->
<hibernate-configuration>
<session-factory>
<property name="show_sql">true</property><!--顯示sql語句-->
<property name="format_sql">true</property><!--使顯示的sql語句格式化-->
<property name="hbm2ddl.auto">create</property><!--如果原表存在則先刪原表再創建新表-->
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:@10.10.3.237:1521:tarena</property>
<property name="connection.username">openlab</property>
<property name="connection.password">open123</property>
<property name="connection.isolation">2</property><!-- 設置事務隔離級別(2表示避免臟讀) -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property><!-- 使用的數據庫方言信息 -->
<mapping resource="com/tarena/biz/entity/Account.hbn.xml"/><!-- 映射文件的位置,和實體類放在同一目錄 -->
</session-factory>
</hibernate-configuration>
3. hibernate的映射類的XXXX.hbm.xml的寫法
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- 建立類于數據庫中表的映射關系 -->
<hibernate-mapping package="com.tarena.biz.entity"><!--映射類所在的包-->
<!-- 類名和表名的映射 -->
<class name="Account" table="account_hz">
<!-- 屬性和字段的映射 -->
<id name="aid" column="aid"><!--主鍵生成策略-->
<generator class="hilo"><!—主鍵產生方式:高低位算法->
<param name="table">pk_table</param>
<param name="column">id_value</param>
</generator>
</id>
<property name="actNo" column="actNo"></property>
<property name="balance" column="balance"></property>
<!--在hibernate中其他類型可以自動識別只有Data類型必須指名-->
</class>
</hibernate-mapping>
4. 創建數據庫表結構:
create account_hz(aid number(20) primary key,
actNo varchar(50) not null unique,
balance number(12,2) not null);
create t_hilo(hi number(20));
insert into t_hilo values(1);//必須事先插入一條記錄到表
5. 調用hibernate API
public class Test {
public static void main(String[] args) {
Account act = null;
//step1: 創建Configration對象
Configuration config = new Configuration().configure();
//step2: 獲取SessionFactorye
SessionFactory sf = config.buildSessionFactory();
//step3:獲取Session和Transaction,進行持久化操作
Session s = null;
Transaction tran = null;
try {
s = sf.openSession();
tran = s.beginTransaction();//啟動事務
act = new Account(12345, 2000.00);
s.save(act);//將帳戶對象持久化
tran.commit();//提交事務
} catch (Exception e) {
if(tran != null){
tran.rollback();
e.printStackTrace();
} finally{
if(s!=null) s.close();
}
}
}
6.Util工具類:
public class HbnUtilV1 {
private static SessionFactory sf;
static{
Configuration config = null;
try {
config = new Configuration().configure();
sf = config.buildSessionFactory();
} catch (Exception e) {
e.printStackTrace();
}
}
public static Session getSession(){
Session s = null;
if(sf!=null)
s = sf.openSession();
return s;
}
}
day3
一. one-to-many
1. one端set標簽的設置
<set name="acts" cascade="all" inverse="true"><!-- 讓one端(user)將關系的維護方交給另一端:many端(Account) -->
<key column="fid"></key>
<one-to-many class="Account"/><!-- 指定集合中存放的是Account類 -->
</set>
注:inverse="true"就是在設置如果在內存中的修改或添加了這個集合中的某一個或某幾個對象他不會將全部集合的信息同步到數據庫,而是只將集合中被修改的對象重新同步到數據庫。
2. many端
<many-to-one name="user" column="fid" cascade="save"><!-- 設置級聯操作 -->
</many-to-one>
二.狀態
1. 持久對象的狀態-->PO外在的狀態 (OOAD中 對象的狀態-->PO內在的狀態)
Transient 暫時狀態: po 和Session無關,數據庫中也無該記錄
Persistent 持久狀態: po 和Session有關,數據庫中有該記錄
Detached 游離/脫管態:po和Session無關,數據庫中有該記錄
注:和Session關系,即Session中有該對象的副本和該對象的引用
持久化對象: 即就是在數據庫中存有其相對應數據的對象,并且在內存中也有這個對象,內部狀態和外部狀態同步, 這個對象在Session的管理范圍內,也就是調用Session.save()方法同步到數據庫的對象。
臨時對象: 即在內存中剛剛創建(new)的對象,還沒有同步到數據庫,或者是調用Session.delete(),數據庫中信息被刪除了的對象。
游離對象: 也就是在數據庫中有和該對象向對應的紀錄,并且在內存中的也存在該對象,但是不在Session的管理范圍之內,如在Session關閉之后,就成了游離對象,就不會在將其改變同步到數據庫中.
內在狀態-->外在化? 是-->持久態 --否-->暫態
(類屬性) (表記錄) --是-->Session同步?否-->游離態
2. 狀態轉換: _|_-->close():關閉Session/clear()清空緩存內對象/evict()清除某個持久對象
save() / saveOrUpdate() close()/clear()/evict()
Transient--------------------->Persistent----------------------->Detached
(臨時態)<---------------------(持久態)<----------------------- (游離態)
delete() update()/saveOrUpdate()/lock()
三. 級聯操作cascade
cascade屬性是設置級聯操作的也就是在操作一端的數據如果影響到多端數據時會進行級聯操作.
1. none: 就是只對本對象進行操作,不使用級聯操作,默認級聯是none。
2. save-update: 也就是只有對象保存操作(持久化操作)或者是持久化對象的更新操作,才會級聯操作關聯對象(子對象),one2many關系中多的那一方會使用;
3. delete: 對持久化對象的刪除操作時會進行級聯操作關聯對象(子對象)。
4. all: 對持久化對象的所有操作都會級聯操作關聯對象(子對象)。
5. all-delete-orphan: 在多端進行刪除操作時,會在多端表中留下null空紀錄,設置了級聯操作為delete之會將表中表示關聯的外鍵id置成null,不會將這條紀錄也刪除掉,而把級聯設置成delete-orphan就不會留有空紀錄,而是級聯的把相關紀錄刪除掉。(級別最深)
cascade="save-update,delete-orphan"
四. 批量更新(Batch update)
1. session.flush()和session.clear()聯合使用
2. batch-size: 這個屬性寫在set標簽中,代表批量加載,也就是在加載一端的集合屬性時會一次加載指定的數量的對象,而不是默認的一個一個的加載,會提高效率,批量加載只能用于延遲加載和立即加載策略,也就是(lazy="true"或者lazy="false")。
注: lazy="true" 延遲加載,所謂的延遲加載,就是對一端的集合屬性的加載策略,就是在不使用到集合中的對象的數據就不會真正的加載集合中的對象數據,而是家在一個代理對象就相當于的一個空的容器。這也就是會出現LazyInitializationException異常,也就是沒有初始化這個代理的集合對象,在事先查詢到了集合中的對象就會初始化這個對象,如果Session沒有關閉就會在查詢加載集合中的對象信息,如果提前關閉了Session,當使用集合中的對象信息時就會有這個異常。
五. Hibernate控制的事務(ACID,atomicity consistency isolation durability)
事務保證原子操作的不可分,也就是操作的同時成功或同時失敗。
Transaction tran=session.beginTranaction();
//....事務操作
tran.commit();
tran.rollback();
以上是事務對象的方法,來實現對事務的支持。
六、hibernate的事務隔離級別
hibernate的事務隔離級別和JDBC中大致相同。
設置時要在hibernate.cfg.xml配置
<property name="hibernate.connection.isolation">4</property>
1,讀未提交的數據(Read uncommitted isolation)
2,讀已提交的數據(Read committed isolation)
4,可重復讀級別(Repeatable read isolation)
8,可串行化級別(Serializable isolation)
七、hibernate的鎖(悲觀鎖,樂觀鎖)
1. 悲觀鎖: 是由數據庫本身所實現的,會對數據庫中的數據進行鎖定,也就是鎖行,
在同一時間內只能有一個讀寫數據操作。
LockMode.UPGRADE,修改鎖,在get()方法中加上這個設置作為第三個參數。
LockMode.NONE 無鎖機制
LockMode.READ 讀取鎖(JDBC中的for update)
LockMode.WRITE 寫入鎖,不能在程序中直接使用
例如:tran = s.beginTransaction();
user = (User)s.get(User.class , userid , LockMode.UPGRADE);
user.setName("new name");
tran.commit();
還可以使用Session.lock() Query.setLockMode() Criteria.setLockMode()方法來設置鎖
2. 樂觀鎖: 也就是通過對記錄加上某些信息來解決并發訪問的問題。
解決沖突的手段: 加上版本versionNo
if(read_versionNo=versionNo) write versionNo++ else do it again
<version name="version"/>必須緊跟在<id>之后
persist就只是將級聯對象也持久化到數據庫。
day4
一. many-tomany(以學生選課為例)
1.Student.hbm.xml文件的設置
<set name="students" table="ying_enrollment" cascade="none"><!--cascade 設置為none,為了減少update更新冗余--!>
<key column="fcid"></key>
<many-to-many class="Student" column="fsid"/>
<!-- table ying_enrollment 中間表,體現多2多關系 -->
</set>
2.Course.hbm.xml文件的設置
<set name="courses" table="ying_enrollment" cascade="save-update" inverse="true"
lazy="false"><!-- lazy表示是否延遲加載set對象,false使Session關閉后仍然可用 -->
<key column="fsid"></key>
<many-to-many class="Course" column="fcid"/>
<!-- table ying_enrollment 中間表,體現多2多關系 -->
</set>
在Student實體類中要設置Student與Course的關聯:
public void registerCourse(Course c) {
courses.add(c);
c.getStudents().add(this);
}
3.注: hibernate的實現方式是通過中間表(ying_enrollment),間接實現了多對多關系,實際上也是將多對多拆分
成兩個雙向的一對多關系。
Hibernate知識點小記
1.cascade
cascade屬性是設置級聯操作的也就是在操作一端的數據如果影響到多端數據時會進行級聯操作,一對一的時候直接
寫在標簽上,其他的要寫在set標簽上
cascade="none|save-update|all|all-delete-orphan"
none 就是不使用級聯操作,默認級聯是none。
save-update 也就是只有對象保存操作(持久化操作)或者是持久化對象的更新操作,才會級聯操作關聯對象(子對象)。
all 對持久化對象的所有操作都會級聯操作關聯對象(子對象)。
all-delete-orphan,在多端進行刪除操作時,會再多端表中留下null空紀錄,設置了級聯操作為delete之會將表中表示關聯的外鍵id置成null,
不會將這條紀錄也刪除掉,而把級聯設置成delete-orphan就不會留有空紀錄,而是級聯的把相關紀錄刪除掉。
2.inverse
inverse="true"就是在設置如果在內存中的修改或添加了這個集合中的某一個或某幾個對象他不會將全部集合的信息同步到數據庫,
而是只將集合中被修改的對象重新同步到數據庫
3.lazy
lazy=“true”
延遲加載,所謂的延遲加載,就是對一端的集合屬性的加載策略,就是在不使用到集合中的對象的數據就不會真正的加載集合中的對象數據,
而是家在一個代理對象就相當于的一個空的容器。這也就是會出現LazyInitializationException異常,也就是沒有初始化這個代理的集合對象,
在事先查詢到了集合中的對象就會初始化這個對象,如果Session沒有關閉就會在查詢加載集合中的對象信息,如果提前關閉了Session,
當使用集合中的對象信息時就會有這個異常。
4.fetch
fetch=“join”,這就是使用了預先抓取策略,也就是針對關聯的對象的加載策略,在使用到關聯對象的信息時會再發送sql語句,
如果不使用fetch=“join”,就會不使用表連接而是先查出一端的關聯id再一條一條的發送sql語句查詢到關聯對象信息,使用了fetch=“join”
就會使用表連接將關聯對象信息直接查尋出來的。fetch=“lazy”這個是默認的設置。
二. inherit_mapping
建表策略(以Account CheckAccount DebitAccount 類為例)
(1)只為具體類建表
只為具體類建表,使用于不使用多態的情況下,具體類之間沒有繼承關系時適用
需要針對每個類寫映射配置文件,就和普通的單表映射的xml文件相同。
也可以使用一個xml文件來進行映射,可以通過寫union-subclass標簽來表現其關系
這里不能使用id生成策略中的native,而是要指定特定的生成策略。
例:
<union-subclass name="CheckAccount"
table="t_check_account"><!-- 只為具體類建表 -->
<property name="overdraft"></property>
</union-subclass>
<union-subclass name="DebitAccount"
table="t_debit_account"><!-- 只為具體類建表 -->
<property name="currency"></property>
</union-subclass>
(2)每個類建一個表
每個類建一個表,可以有效減少數據的冗余,減少字段,查詢效率不很高。
正對每個類建一個表,只要寫一個配置文件來進行類的映射即可
映射文件中的子類可以使用join-subclass標簽來表示,并且引用父類的主鍵作為共享主鍵,就是不需要指定id生成策略
例:
<joined-subclass name="CheckAccount"
table="ying_check_account"><!-- 每一個類建一個表 -->
<key column="faid"></key>
<property name="overdraft"></property>
</joined-subclass>
<joined-subclass name="DebitAccount"
table="ying_debit_account"><!-- 每一個類建一個表 -->
<key column="faid"/>
<property name="currency"></property>
</joined-subclass>
(3)所有類建一個表
所有類只建一個表,查尋效率比較高,但是會產生很多空間浪費,當子類中有非空約束,
就不大適用了,這是對于子類可以使用subclass標簽表示。
例:
<subclass name="CheckAccount"
discriminator-value="c">
<property name="overdraft"></property>
</subclass>
<subclass name="DebitAccount"
discriminator-value="d">
<property name="currency"></property>
</subclass>
注: 不考慮多態時,最好是用只針對具體類建表(2個表),而考慮多態時盡量使用所有類建一個表,
只有當子類中的屬性過多是才考慮每個類建一個表的策略。
(4)三種策略的比較:(詳見孫衛琴主編的精通Hibernate一書P424)
如果不需要支持多態查詢和多態關聯,可以采用每個具體類對應一個表
如果需要持多態查詢和多態關聯,并且子類包含的屬性很多,可以采用所有類對應一個表
如果需要持多態查詢和多態關聯,并且子類包含的屬性很多,可以采用每個類對應一個表
day5
一. 組件映射:
把持久類換成非持久類保存在數據庫中;
一個類如果有一個獨立的oid,則該類是一個持久類,否則是一個組件.
目的: 簡化操作
適應情況:
兩個類之間具有組合(或強度較小的聚合)關系
組合/聚合
PO類(整體)<------------->組件類(部分)
1:1/1:m
| 組件映射(1:1)
------->
集合映射(1:m)
組件是退化的PO;
oid消失
組件映射是退化的基數映射
只要有可能,就要用組件映射
例如:(以Account Address類為例,只需要寫一個Account.hbm.xml)
<!-- 將Set中的所有Address類作為自己的一個組成部分-->
public class Account {
private Long aid;
private long actNo;
private double balance;
private Address address;
}
<set name="address" table="yinglh_address">
<key column="fid"></key>
<composite-element class="Address">
<property name="zipcode"></property>
<property name="city"></property>
<property name="street"/>
</composite-element>
</set>
二. 集合映射
1.集合比較
集合 順序 重復
Set 無 否
List 有 能
Map 有 key(否)
bag 無 能
1、set映射
關聯對象的屬性除了外鍵之外,只有1、2個屬性,那么就可以使用set映射
使用了set標簽的element元素,不用創建關聯對象就可以實現單向一對多的關聯關系
public class Room implements Serializable{
private int id;
private String roomNumber;
private Set<String> equipments = new HashSet<String>();
private Set<Image> images = new HashSet<Image>();
}
<set name="equipments" table="equipment_set">
<key column="roomid" foreign-key="fk_equip_room_set"/>
<element column="description" type="string" length="128" not-null="true"/>
</set>
<set name="images" table="image_set">
<key column="roomid" foreign-key="fk_img_room_set"/>
<composite-element class="Image">
<property name="path" column="path" type="string" length="50" not-null="true"/>
<property name="width" column="width" type="integer" />
<property name="height" column="height" type="integer" />
</composite-element>
</set>
2、map映射
非常有用的一種集合映射
public class Room implements Serializable{
private int id;
private String roomNumber;
private Map<String, String> equipments = new HashMap<String, String>();
private Map<String, Image> images = new HashMap<String, Image>();
}
<map name="equipments" table="equipment_map">
<key column="roomid" foreign-key="fk_equip_room_map"/>
<map-key column="name" type="string" length="15" />
<element column="description" type="string" length="128" not-null="true"/>
</map>
<map name="images" table="image_map">
<key column="roomid" foreign-key="fk_img_room_map"/>
<map-key column="name" type="string" length="15" />
<composite-element class="Image">
<property name="path" column="path" type="string" length="50" not-null="true"/>
<property name="width" column="width" type="integer" />
<property name="height" column="height" type="integer" />
</composite-element>
</map>
3.list映射
public class Quiz {
private Long oid;
private String quizNo;
private List answers = new ArrayList();
}
<list name="answers" table="yinglinhai_answers">
<key column="fid"></key>
<list-index column="indexs"></list-index>
<element column="answer" type="string"></element>
</list>
4.bag映射
public class Lab {
private Long oid;
private int labNo;
private List records = new ArrayList();
}
<idbag name="records" table="yinglinhai_records">
<collection-id type="long" column="cid">
<generator class="increment"></generator>
</collection-id>
<key column="fid"/>
<element column="record" type="string"></element>
</idbag>
三、HQL
1、query
(1) session.createQuery("from User");
session.createCriteria(User.class);
(2)查詢的分頁顯示
query.setFirstResult(0);
query.setMaxResult(10);
(a) MySql
select * from student where limite ?,?
(b) Oracle
select * from (select row_.* ,rownum rownum_
from (select student0_.id as id0_,
from student student0_) row)
where rownum_ <= ?
and rownum_ > ?
(3)在配置文件中的query
<query name="findByName">
<![CDATA[ --- 當作字符串看,不做特殊處理
from User u where u.namelike :name
]]>
</query>
session.getNamedQuery("findByName").setString("name",name);
from User; 返回對象數組,不可以強轉
select u from User u; 返回對象集合,對于集合中的每個元素都可以強轉成User對象
2、Dynamic query
public Collection findStudents(Student student){
StringBuffer sb = new StringBuffer("select student from Student student where student.id>-1 ");
--- student.id>-1 永遠為真,再添加條件的時候都用and即可,
避免判斷用where還是and
if(student.getName() != null)
sb.append("and student.name=:name");
if(student.getBirthday() != null)
sb.append("and student.birthday=:birthday");
if(student.getEmail() != null)
sb.append("and student.email=:email");
……
}
3、QBE
屬性多時,效率高,語句少
忽略主鍵、version、關系(查詢當前對象)
與QBC結合,彌補id查詢
Example.excludeProperty("param"); --- 查詢時忽略某個屬性