Hibernate出現的原因:
直接使用JDBC操作數據庫的步驟很繁瑣;
JDBC操作的是關係型數據庫;
我們用JAVA開發程序,則使用面向對象的思想;
Hibernate正是在這兩種不同的模型之間建立關聯,
Hibernate給我們提供了利用面向對象的思想來操作關係型數據的接口
關係模型(Relational Model)
關係模型把世界看作是由實體(Entity)和聯繫(Relationship)構成的。
所謂實體就是指現實世界中具有區分與其它事物的特徵或屬性並與其它實體有聯繫的對象。在關係模型中實體通常是以表的形式來表現的。表的每一行描述實體的一個實例,表的每一列描述實體的一個特徵或屬性。
所謂聯繫就是指實體之間的關係,即實體之間的對應關係。
1:1 1:n m:n
關係數據庫
表
字段
主鍵
外鍵
面向對象三大特徵:封裝、繼承(一般與特殊)
、多態(覆蓋與重載)
類,對象,屬性,關係
一般與特殊關係(is a)
組成(has a)
關聯及其多重性1:1 1:n m:n
雙向關聯與單向關聯
對象關係映射(Object Relational Mapping,簡稱ORM)
ORM是一種爲了解決面向對象與關係數據庫存在的互不匹配的現象的技術。簡單的說,ORM是通過使用描述對象和數據庫之間映射的元數據,將java程序中的對象自動持久化到關係數據庫中。本質上就是將數據從一種形式轉換到另外一種形式。什麼是ORM?
面向對象的開發方法是當今企業級應用開發環境中的主流開發方法
關係數據庫是企業級應用環境中永久存放數據的主流數據存儲系統
字母O起源於“對象”(Object)而R則來自於“關係”(Relational)。幾乎所有的程序裏面,都存在對象和關係數據庫。在業務邏輯層和表現層中,我們是面向對象的。當對象信息發生變化的時候,我們需要把對象的信息保存在關係數據庫中。
當你開發一個應用程序的時候(不使用O/R Mapping),你可能會寫不少數據訪問層的代碼,用來從數據庫保存,刪除,讀取對象信息等等。而這些代碼寫起來總是重複的。
對象-關係映射模式
屬性映射
類映射
關聯映射
一對一一對多多對多
Hibernate
對象/關係映射一直都是數據庫技術中的難點,儘管人們提出了許多方案解決這個問題,但都不能完全做到即便利又高效。
EJB的推出讓人們看到了希望,但實踐證明實體Bean的效率並不高,並且還十分難於被初學者理解。由Gavin King創建的Hibernate框架,從某種程序上正在朝着正確的方向邁進,並且得到越來越多IT從業人員的認可。就像當年的Struts框架一樣,Hibernate也已經在許多項目中得到廣泛應用。
Hibernate由於投注了更多的精力在提升效率上,使用起來又十分方便,新版的EJB規範正在向Hibernate方向靠攏。正是由於得到廣泛的認可,Hibernate已經成爲程序員必須掌握的技術之一。
Hibernate能幫助我們利用面向對象的思想,開發基於關係型數據庫的應用程序
第一:將對象數據保存到數據庫
第二:將數據庫數據讀入對象中
Hibernate的基本組件
實體類
實體類映射文件
重點學習的部分Hibernate配置文件
輔助工具
Hibernate核心接口
Hibernate
運行的底層信息:數據庫的URL用戶名密碼JDBC驅動類,數據庫Dialect,
數據庫連接池等(
hibernate.cfg.xml
)
Hibernate映射文件(*.hbm.xml)。
Hibernate配置的兩種方法:
屬性文件(hibernate.properties)
調用代碼:
Configuration cfg = new Configuration();
XML文件(hibernate.cfg.xml)
調用代碼:
Configuration cfg = new Configuration().configure();
SessionFactory(會話工廠)
概述:
它與數據庫綁定,一個數據庫對應一個SessionFactory,關於數據庫中的所有東西(例如:表之間的關聯)都放在SessionFactory中了,二級緩存與SessionFactory中的二級緩存就是進程級的緩存,就相當於Web中的Application對象,因此它是重量級的,它的創建時間比較耗時,所以該對象只創建一個,不要頻繁創建。應用程序從SessionFactory(會話工廠)裏獲得Session(會話)實例。它在多個應用線程間進行共享。通常情況下,整個應用只有唯一的一個會話工廠——例如在應用初始化時被創建。然而,如果你使用Hibernate訪問多個數據庫,你需要對每一個數據庫使用不同的會話工廠(即:需要爲每個數據庫都創建一個會話工廠)會話工廠緩存生成的SQL語句和Hibernate在運行時使用的映射元數據。
調用代碼SessionFactory sessionFactory = cfg.buildSessionFactory();
說明:
SessionFactory由Configuration對象創建,所以每個Hibernate配置文件,實際
上是對SessionFactory的配置。
Session(會話)概述:
它是操縱Hibernate進行CRUD(增、刪、改、查)操作的,即Hibernate在進行CRUD操作時必須使用Session,Session不同於JDBC中的Connection。
也可以這樣理解Session對Connection又進行了一層包裝,打開一個Session
並不等於打開一個Connection,因爲Session的功能要比Connection強,Session不僅具有Connection的功能,還具有管理一級緩存的功能,例如在Hibernate中保存一個對象,Hibernate要完成兩個功能,一是發出相應的SQL語句將數據存儲到數據庫表中,另一個功能是把當前這個對象放入到緩存中Session不是線程安全的,它代表與數據庫之間的一次操作,它的概念介於Connection和Transaction之間,由於不是線程安全的,所以不能多線程共享使用,否則會產生莫明其妙的問題,Session通常是一個業務請求過來,就open出一個Session,業務請求完畢後Session隨之關閉,通常是Session關閉後,與該Session對應的事務就關閉了,即一個業務請求對應一個事務。
Session也稱爲持久化管理器,因爲它是與持久化有關的操作接口。
Session通過SessionFactory打開,在所有的工作完成後,需要關閉。它與Web的
HttpSession沒有任何關係。
調用代碼Session session = sessionFactory.openSession();
持久化對象的狀態
瞬時對象
(Transient Objects)
使用new 操作符初始化的對象不是立刻就持久的。它們的狀態是瞬時的,也就是說它們沒有任何跟數據庫表相關聯的行爲,只要應用不再引用這些對象
(不再被任何其它對象所引用),它們的狀態將會丟失,並由垃圾回收機制回收。
特點是:數據庫中沒有與之對應的記錄。
持久化對象
(Persist Objects)
持久實例是任何具有數據庫標識的實例。它有持久化管理器Session統一管理,
持久實例是在事務中進行操作的——它們的狀態在事務結束時同數據庫進行同步。當事務提交時,通過執行SQL的INSERT、UPDATE和DELETE語句把內存中的狀態同步到數據庫中。特點是:數據庫中有與之對應的記錄,同時session
中也有該記錄對應的對象存在,即受session管理。
離線對象
(Detached Objects)
Session
關閉之後,持久化對象就變爲離線對象。離線表示這個對象不能再與數據庫保持同步,它們不再受Hibernate管理。特點是:數據庫中有與之對應的記錄,同時session中沒有該記錄對應的對象存在,即不受session管理。
持久化對象的生命週期(lifecycle)
Transaction(事務)
概述:
它將應用代碼從底層的事務實現中抽象出來——這可能是一個JDBC事務,一個JTA用戶事務或者甚至是一個公共對象請求代理結構(CORBA)——允許應用通過一組一致的API控制事務邊界。這有助於保持Hibernate應用在不同類型的執行環境或容器中的可移植性。
調用代碼:
Transaction tx = session.beginTransaction();
注:使用Hibernate進行操作時必須顯式的調用Transaction(默認:autoCommit=false)。
Query接口
概述:
Query(查詢)接口允許你在數據庫上執行查詢並控制查詢如何執行。查詢語句使用HQL或者本地數據庫的SQL方言編寫。
調用代碼:
Query query = session.createQuery("from User");
注意: 在Hibernate 3.x中不建議使用如下包中的類: org.hibernate.classsic.*
這個包下面的類,是Hibernate 3.x爲了向下兼容Hibernate 2.x才保留下來的。
Hibernate的對象關係映射
映射文件的基本結構舉例
<?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>
<class name="com.domain.User">
<id name="id">
Hibernate 3.x 基礎
Hibernate出現的原因:
直接使用JDBC操作數據庫的步驟很繁瑣;
JDBC操作的是關係型數據庫;
我們用JAVA開發程序,則使用面向對象的思想;
Hibernate正是在這兩種不同的模型之間建立關聯,Hibernate給我們提供了利用面向對象的思想來操作關係型數據的接口。
關係模型(Relational Model)
關係模型把世界看作是由實體(Entity)和聯繫(Relationship)構成的。
所謂實體就是指現實世界中具有區分與其它事物的特徵或屬性並與其它實體有聯繫的對象。在關係模型中實體通常是以表的形式來表現的。表的每一行描述實體的一實例,表的每一列描述實體的一個特徵或屬性。
所謂聯繫就是指實體之間的關係,即實體之間的對應關係。
1:1 1:n m:n 關係數據庫
表(字段,主鍵,外鍵)
面向對象三大特徵:封裝、繼承(一般與特殊)、多態(覆蓋與重載)
類對象屬性關係
一般與特殊關係(is a)
組成(has a) 關聯及其多重性 1:1 1:n m:n 雙向關聯與單向關聯
對象關係映射(Object Relational Mapping,簡稱ORM)
ORM是一種爲了解決面向對象與關係數據庫存在的互不匹配的現象的技術。簡單說,ORM是通過使用描述對象和數據庫之間映射的元數據,將java程序中的對象自動持久化到關係數據庫中。本質上就是將數據從一種形式轉換到另外一種形式。
什麼是 ORM?
面向對象的開發方法是當今企業級應用開發環境中的主流開發方法,關係數據庫是企業級應用環境中永久存放數據的主流數據存儲系統
字母O起源於“對象”(Object),而R則來自於“關係”(Relational)。幾乎所有的程序裏面,都存在對象和關係數據庫。在業務邏輯層和表現層中,我們是面向對象的。當對象信息發生變化的時候,我們需要把對象的信息保存在關係數據庫中。 當你開發一個應用程序的時候(不使用O/R Mapping),你可能會寫不少數據訪問層的代碼,用來從數據庫保存,刪除,讀取對象信息等等。而這些代碼寫起來總是重複的。
對象-關係映射模式
屬性映射
類映射
關聯映射 一對一 一對多 多對多
Hibernate 對象/
關係映射一直都是數據庫技術中的難點,儘管人們提出了許多方案解決這個問題,
但都不能完全做到即便利又高效。EJB的推出讓人們看到了希望,但實踐證明實體Bean的效率並不高,並且還十分難於被初學者理解。由Gavin King創建的Hibernate框架,從某種程序上正在朝着正確的方向邁進,並且得到越來越多IT從業人員的認可。就像當年的Struts框架一樣,Hibernate也已經在許多項目中得到廣泛應用。Hibernate由於投注了更多的精力在提升效率上,使用起來又十分方便,新版的EJB規範正在向Hibernate方向靠攏。正是由於得到廣泛的認可,Hibernate已經成爲程序員必須掌握的技術之一。
Hibernate能幫助我們利用面向對象的思想,開發基於關係型數據庫的應用程序
第一:將對象數據保存到數據庫
第二:將數據庫數據讀入對象中
基於B/S的典型三層架構
Hibernate配置的兩種方法:屬性文件(hibernate.properties)
調用代碼:Configuration cfg = new Configuration();
XML文件(hibernate.cfg.xml)
調用代碼:Configuration cfg = new Configuration().configure();
SessionFactory(會話工廠)
概述:
它與數據庫綁定,一個數據庫對應一個SessionFactory,關於數據庫中的所有東西(例如:表之間的關聯)都放在SessionFactory中了,二級緩存與SessionFactory相關,二級緩存就是進程級的緩存,就相當於Web中的Application對象,因此它是重量級的它的創建時間比較耗時,所以該對象只創建一個,不要頻繁創建。應用程序從SessionFactory(會話工廠)裏獲得Session(會話)實例。它在多個應用線程間進行共享。通常情況下,整個應用只有唯一的一個會話工廠——例如在應用初始化時被創建。然而,如果你使用Hibernate訪問多個數據庫,你需要對每一個數據庫使用不同的會話工廠(即:需要爲每個數據庫都創建一個會話工廠)。
會話工廠緩存了生成的SQL語句和Hibernate在運行時使用的映射元數據。
調用代碼: SessionFactory sessionFactory = cfg.buildSessionFactory(); 說明:SessionFactory由Configuration對象創建,所以每個Hibernate配置文件,實際上是對SessionFactory的配置。
Session(會話)概述:
它是操縱Hibernate進行CRUD(增、刪、改、查)操作的,即Hibernate在進行
CRUD操作時必須使用Session,Session不同於JDBC中的Connection。也可以這樣理解Session對Connection又進行了一層包裝,打開一個Session並不等於打開一個Connection因爲Session的功能要比Connection強,Session不僅具有Connection的功能,還具有管理一級緩存的功能,例如在Hibernate中保存一個對象,Hibernate要完成兩個功能,一是發出相應的SQL語句將數據存儲到數據庫表中,另一個功能是把當前這個對象放入到緩存中。 Session不是線程安全的,它代表與數據庫之間的一次操作,它的概念介於Connection和Transaction之間,由於不是線程安全的,所以不能多線程共享使用,否則會產生莫明其妙的問題,Session通常是一個業務請求過來就open出一個Session,業務請求完畢後Session隨之關閉,通常是Session關閉後,與該Session對應的事務就關閉了,即一個業務請求對應一個事務。Session也稱爲持久化管理器,因爲它是與持久化有關的操作接口。
Session通過SessionFactory打開,在所有的工作完成後,需要關閉。它與Web層的HttpSession沒有任何關係。
調用代碼
Session session = sessionFactory.openSession();
持久化對象的狀態
瞬時對象(Transient Objects) 使用new 操作符初始化的對象不是立刻就持久的。它們的狀態是瞬時的,也就是說它們沒有任何跟數據庫表相關聯的行爲,只要應用不再引用這些對象(不再被任何其它對象所引用),它們的狀態將會丟失,並由垃圾回收機制回收。 特點是:數據庫中沒有與之對應的記錄。
持久化對象(Persist Objects)
持久實例是任何具有數據庫標識的實例。它有持久化管理器Session統一管理,持久實例是在事務中進行操作的——它們的狀態在事務結束時同數據庫進行同步。當事務提交時,通過執行SQL的INSERT、UPDATE和DELETE語句把內存中的狀態同步到數據庫中。特點是:數據庫中有與之對應的記錄,同時session中也有該記錄對應的對象存在,即受session管理。
離線對象(Detached Objects) Session關閉之後,持久化對象就變爲離線對象。離線表示這個對象不能再與數據庫保持同步,它們不再受Hibernate管理。特點是:數據庫中有與之對應的記錄,同時session中沒有該記錄對應的對象存在,即不受session管理。
持久化對象的生命週期(lifecycle)
Transaction(事務)
概述:
它將應用代碼從底層的事務實現中抽象出來——這可能是一個JDBC事務,一個JTA用戶事務或者甚至是一個公共對象請求代理結構(CORBA)——允許應用通過一組一致的API控制事務邊界。這有助於保持Hibernate應用在不同類型的執行環境或容器中的可移植性。
調用代碼:
Transaction tx = session.beginTransaction();
注:使用Hibernate進行操作時必須顯式的調用Transaction(默認autoCommit=false)。
Query
接口
概述:Query(查詢)接口允許你在數據庫上執行查詢並控制查詢如何執行。查詢語句HQL或者本地數據庫的SQL方言編寫。
調用代碼: Query query = session.createQuery("from User"); 注意:
在Hibernate 3.x中不建議使用如下包中的類:org.hibernate.classsic.*
這個包下面的類,是Hibernate 3.x爲了向下兼容Hibernate 2.x才保留下來的。
Hibernate的對象關係映射
映射文件的基本結構舉例
<?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>
<class name="com.domain.User">
<id name="id">
</id> <property name="name"/>„ </class> </hibernate-mapping>
hibernate-mapping元素: 可以包含的子元素 class –描述被映射的類subclass/joined-subclass –在繼承關係的映射中會用到
query – 將查詢語句定義在配置文件class元素: 常用屬性 name – 實體類的類名 table –被映射到數據庫表的名稱可以包含的常見子元素 id –
主鍵定義 property – 屬性定義 關係映射定義(一對多、多對一等)主鍵- id: 被映射的類必須要有一個id定義 通常使用邏輯主鍵 邏輯主鍵:沒有意義的唯一標識符 業務主鍵:有意義的唯一標識符 Hibernate使用generator類來生成主鍵Hibernate自帶了很多generator(不同的主鍵生成策略int/long – native String - uuid 我們也可以定義自己的generator實現IdentifierGenerator接口,一般情況下不需要實現自己的generator。
主鍵生成策略
generator 主鍵生成器,每個主鍵都必須定義相應的主鍵生成策略。它用來爲持久化實
例生成唯一的標識。Hibernate內置的主鍵生成策略
數據庫提供的主鍵生成機制。identity、sequence(序列)。
外部程序提供的主鍵生成機制。increment (遞增)(這種策略在做集羣時,不應該使用,因爲該策略是在同一個JVM中是唯一的) ,hilo(高低位) ,seqhilo(使用序列的高低位 ),uuid.hex(使用了IP地址+JVM的啓動時間(精確到1/4秒+系統時間+一個計數器值(在JVM中唯一) ),uuid.string。 其它:native(本地),assigned(手工指定),foreign(外部引用)。
普通屬性映射 - property:
<property name="property_name"/>
可使用的常見屬性如下:
name – 對應類的屬性名稱type –
指定屬性的類型,一般情況下可以不用指定,由hibernate自動匹配(可參考文檔中的有關說明)length –指定長度 column – 指定屬性所對應的數據庫字段的名稱,如果不指定,就是屬性的名稱實體類的設計原則: 實現一個默認的(即無參數)構造方法。 提供一個標識屬性(即id,當然也可以用別的名稱,它一般和業務無關),這個屬性就是與數據庫中的主鍵字段對應的。
使用非final類型的類,即實體類最好不定義成
final類型的,否則在使用時,有時Hibernate會拋出異常。爲實體類的中屬性提供setter/getter方法,屬性的訪問控制符可以是private但對應的setter/getter方法的訪問控制符一般爲public。
Hibernate 3.x 關聯映射
many-to-one(多對一關聯映射)
User-Dept 多個用戶屬於某個組 從代碼上體現爲:
public class Dept {
privte String id;
private String name;
}
public class User{
private String id;
privte String name;
private Dept dept;
public Dept getDept (){return dept;}
public void setDept (Dept dept){
this.dept = dept;
}
}
many-to-one
的映射最常用,也是最容易理解和編寫的
<many-to-one name="dept" column="deptid"/>
生成的
DDL
語句如下
create table T_Dept (
id varchar(255) not null,
name varchar(255),
primary key (id))
create table User (
id varchar(255) not null,
name varchar(255),
password varchar(255),
createTime datetime,
expireTime datetime,
deptid varchar(255),
primary key (id))
alter table User add index
FK285FEBC3D18669 (deptid),
add constraint
FK285FEBC3D18669 foreign key (deptid) references T_Dept (id)
從生成的DDL語句,我們可以知道,實際上是在User表上建立了一個指向Group表的外鍵關聯。
<many-to-one name="dept" column="deptid"/>
的含義:
該標籤是
加在多的一端
,它會在多的一端加入一個字段,這個字段的名字就叫做
deptid,這個字段加上後它就會作爲外鍵參照name屬性指定的dept(即對應的實體類是Dept,即叫做一的一端)的主鍵字段。也就是說它會在多的一端對應的表中加入一個字段,這個字做作爲外鍵字段參照一的一端對應表的主鍵字段,這個外鍵字段將來存儲的就是一的一端的主鍵。
重要屬性-cascade(級聯)級聯的意思是指定兩個對象之間的操作聯動關係,
對一個對象執行了操作之後,對其指定的級聯對象也需要執行相同的操作 總共可以取值爲:all、none、save-update、delete all:代表在所有的情況下都執行級聯操作none:在所有情況下都不執行級聯操作save-update:在保存和更新的時候執行級聯操作 delete:在刪除的時候執行級聯操作
如:<many-to-one name="group"column="groupid“ cascade="all"/>
級聯只對添加、修改、刪除起作用,對加載(查詢)不起作用(1)無cascade配置的User-Group執行代碼: <many-to-one name="dept" column="deptid"/>
java代碼:
Dept dept = new Dept();
dept.setName("jkjk");
User user = new User();
user.setName("管理員");
user.setDept(dept);
session.save(user);
執行結果:
拋出org.hibernate.TransientObjectException異常,以上代碼中,dept對象是一個瞬時對象,user對象引用了一個瞬時對象,所以在保存的時候出現異常。(2)無cascade
配置時正確的java執行代碼:
爲避免異常,我們可以需要將dept對象保存
Dept dept = new Dept();
dept.setName("jkjk");
執行save操作之後,dept對象變成持久化對象的狀態
悲觀鎖:
鎖的含義--鎖具有排它性,也就是說當一上線程將數據鎖住後,其它線程不能進行讀取或修改被鎖定的數據。
悲觀鎖的實現,通常依賴於數據庫機制,在整個過程中將數據鎖定,其它任何用戶都不能讀取或修改。 悲觀鎖適用場合:
數據庫自增字段(序列)的實現
一般適用於短事務,因爲長事務執行時間較長,在此過程中數據會被一直鎖定,其它用戶不能進行訪問和修改,因此併發性不好,但是如果此項業務的確需要進行串行訪問,針對此現象只能使用悲觀鎖,因爲此時需要的數據安全性較高。即在要求數據準確性和安全性較高的情況下,可以採用悲觀鎖來實現,但是同時還需要考慮訪問速度性能下降的現實問題。
Session接口的load()方法有兩種重載形式:
一種是正常支持延遲加載數據;
另一種是使用帶鎖的方式來加載數據。 LockMode(鎖模式)常用的參數如下:
LockMode.UPGRADE
LockMode.UPGRADE_NOWAIT:專門爲Oracle數據庫提供的。
public void testLoad(){
Inventory inv = null; try{
inv = (Inventory)session.load(Inventory.class, 1, LockMode.UPGRADE_NOWAIT); } catch(Exception e){ } finally{ } }
樂觀鎖:
樂觀鎖實質上不是鎖,它是一種衝突檢測手段,也就是數據讀取上來後,可以隨意修改,但是在更新或保存的時候它進行檢測,如果檢測到的數據版本低於數據庫中該記錄的版本號時,提示不能進行更新。
樂觀鎖的併發性較好,因爲當一個線程修改數據時,其它線程是可以繼續訪問數據。
例如:SVN版本控制器就是基於樂觀鎖的思想實現的,而微軟公司的VSS版本控制器則是基於悲觀鎖的思想實現的。
Hibernate對樂觀鎖的支持主要體現在實體映射文件上,如下所示: <!-- 配置optimistic-lock屬性指定當前實體類使用樂觀鎖,其中version表示樂觀鎖的實現採用版本檢測方式 -->
<class name="Inventory" table="t_inventory" optimistic-lock="version"> <id name="id" column="i_id" length="32"> <generator class="sequence">
<param name="sequence">myseq</param> </generator> </id>
<!-- 加入<version.../>標籤,用於映射實體類中用於保存樂觀鎖標記的屬性,它必須在<id.../>標籤的後面出現-->
<version name="version" column="i_version"/> <property name="itemName" column="v_itemName"/> <property name="counts" column="i_counts"/> </class>
實體類的設計:
public class Inventory { private int id;
private String itemName; private int counts;
//用於存儲表中版本檢測字段的值,類型必須爲int private int version; public Inventory(){
} /*
* 提供所有屬性的setXXX和getXXX方法 */ }
大多數基於數據版本記錄機制(version)實現,一般是在數據庫表中加入一個version字段,讀取數據時將版本號一同讀出,之後更新數據時版本號自動加1(Hibernate會自動完成),如果提交數據時版本號小於數據表中的版本號,則認爲數據是過期的,否則給予更新。
Hibernate 3.x——HQL查詢語言
示例:Hibernate3_10_HQL Hibernate查詢:
數據查詢與檢索是Hibernate中的一個亮點。相對其他ORM實現而言,Hibernate提供了靈活多樣的查詢機制。
標準化對象查詢(Criteria Query):以對象的方式進行查詢,將查詢語句封裝爲對象操作。優點:可讀性好,符合Java 程序員的編碼習慣。缺點:不夠成熟,不支持投影(projection)或統計函數(aggregation)
Hibernate語言查詢(Hibernate Query Language,HQL):它是完全面向對象的查詢語句,查詢功能非常強大,具備多態、關聯等特性 。Hibernate官方推薦使用HQL進行查詢。 Native SQL Queries(原生SQL查詢):直接使用標準SQL語言或跟特定數據庫相關的SQL進行查詢。
Hibernate語言查詢(Hibernate Query Language,HQL): HQL用面向對象的方式生成SQL語句:
以類和屬性來代替表和數據列; 支持多態; 支持各種關聯; 減少了SQL的冗餘;
HQL支持所有的關係數據庫操作:
連接(joins,包括Inner/outer/full joins),笛卡爾積(cartesian products); 投影(projection)--查詢一到多個屬性;
聚合(Aggregation,max, avg)和分組(group); 排序(Ordering); 子查詢(Subqueries);
SQL函數(SQL function calls)
簡單示例:查詢用戶名以“王”開頭的所有用戶。 Query query = session.createQuery(
"from User user where user.name like '王%'"); List users = query.list();
複雜示例:從User和Group中查找屬於“admin”組的所有用戶。 Query query = session.createQuery(
"from User user where user.group.name='admin'"); 如果用傳統的SQL則查詢語句如下:
select user.userId as userId, user.name as name, user.groupId as groupId, user.idCardId as idCardId from TBL_USER user, TBL_GROUP group where (group.groupName='admin' and user.groupId=group.groupId)
HQL語言-簡單屬性查詢: (1)單一屬性查詢:
List users = session.createQuery("select name from User").list(); 說明:
List集合中此時存儲的是name屬性集合,其類型爲實體類(即User)的name屬性的類型(本例爲String類型),所以List集合中對象的類型取決於實體類中屬性的類型。
(2)多屬性查詢: List users =
session.createQuery("select id, name from User ").list(); 說明:
List集合中此時存儲的對象數組(即Hibernate查詢多個屬性時返回的是對象數組,一條記錄爲一個對象數組,最後將所有對象數組存儲到集合中),其中對象數組的長度與查詢的屬性個數是相同的(即select關鍵字後面有多少個屬性,將來對象數組的長度就是多少);
爲什麼會是Object類型的數組?這是因爲實體類中屬性的類型是不同的,所以用Object類型表示更爲通用。
數組下標元素存儲的對象類型與實體類中屬性的類型相同。
(3)採用HQL動態實例化對象方式返回所要查詢的屬性: List users =
session.createQuery("select new User(id, name) from User").list(); 說明:
List集合中此時存儲的是User對象,但是該User對象並不是實體對象,而是我們自己new出來的(即不是Hibernate自動封裝的),因此不受Session統一管理,此時的User對象中只有id和name屬性有值,其它屬性均爲null。 User對象必須提供一個構造函數,形式爲: public User(int id, String name) {„}
否則將不能創建User對象,因爲創建對象時調用的是帶參數的構造方法。
(4)採用別名的方式查詢屬性值:
採用別名方式查詢屬性值,在Hibernate中可以使用as關鍵字指定別名,也可以不使用as關鍵字指定別名,兩者都可以指定別名,如下所示: 不採用as關鍵指定別名 List users =
session.createQuery("select u.id, u.name from User u").list(); 採用as關鍵字指定別名 List users =
session.createQuery("select u.id, u.name from User as u").list();
說明:當指定別名後,在查詢的屬性名需要使用前綴時必須使用別名,而不能再使用原實體對象的名稱,即: u.id, u.name。
【小結】
單一屬性查詢,返回結果集屬性列表,元素類型和實體類中相應的屬性類型一致; 多個屬性查詢,返回的集合元素是對象數組,數組元素的類型和對應的屬性在實體類中的類型一致;
數組的長度取決與select中屬性的個數;
如果認爲返回數組不夠對象化,可以採用HQL動態實例化對象的方式: select new User(id, name) from User
String key = "'%張%'";
hql = hql + key;
List user = session.createQuery(hql).list();
(2)採用佔位符的形式查詢 如:
Query query = session.createQuery("select u.id, u.name from User u where u.name like ?");
query.setParameter(0, “%王%"); List users = query.list(); 或使用方法鏈編程:
List users = session.createQuery("select u.id, u.name from User u where u.name like ?")
.setParameter(0, “%王%") .list();
注:在Hibernate中第1個佔位符的下標位置從0開始
(3)採用參數命名的方式查詢 單一條件
List users = session.createQuery("select u.id, u.name from User u where u.name like :myname")
.setParameter("myname", "%張%") .list(); 多條件
List users = session.createQuery("select u.id, u.name from User u where u.name like :myname and u.id=:myid")
.setParameter("myname", "%張%") .setParameter("myid", 1) .list();
說明:定義命名參數固定格式::+參數名稱(即:myid ) 賦值時,直接寫參數名即可: setParameter("myid", 1) setParameter方法用於爲參數賦單一值
(4)採用in關鍵字(即包含)查詢
List users = session.createQuery("select u.id, u.name from User u where u.id in(:myids)")
.setParameterList("myids", new Object[]{1, 2, 3, 4, 5}) .list();
說明:由於in中的條件是集合的形式,所以賦值是需要採用setParameterList這種方法,賦的值則採用對象數組的形式。
(5)採用數據庫函數查詢——不建議使用
在Hibernate的HQL語言中可以直接使用數據庫中的函數,但是這就使Hibernate與特定的數據庫進行了高耦合,不便於日後的移植。
如:
List students = session.createQuery("select u.id, u.name from User u where CONVERT(varchar(10), DATEPART(mm,u.create_time))=?") .setParameter(0, "06") .list();
(6) 查詢某一時間段的數據
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //查詢2010-06-10到2010-07-31入職的員工
List users = session.createQuery("select u.id, u.name from User u where u.create_time between ? and ?")
.setParameter(0, sdf.parse("2010-06-10 00:00:00")) .setParameter(1, sdf.parse("2010-07-31 23:59:59")) .list();
說明:在實際項目開發中,關於日期段的查詢一般都是從起始日期的00:00:00時間開始,結束於結束日期的23:59:59,這樣對於日期類型的數據庫表的字段更爲精確。
【小結】
可以採用拼字符串的方式傳遞參數
可以採用 ?來傳遞參數(索引從0開始) 可以採用 :參數名 來傳遞參數
如果傳遞多個參數,可以採用setParamterList方法
在HQL語言中可以使用數據庫的函數,如:CONVERT、DATEPART
HQL語言-原生SQL語句查詢: 採用原生SQL語句查詢
List users = session.createSQLQuery("select * from t_user").list(); 說明:
使用原生SQL語句,需要使用:SQLQuery接口; 使用原生SQL語句時,不會往Session(一級)緩存中放入數據,即SQLQuery接口中的list()方法不會往Session(一級)緩存中放入數據;
SQLQuery接口的list()方法返回的List集合中存儲的是數組對象
HQL語言-外置命名查詢:
外置命名查詢就是指把HQL語句放到一個配置文件中,而不是把HQL語句直接寫到程序代碼中,採用這種方式的目的是爲了讓HQL語句與程序代碼更好的進行解耦。
在實際開發中這並不是決對的,因爲當把所有的HQL語句放入到一個配置文件中在調試時,會增加一定的工作量,有時顯得會不太方便,當項目追求低耦合度時,可以考慮採用這種方式。
實體映射文件:
在</class>的標籤後面加入如下代碼: <!-- 外置命名查詢定義 --> <query name="searchUsers">
<![CDATA[
SELECT u FROM User u where u.id<? ]]> </query>
說明: 配置文件中之所以使用<![CDATA[„ ]]>是因爲在HQL語句中含有”<”號,該符合是XML語言的組成部分,如果文本中含有XML語言特定的符號時可以將文本放在<![CDATA[„ ]]> 中,如果不使用<![CDATA[„ ]]>,則這些特定符號需要進行轉義,否則會引起一些問題。
使用:
List users = session.getNamedQuery("searchUsers")
.setParameter(0, 6) .list(); 說明:
searchUsers:爲配置文件中外置命名查詢的名稱
setParameter(0, 6):爲外置命名查詢的HQL語句的佔位符賦值 在映射文件中採用<query>標籤來定義HQL語句
在程序中採用session.getNamedQuery()方法得到HQL查詢串
HQL語言-查詢過濾器:
啓用查詢過濾器後,當Session沒有關閉的時候,只要在Session中執行的所有HQL語句都會自動加上查詢過濾器中的條件(即實現過濾功能)。
(1)定義查詢過濾器
在</class>的標籤後面加入如下代碼:
<filter-def name="filtertest">
<filter-param name="myid" type="integer"/>
</filter-def> 說明
filtertest:查詢過濾器名稱
myid:過濾條件中的參數名稱
type:指定參數的類型,本例爲小寫的integer表示這是Hibernate中的類型,也可以寫成Java中的java.lang.Integer
(2)爲實體映射文件指定使用查詢過濾器在<class>標籤中的最後加入如下代碼:
<!-- 指定使用查詢過濾器,i_id數據庫字段名 -->
<filter name="filtertest" condition="i_id < :myid"/>
說明:
name:指定要使用的查詢過濾器名稱condition:指定查詢過濾器的條件,將來過濾器將使用該條件進行過濾操作。其中“< ”表示的是“<”號,因爲該符號是XML語言的特殊符號,所以在這裏需要進行轉義。
(3)在程序中啓動查詢過濾器
//啓用查詢過濾器,只有啓動以後查詢過濾器才起作用
session.enableFilter("filtertest") .setParameter("myid", 6);
【小結】
在映射文件中定義過濾器參數在類的映射中使用這些參數在程序中啓用過濾器查詢過濾器中的條件在Hibernate生成具體的SQL語句時會自動加上該條件,所以條件中必須使用表中的字段名HQL語言-分頁查詢:
Hibernate分頁查詢
List users = session.createQuery("from User")
//指定從第幾條記錄開始,下標從0開始,本例爲從第2條記錄開始查詢
.setFirstResult(1) //每頁顯示多少條記錄
.setMaxResults(2) .list();
HQL語言-對象導航查詢:
所謂的對象導航查詢是指查詢某一個實體時,條件是這個實體所關聯的另外一個實體對象,例如:查詢員工的姓名,條件是根據部門名稱進行查詢。
對象導航查詢:
List users = session.createQuery("select u.name from User u where U.dept.name like '%人力%'").list(); HQL語言的含義是:查找部門名稱中含有“人力”字樣的部門中的員工姓名字段。
說明:
查詢的條件是根據User類中的關聯的實體對象dept的名稱來進行查詢的,其中dept是User類中對Dept的引用,那麼這種導航的工作原理是根據配置文件中的關聯映射完成的。
在HQL語句中採用
進行導航HQL語言-連接查詢:
Hibernate中支持3種連接查詢:
內連接
左外連接
右外連接
說明:HQL中的連接必須採用對象導航進行連接,否則將拋出org.hibernate.hql.ast.QuerySyntaxException異常。
(1)內連接
示例:查詢員工的姓名及所在部門的名稱
內連接有兩種方式:
不使用inner關鍵字
List users = session.createQuery("select d.name, u.name from User u join u.dept d").list();
使用inner關鍵字
List users = session.createQuery("select d.name, u.name from User u inner join u.dept d").list();
說明: 在Hibernate中上述 的HQL語句就能夠將User實體對象和Dept實體對象的關聯關係建立起來了,因爲這都是通過實體映射文件來完成的,體現在表上則是通過兩張表的連接完成的。 (2)左外連接
示例:查詢部門的名稱(包含沒有員工的部門)及員工的姓名
List users = session.createQuery("select d.name, u.name from Dept d left join d.users u").list();
說明: Hibernate會根據Dept實體類的映射文件中的關聯映射關係自動發出左外連接的SQL語句(即t_dept表與t_user表之間採用左外連接的SQL語句)。 (3)右外連接
示例:查詢員工姓名(包含未分配部門的員工)及員工所在部門的名稱。
List users = session.createQuery("select d.name, u.name from Dept d right join d.users u").list(); 說明:Hibernate會根據Dept實體類的映射文件中的關聯映射關係自動發出右外連接的SQL語句(即t_dept表與t_user表之間採用右外連接的SQL語句)。
HQL語言-統計查詢:
Hibernate 3.x支持統計查詢,但是使用Hibernate做純粹的統計應用比較少,因爲涉及到的數據量非常大,在實際項目開發中,一般採用第三方的報表工具,比較常用的有:iReport、如意報表、JfreeChart等。
示例:查詢員工人數
統計總數,一般有編程兩種方式:
(1)採用獲取返回的List集合長度的方式
List users = session.createQuery("select count(id) from User").list(); //Hibernate的統計返回值一般都是long型,防止統計數據過大造成越界異常 Long count = (Long)users.get(0);
說明:這種方式效率不高,因爲返回的是一個List,而該List中就一個元素(即統計的數量值),然後還需要使用get方法將該元素取出。 (2)採用從返回的Long對象中獲取統計數值 Long count =
(Long)session.createQuery("select count(id) from User").uniqueResult(); 說明:這種方式效率高,因爲返回的不是一個List集合,而是一個長整型的對象,該對象即是統計出來的數據,因爲Hibernate使用長整型來封裝統計出來的數值,目的是防止統計出來的數值過大,造成數據溢出異常。數據類型一定要切記,是Long(即封裝類)。
HQL語言-DML查詢: Hibernate 3.x支持DML風格的操作,所謂DML風格就是指批量的執行insert、update、delete等操作這裏的這些操作指的都是批量執行Hibernate中對應的save、update、delete方法。
但是這些批量操作一般不建議使用,因爲當批量執行一批語句後,Hibernate是不會修
改緩存的,這就造成了數據庫中的數據與緩存中的數據不同步的問題,假設一次性批量插入1萬條數據,這時Hibernate不可能將這1萬條數據都放入緩存,因爲這非常容易導致內存溢出異常。
在實際項目開發中對於那些變化不大數據( 即不是經常訪問的數據)可以採用DML來操作。 示例:
session.createQuery("update User u set u.name=? where u.id < ?")
.setParameter(0, "XXX") .setParameter(1, 5) .executeUpdate();
說明:執行完批量的update操作後數據庫中的值已經發生了變化,但是緩存中的值並沒有發生變化還是原來的值,但是當我們訪問時,其實訪問的是緩存中的值,這就造成了數據的不準確性。
注:
session.get()和session.load()區別: get()方法:
1. 不支持延遲加載,會馬上發出查詢sql語句,加載User對象;
2. 使用get()方法加載數據,如果數據庫中不存在相應的數據,則返回null。 load()方法:
1. 支持延遲加載,只有真正使用這個對象的時候,纔會發出查詢sql語句
2. 使用load加載數據,如果數據庫中沒有相應的數據,那麼拋出ObjectNotFoundException,即不會返回null。
get(),load()方法會把查詢上來的數據放入緩存,也會使用緩存
get(),load()方法會將查詢上來的數據放入一級緩存中一份,然後再將該數據放入二級緩存中一份;先查找一級緩存再查找二級緩存,如果在任何一級緩存中能夠查到該數據,則直接進行使用,否則發出SQL加載數據
結論:session是共享二級緩存(SessionFactory緩存)的
Query query = session.createQuery("") query.iterate()
iterate() 查詢實體對象: iterate() 使用緩存(但不包括查詢緩存,oid是存儲在查詢緩存中的),發出查詢id的SQL語句,不會發出查詢實體對象的SQL語句。
原理同load()方法,緩存中如果存在該實體對象,則直接使用,如果不存在該實體對象,則發出SQL語句去查詢。
iterate() 方法查詢普通屬性:
一級緩存、二級緩存只存儲實體對象
發出查詢姓名屬性的SQL語句;不會發出查詢ID屬性列表的SQL語句(因爲此時的HQL語句查詢的是普通屬性而不是實體對象)
* 一(二)級緩存是緩存實體對象的,即如果查詢出來的是實體對象,則才向一(二)級緩存中存放,如果查詢出來的是普通屬性,則不向一(二)級緩存中存放。
重點!!!
* 思考:執行如下語句,是否將查詢出來的數據存放到一級緩存中?
Iterator iter = session.createQuery("select new User(u.name) from User u where u.id=1").iterate();
答案:不會向一級緩存中存放,因爲此時查詢出來的User對象是我們自己new出來的,而不是Hibernate自動爲我們new出來的,所以不受Session管理。
Query.list()方法:
1、當關閉查詢緩存(默認)時,list()方法不使用一級緩存和二級緩存中的數據
2、當開啓查詢緩存時,list()方法就會使用一級緩存和二級緩存中的數據,順序是先找一級緩存再找二級緩存
Session 、SessionFactory:
clear() 方法清除緩存中的全部實體對象
evict() 方法可以指定要從緩存中清除哪個實體對象