Hibernate快速入門

Hibernate框架
Hibernate框架
Hibernate快速入門
Hibernate基礎語義
Hibernate基礎配置
Hibernate O/R映射
Hibernate數據關聯
Hibernate數據檢索
HQL實用技術
Hibernate高級特性
Hibernate快速入門
OR映射問題一直是程序開發中最爲複雜的問題,一直沒有得到很好的解決
目前已知的OR解決方案有:
1、實體EJB,主要是指CMP方式的實體EJB
2、JDO,Java Data Object試圖解決EJB存在的問題
3、TopLink,WebGain公司的產品,現在被Oracle收購
4、Hibernate
Hibernate快速入門
Hibernate與Struts一樣,也是開源項目,提供了一種實現OR映射的框架,管理該項目的組織是JBoss
Hiberate誕生於2001年11月,是Gavin King 業餘時間的個人作品,後被JBoss採納,成爲其主力推廣產品
Gavin King目前已經成爲
 EJB3.0專家委員,JBoss
 的核心成員,生於74年,
    澳大利亞墨爾本人。
Hibernate快速入門
Gavin King最早就職於澳大利亞悉尼一家IT公司Cirrus Technologies ,在決定開發Hibernate時,Gavin King對SQL一無所知。
2003年9月,Gavin King和hibernate的一些開發者加入了JBoss,專門從事Hibernate的開發
在Sun公司制訂EJB3.0時,他成爲了實際上的領導人
Gavin King曾經在網站上聲明如果有人能使用Java代碼寫出比Hibernate效率高的片段來,他願意爲每一段代碼付100美元
Hibernate快速入門
Hibernate的核心思想是將數據庫中的數據映射到JavaBean對象上,並可直接將對象屬性上的數據寫到數據庫相應字段上。
Hibernate中稱用於存儲數據的JavaBean對象爲POJOs(Plain Old Java Objects,也稱爲Plain Ordinary Java Objects)
要解決的重要問題是JavaBean對象的每一個屬性分別與哪一個表的哪一個字段相對應
Hibernate快速入門
如定義如下對象:
public class User {
   private String id;
   private String userId;
   private String password;
   public String getId() {
  return id;
   }
   public void setId(String id) {
  this.id = id;
   }
   …………….
}
Hibernate快速入門
Hibernate核心接口是Session,數據庫操作通過這個接口實現。
例如:
Configuration cfg = new Configuration();
SessionFactory factory = cfg.configure().buildSessionFactory();
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
User user = new User();
user.setUserId("teacher");
user.setPassword("teacher");
session.save(user);
tx.commit();
session.close();
其中,session是Hibernate中Session,以上代碼等同insert語句。
Hibernate快速入門
要實現以上效果,必須要解決兩個問題,一是要屏蔽不同數據庫之間的差異,二是要對象與表的映射。
Hibernate是通過配置文件解決上面的問題,配置文件有兩種類型,一種配置了數據庫方面的信息稱爲Hibernate配置,另一種配置了對象與表的映射關係稱爲Hibernate映射
這兩種配置均可以XML文件的形式配置
Hibernate快速入門
Hibernate配置:
<?xml version='1.0' encoding='UTF-8'?>
<!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="dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="show_sql">true</property>
        <property name="connection.datasource">java:comp/env/jdbc/DataSource</property>
        <mapping resource="user.xml"/>
    </session-factory>
</hibernate-configuration>

Hibernate快速入門
dialect:配置SQL方言,只要作用是體現數據庫間SQL語言的差異
show_sql:運行期是否在控制檯打印SQL語句
connection.datasource:Hibernate使用數據源的JNDI名
mapping:指定POJO的配置文件

Hibernate快速入門
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=“cn.com.lhhc.hibernate.first.dao.User" table="user">
        <id name="id" column="id">
            <generator class="identity"/>
        </id>
        <property name="userId" column="userid"/>
        <property name="password" column="password"/>
    </class>
</hibernate-mapping>

Hibernate快速入門
class指定POJO與哪個表對應
id指定類中哪個屬性爲主鍵,與哪個字段相對應
property定義了類屬性與字段間的對應關係

Hibernate快速入門
一個hibernate.cfg.xml文件與一個SessionFactory相對應,SessionFactory不是輕量級的,Hibernate建議在一個應用程序中只創建一個SessionFactory實例
Session對象是非線程安全的,因此在開發時必須保證一個線程只創建一個session
所以應該解決兩個問題,一是SessionFactory在整個應用中只有一個,二是Session在每個線程中只有一個。Hibernate給出了一種解決方案的建議:
Hibernate快速入門
public class HibernateUtil {
    private static final SessionFactory sessionFactory;
    static {
        try {
            sessionFactory = new Configuration().configure().buildSessionFactory();
        } catch (HibernateException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }
    public static final ThreadLocal session = new ThreadLocal();
    public static Session currentSession() throws HibernateException {
        Session s = (Session) session.get();
        if (s == null) {
            s = sessionFactory.openSession();
            session.set(s);
        }
        return s;
    }
    public static void closeSession() throws HibernateException {
        Session s = (Session) session.get();
        session.set(null);
        if (s != null)  s.close();
    }
}
Hibernate快速入門
SessionFactory使用的是單例模式,保證在全局只存在一個實例
Session採用了線程局部變量(ThreadLocal)的辦法,保證在一個線程中僅存在一個Session實例
以上僅對於Hibernate2,在Hibernate3中SessionFactory提供了getCurrentSession方法,保證了線程中Session的惟一性
Hibernate快速入門
getCurrentSession在第一次調用時創建新的實例,以後調用則返回已經實例化的Session。
與此同時,Hibernate還保證了在線程結束時,或事務提交時關閉Session
使用getCurrentSession需要在配置中加入
<property name="current_session_context_class">
  thread
</property>
Hibernate快速入門
代碼也應修改爲:
Session session = factory.getCurrentSession();
session.beginTransaction();
User user = new User();
user.setUserId("teacher");
user.setPassword("teacher");
session.save(user);
session.getTransaction().commit();
總結
Hibernate採用POJO表示數據對象:
對象屬性與表中字段值相對應;
通常一個JavaBean類與一個表對應;
一個JavaBean對象與表中一行相對應。
JavaBean與表的映射關係是由XML文檔定義的。
總結
Hibernate中的數據庫操作由Session對象來完成,Session非線程安全,必須保證一個線程只有一個Session對象
Hibernate2中建議使用ThreadLocal對象來保存Session對象
Hibernate3則提供了getCurrentSession方法,但要對SessionFactory做相應配置
總結
Session對象由SessionFactory創建,SessionFactory由hibernate.cfg.xml文件來配置
一個SessionFactory與一個hibernate.cfg.xml相對應,具體地指定了使用哪種數據庫等相關信息。
SessionFactory是非輕量級的,一個應用中最好只有一個SessionFactory對象,使用單例模式
Hibernate基礎語義
Configuration
SessionFactory
Session
Configuration
Configuration類負責管理Hibernate的配置信息
Hibernate運行時需要獲取一些底層實現的基本信息 ,這些屬性可以在hibernate.cfg.xml中,或hibernate.properties中設定
Configuration
當調用 Configuration config=new Configuration().configure();Hibernate會自動在當前Classpath中搜尋hibernate.cfg.xml文件並加載到內存中
Configuration
Configuration類一般只有在獲取SessionFactory時涉及,當SessionFactory實例創建後,由於配置信息已經由Hibernate綁定在返回的SessionFactory之中,因此一般情況下不再用Configuration
如果不使用默認的hibernate.cfg.xml,可以指定:
Configuration config=new Configuration().configure(new File(“c:\\sample\\myhibernate.xml”));
SessionFactory
SessionFactory負責得到Session對象。
  SessionFactory
sessionFactory=config.buildSessionFactory();

SessionFactory中保存了對應當前數據庫配置的所有映射關係,同時也負責維護當前的二級數據緩存和Statement Pool。
Session
Session是Hibernate持久化操作的基礎。
這裏的Session與web層的HttpSession沒有關係,Hibernate Session與Hibernate,相當於JDBC Connection相對於JDBC
Insert,delete,update可以通過session相對應save,delete,update方法完成

Session
查詢比較複雜,通過主鍵查詢可以通過session的get或load
帶條件的查詢,稱爲find,可以通過Query,Criteria。
Session中的createQuery,createCriteria方法得到Query,Criteria
Query和Criteria不同之處在於,Query面向HQL和Native SQL,而Criteria提供了面向對象的查詢模式。

Hibernate基礎配置
SessionFactory配置
事務管理
SessionFactory配置
數據庫連接配置
 JDBC連接配置:dialect,driver_class,url,username,
   password
 JNDI連接配置:datasource,user,pwd,dialect
數據庫連接池配置
 四種連接池:默認數據庫連接池,C3P0,dbcp,Proxool,推薦dbcp
事務管理
數據庫連接配置
 JDBC連接配置:dialect,driver_class,url,username,password
 JNDI連接配置:datasource,user,pwd,dialect
數據庫連接池配置
 四種連接池:默認數據庫連接池,C3P0,dbcp,Proxool,推薦dbcp
Hibernate O/R映射
Hibernate基本數據類型
實體映射
高級映射技術
複合主鍵
實體映射策略
O/R映射簡介
Hibernate的核心思想是將數據庫中的字段映射到JavaBean的屬性上,因此明確了JavaBean屬性與數據庫映射關係的映射文件就顯得十分重要
Hiberante中數據的增、刪、改是圍繞着Java對象展開的,而並非直接在數據庫上進行,因此只有Java對象上的屬性聲明爲持久化的纔會對數據庫造成影響
O/R映射簡介
<?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="dao.User" table="user">
        <id name="id" column="id">
            <generator class="identity"/>
        </id>
        <property name="userId" column="userid"/>
        <property name="password" column="password"/>
    </class>
</hibernate-mapping>
O/R映射簡介
POJO類與表的映射是通過class完成的
標識屬性與主鍵的映射是通過id及composite-id實現的,後者用於聯合主鍵,同時還需要指定主鍵的生成器
屬性與字段的映射是通過property元素完成的

Hibernate基本數據類型
在進行Hibernate實體屬性關係定義時,需要提供屬性的數據類型設定
通過這些類型定義,Hibernate即可完成Java數據類型到數據庫特定數據類型的映射關係。
Hibernate基本數據類型
簡單屬性配置片斷:
<property name=”age” type=”integer” column=”age”/>
  將integer類型的屬性age映射到庫表字段age
Hibernate提供 豐富的數據類型支持,其中包括了傳統的Java數據類型,如String,integer,以及JDBC數據類型,如Clob,Blob等。除此之外,Hibernate還提供用戶自定義數據類型支持。
Hibernate基本數據類型
Java原始類型:
Hibernate基本數據類型
大數據類型:各數據庫之間由於實現機理差異,對於大數據存取方式難以通用
Hibernate基本數據類型
其他類型:
實體映射
實體映射技術作爲類和表之間的聯繫紐帶,在ORM中起着至關重要的作用。
對於Hibernate而言,映射關係更多體現在配置文件的維護過程中。
Hibernate選用XML作爲映射配置文件的基礎形式
實體映射
分三部分講解
實體映射基礎
高級映射技術
實體映射策略
實體映射
分三部分講解
實體映射基礎
介紹Hibernate中類/表映射、屬性/字段映射的基本技術
高級映射技術
介紹自定義數據類型、複合主鍵、特殊字段(Blob/Clob)的相關映射技術。
實體映射策略
圍繞實體映射中實體粒度、層次的設計思路進行探討。對特殊情況下與實體邏輯結構,實體讀寫性能相關的一些通用設計策略進行介紹
實體映射基礎
實體映射的核心內容,即實體類與數據庫表之間的映射定義,
類表之間的映射主要包括3部分:
表名-類名映射,主鍵映射,字段映射
實體映射基礎
類表映射:<class name=”com.TUser” table=”T_User”>
主鍵映射:<id name=”id” column=”id” type=”java.lang.Integer”><generator class=”native”></id>
Native表示主鍵生成方式由Hibernate處理,調用數據庫本身的主鍵生成方式
屬性/字段映射配置:<property name=”name” column=”name” type=”java.lang.String”/>
高級映射技術
Hibernate基本數據類型可以滿足大部分應用需求。
對於特殊情況,出於設計上的統一性考慮,我們需要針對數據結構中可能重複出現的一些數據模式,引入自定義數據類型。
很少使用
高級映射技術
Hibernate中,通過composite-id節點對複合主鍵進行定義
對於複合主鍵而言,可以通過兩種方式確定主鍵
基於實體類屬性的複合主鍵
基於主鍵類的複合主鍵
Hibernate中有createBlob和createClob方法,創建Blob,Clob對象。其中Blob對象基於FileInputStream構建,Clob基於String構建
實體映射策略
對於Hibernate而言,所謂細粒度對象模型,主要是針對實體類設計的對象細分,對象的細分主要出於兩個目的:
面向設計的粒度細分(通過對象細化,實現更加清晰的系統邏輯),
面向性能的粒度細分(針對業務邏輯,通過合理的細粒度對象,提高系統的能耗比(性能、資源消耗))
實體映射策略
面向設計的粒度細分
一張表可以對應多個實體類。
對於單表的對象細分,在Hibernate中可藉助Component節點的定義完成。
Component與實體對象的根本差別,就在於Component沒有標識(identity),它作爲一個邏輯組成,完全從屬於實體對象。

<class name=”” table=”T_USER”>
<id …></id>
<component name=”name” class=””>
<property …/>
<property …/>
</component>

</class>
實體映射策略
面向性能的粒度細分:
對於一些Clob,Blob字段,數據庫讀取代價較高,希望真正調用相應get方法時才真正從數據庫中讀取數據。Hibernate2中用繼承完成。將高代價數據寫在子類中。
在子類的xml中寫:
 <class name=”” table=”” polymorphism=”explicit”>
實體層次設計
實體層次設計:繼承關係是關係數據與面向對象數據結構之間的主要差異之一。如何在關係型數據庫的基礎之上,通過繼承關係得到清晰合理的層次劃分,是Hibernate實體層次設計中的一個關鍵問題。
Hibernate中支持3種類型的繼承關係:
Table per concrete class(TPC)
表與類之間的獨立一對一關係
Table per subclass(TPS)
每個子類對應一張子表,並與主類共享主表
Table per hierarchy(TPH)
表與類的一對多關係
繼承-TPC
TPC情況下,每具體類對應一張表,不管是子類還是父類,只要不是抽象類均有一張表與之對應。
表與表之間不存在主外鍵對應關係,數據存在冗餘
TPC情況下,主鍵不能使用identity生成器,並且必須保證子表中的字段名與父表中的字段名完全相同
繼承-TPC
<class name="Course" abstract="true" >
    <id name="id" column="course_id" type="string" length="128">
       <generator class="uuid" />
    </id>
    <property name="name" column="course_name" />
    <property name="period" column="total_period" />
    <property name="notes" />
    <union-subclass name="PractiseCourse" table="practise_course">
       <property name="projectName" column="project_name" />
       <property name="projectPeriod" column="project_period" />
    </union-subclass>
    <union-subclass name="CertificationCourse" table="certification_course">
       <property name="certificationName" column="cert_name" />
       <property name="examTime" column="exam_time"/>
    </union-subclass>
</class>
繼承-TPC
在操作上,TPC與其它兩種方式相同,也能夠實現多態查詢,多態插入操作
List gc = session.createQuery("from Course").list();
List pc = session.createQuery("from PractiseCourse").list();
List cc = session.createQuery("from CertificationCourse").list();
Iterator it = pc.iterator();
while(it.hasNext()) {
 Course c = (Course)it.next();
 System.out.println(c.getName());
}
加載時使用union關鍵字
繼承-TPS
在TPH下,由於不同的子類包含有不同的字段值,因此在表中必然有空的字段,存儲空間浪費比較嚴重
從數據庫表設計出發,應該將那些非共性的字段單獨建立表,共有數據存在另外一張表中。
這就要求每個類都對應一個單獨的表,子類表維護一個到父類表的外鍵。
繼承-TPS
繼承-TPS
 <class name="Course" table="course" >
    <id name="id" column="course_id">
       <generator class="identity" />
    </id>
    <property name="name" column="course_name" />
    ……
    <joined-subclass name="PractiseCourse" table="practise_course">
       <key column="pcourse_id" />
       <property name="projectName" column="project_name" />
       <property name="projectPeriod" column="project_period" />
    </joined-subclass>
    <joined-subclass name="CertificationCourse" table="certification_course">
       <key column="ccourse_id" />
       <property name="certificationName" column="cert_name" />
       <property name="examTime" column="exam_time"/>
    </joined-subclass>
  </class>
繼承-TPS
PractiseCourse pc = new PractiseCourse();
pc.setName("web developer");
pc.setPeriod(300);
pc.setProjectName("Training System");
pc.setProjectPeriod(100);
session.save(pc);
其使用方法與TPH相差無幾,加載時使用外連接
繼承-TPH
TPH下,一般只爲父類創建一個表,在表中設置字段區分對象種類。
不同子類具有的不同屬性保存在同一張表中,不具備的屬性保存值爲空。
以課程爲例,包括普通課程、實習課程和認證考試課程,它們的關係如圖:
繼承-TPH
繼承-TPH
<class name="Course" table="course" discriminator-value="GC" >
    <id name="id" column="course_id">
       <generator class="identity" />
    </id>
    <discriminator column="course_type" type="string" />
    <property name="name" column="course_name" />
    <property name="period" column="total_period" />
    <property name="notes" />
    <subclass name="PractiseCourse" discriminator-value="PC">
       <property name="projectName" column="project_name" />
       <property name="projectPeriod" column="project_period" />
    </subclass>
    <subclass name="CertificationCourse" discriminator-value="CC">
       <property name="certificationName" column="cert_name" />
       <property name="examTime" column="exam_time"/>
    </subclass>
</class>
繼承-TPH
PractiseCourse pc = new PractiseCourse();
Course gc = new Course();
CertificationCourse cc = new CertificationCourse();
Course gcc = new CertificationCourse();
session.save(pc);
session.save(gc);
session.save(cc);
session.save(gcc);
繼承-TPH
List gc = session.createQuery("from Course").list();
List pc = session.createQuery("from PractiseCourse").list();
List cc = session.createQuery("from CertificationCourse").list();
Iterator itg = gc.iterator();
Iterator itp = pc.iterator();
Iterator itc = cc.iterator();
while(itg.hasNext()) {
 Course c = (Course)itg.next();
 System.out.println(c.getName());
}
關聯關係
在數據庫設計中常用的一種建模方法就是實體關係模型
實體就是現實世界中獨立存在的事物,例如學生、老師;電腦、用戶等等,其在數據庫的反映就是一張張關係表
實體包含了反映事物特徵的屬性,其在數據庫中的反映就是表中的字段
關係是實體間的聯繫,如學生和老師之間是多對多的關係,電腦與用戶是一對一的關係
實體間的關係並不是固定不變的,這完全取決於需求,比如,電腦與用戶間的對應關係也可以是一對多
關聯關係
關係主要有以下幾種:
1、一對一關係
2、一對多關係
3、多對一關係
4、多對多關係
除多對多關係需要關係表來反映外,其餘都是直接通過外鍵來體現的
關聯關係的方向
關係是有方向的,單向的關係是指從實體A能得到與其相關聯另外一個實體B,但從實體B不能得到實體A
例如,如果老師和課程之間是一對多的關係,並且通過一個老師能夠知道他所講的全部課程,但不能通過課程知道任課老師,這種關係就是單向的
雙向的關係是從關係中的任意一個角色都能得到另一實體
Hibernate中定義關係的方法
Hibernate中定義關聯關係的要點:
1、關聯一方持有關聯對象,通常是以屬性的方式保存在對象中。
2、在映射文件中,使用one-to-one、one-to-many、many-to-one、many-to-many定義映射關係。
3、對於one-to-many、many-to-many需要使用集合持有。
一對一關係
一對一關係有三種實現方式:
一是通過外鍵方式實現,即當前對象持有關聯對象的外鍵;
二是通過主鍵方式實現,即當前對象的主鍵即是主鍵也是外鍵,也就是說當前對象的主鍵和與之相關聯對象的主鍵是相同的。
三是通過關係表實現,即關聯雙方都以外鍵的形式存儲在關係表中,通過關係表反映一對一的關係,這種方式很少使用
一對一關係-外鍵方式
<class name="Person">
 <id name="id" column="personId">
  <generator class="native"/>
 </id>
 <many-to-one name="address"
  column="addressId"
  unique="true"
  not-null="true"/>
</class>
<class name="Address">
 <id name="id" column="addressId">
  <generator class="native"/>
 </id>
</class>
一對一關係-外鍵方式
在關聯的另一端如果也要持有對象,應該寫成:
<class name="Address">
 <id name="id" column="addressId">
  <generator class="native"/>
 </id>
    <one-to-one name="owner" class="Person" property-ref="addressId"/>
</class>
property-ref定義參照對象的外鍵屬性是什麼
一對一關係-主鍵方式
<class name="Person">
 <id name="id" column="personId">
  <generator class="native"/>
 </id>
</class>
<class name="Address">
 <id name="id" column="personId">
  <generator class="foreign">
   <param name="property">person</param>
  </generator>
 </id>
 <one-to-one name="person" constrained="true"/>
</class>
一對一關係-主鍵方式
如果定義雙向一對一,則
<class name="Person">
 <id name="id" column="personId">
  <generator class="native"/>
 </id>
      <one-to-one name="address"/>
</class>
<class name="Address">
 <id name="id" column="personId">
  <generator class="foreign">
   <param name="property">person</param>
  </generator>
 </id>
 <one-to-one name="person" constrained="true"/>
</class>
多對一關係
多對一關係中,當前對象持有外鍵。通過持有外鍵找到與之對應的對象。因此,在多對一關係中,當前對象包含一個與之關聯的對象,這個對象以屬性的方式存在對象中。
例如,單元(Unit)和樓房(Buiding)之間就是多對一關係。一幢樓可以包括多個單元,單元只對應一幢樓

多對一關係
多對一關係

<class name="Person">
 <id name="id" column="personId">
  <generator class="native"/>
 </id>
 <many-to-one name="address"
  column="addressId"
  not-null="true"/>
</class>
<class name="Address">
 <id name="id" column="addressId">
  <generator class="native"/>
 </id>
</class>

一對多關係
一對多關聯在系統實現中非常常見,一對多關聯分爲
單向一對多
雙向一對多。
單向一對多關係只需要在“一”方進行配置
雙向一對多需要在雙方配置。
一對多關係
一對多關係有兩種方式實現:
一是通過外鍵方式實現,“一”的角色不持有外鍵,外鍵被多的一方持有。Hibernate不鼓勵使用這種方式實現一對多關係。
二是通過關係表實現,外鍵被關係表持有,每一條對應關係都必須在關係表中註冊。
一對多關係-外鍵方式
<class name="Person">
 <id name="id" column="personId">
  <generator class="native"/>
 </id>
 <set name="addresses">
  <key column="personId"
   not-null="true"/>
  <one-to-many class="Address"/>
 </set>
</class>
<class name="Address">
 <id name="id" column="addressId">
  <generator class="native"/>
 </id>
</class>
一對多雙向-外鍵方式
<class name="Person">
 <id name="id" column="personId">
  <generator class="native"/>
 </id>
 <set name="addresses">
  <key column="personId"
   not-null="true"/>
  <one-to-many class="Address"/>
 </set>
</class>
<class name="Address">
 <id name="id" column="addressId">
  <generator class="native"/>
 </id>
       <many-to-one name=“person” column=“personId”/>
</class>
一對多關係-關係表
<class name="Person">
 <id name="id" column="personId">
  <generator class="native"/>
 </id>
 <set name="addresses" table="PersonAddress">
  <key column="personId"/>
  <many-to-many column="addressId"
   unique="true"
   class="Address"/>
 </set>
</class>
<class name="Address">
 <id name="id" column="addressId">
  <generator class="native"/>
 </id>
</class>
一對多雙向-關係表
<class name="Person">
  <id name="id" column="personId">
    <generator class="native"/>
  </id>
  <set name="addresses"
         table="PersonAddress">
    <key column="personId"/>
    <many-to-many column="addressId"
     unique="true"
    class="Address"/>
  </set>
</class>
多對多關係
多對多隻能通過關係表的形式實現,每一條關係都必須在關係表中註冊。
關係雙方都不持有外鍵,但均持有一個集合屬性,代表與之關聯的關係另一角色。

一對多關聯
單向一對多:
對於one-to-many關聯關係,可以採用java.util.Set,或者net.sf.hibernate.collection.Bag,類型的Collection,表現在XML映射文件中就是<set></set>或者<bag></bag>
單向一對多的實現相對比較簡單,但是存在一個問題,由於是單向關聯,爲了保持關聯關係,我們只能通過主控方對被動方進行級聯更新。如果被關聯方的關聯字段爲not null,當Hibernate創建或更新關聯關係時,可能出現約束違例。
一對多關聯
雙向一對多關係的出現解決了這個問題。它除了避免約束違例和提高性能的好處之外,還有另外優點:由於建立了雙向關聯,可以在關聯任意一方,訪問關聯另一方,提供了更豐富的靈活的控制手段
一對多關聯
雙向一對多關聯:
雙向一對多關聯,實際上是一對多和多對一的組合。
多了Inverse=true的配置,控制方向反轉
Inverse=false的一方爲主控方
多對多關聯
多對多關聯比較特殊,與一對多,一對一關聯不同,需要藉助中間表完成多對多映射信息的保存。
由於採用中間表,所以性能欠佳,避免大量使用。根據情況,採取延遲加載機制避免開銷。
Hibernate數據檢索
Criteria Query
DetachedCriteria
HQL
Criteria Query
Criteria Query通過面向對象化的設計,將數據查詢條件封裝爲一個對象。
簡單來講,Criteria Query可以看作是傳統SQL的對象化表示
如:
Criteria criteria=session.createCriteria(Tuser.class);
Criteria.add(Expression.eq(“name”,”Erica”));
Criteria.add(Expression.eq(“sex”,new Integer(1)));
相當於:select * from t_user where name=’Erica’ and sex=1
DetachedCriteria Query
Hibernate2中,Criteria生命週期位於session生命週期中,也就是說,criteria對象很難重用,Hibernate3提供了一個新的實現:DetachedCriteria
DetachedCriteria可以脫離session實例獨立存在。
DetachedCriteria
deCriteria=DetachedCriteria.forClass(Tuser.class);
DeCriteria.add(Expression.eq(“name”,”Erica”));
….
Criteria criteria=deCriteria.getExecutableCriteria(session);
HQL
Criteria作爲一種對象化的查詢封裝模式,非常適合程序員。不過Hibernate在實現過程中,更加集中在HQL查詢語言上,因此Criteria的功能時間還有欠缺。實際開發中,最常用的還是Hibernate官方推薦的查詢封裝模式:HQL
HQL和Criteria並不是非此即彼,相互孤立的技術,兩者可以相輔相成,在系統開發中,可以適當搭配使用這兩種技術。
HQL
HQL提供了更加豐富靈活的特性,涵蓋了Criteria所有功能,提供了更強大的查詢能力。
HQL提供了更接近傳統SQL語句的查詢語法,也提供了更全面的特性。
HQL語法如下
[select/update/delete…][from…][where …][group by [having…]] [order by…]
HQL基於SQL,同時提供了更加面向對象的封裝。
HQL實用技術
實體查詢
屬性查詢
實體更新與刪除
分組與排序
參數綁定
引用查詢
聯合查詢
子查詢
數據加載方式
SQL查詢
HQL實用技術-實體查詢
HQL子句本身大小寫無關,但是其中出現的類名和屬性名必需主要大小寫區分
如:
String hql=”from TUser”
Query query=session.createQuery(hql)
List userList=query.list();
上面語句查出Tuser以及子類的所有記錄。
如果寫 from Object,則查出數據庫表中所有表的所有記錄。
可以加where條件,寫法與SQL基本一致
HQL實用技術-屬性查詢
有時候我們並不需要獲取完整的實體對象,如只想或許用戶的名字,可以如下:
 List list=session.createQuery(“select user,name from Tuser user”).list();
直接可以對list迭代,每個元素是String
也可以獲得多個屬性,如
  select user,name,user,age from Tuser as user
對返回的List可以迭代,每個元素是Object[]
HQL實用技術-屬性查詢
返回數組方式不復合面向對象風格,可以通過在HQL中動態構造對象的方法進行封裝。
 CreateQuery(“select new Tuser(user,name,user.age) from Tuser as user”).List()
此時返回的是List,迭代後,每個元素爲TUSer對象
通過在HQL中動態構造對象實例,我們實現了對查詢結果的對象化封裝。注意的是,結果中的Tuser除了查詢出來的屬性被賦值外,其他的屬性均沒有賦值。
在HQL的Select子句中可以使用統計函數,如count(*),也可以用數據中函數,如Upper(user.name).也可以用distinct
HQL實用技術-實體更新與刪除
Hibernate2中,HQL只支持查詢,更新如下:
Tuser user=(TUser)session.get(Tuser.class,new Integer(1))
User.setAge(new Integer(18))
Session.save(user)
如果批量修改,就很麻煩。
HQL實用技術-實體更新與刪除
Hibernate3中:
Query query=session.createQuery(“update Tuser set age=18 where id=1”)
Query.executeUpdate();
刪除:
Query query=session.createQuery(“delete Tuser where age>18”)
Query.executeUpdate();
需要注意的是:使用HQL delete/update子句時候,注意對緩存策略的影響。
HQL實用技術-分組與排序
與SQL類似,HQL可以用order by排序
也可以用Group by和having對組進行操作
分組後,每組的數據都封裝到數組裏。
HQL實用技術-參數綁定
可以在HQL語句中直接拼寫查詢參數。但是這種方式編碼凌亂,可讀性差,性能低,額外風險(SQL Injection)
參數可以通過綁定方式解決
session.find方法(Hibernate2)
session.find(“from Tuser where name=?”,”Erica”,Hibernate.STRING)
 多參數情況用數組實現。
HQL實用技術-參數綁定
可以通過Query接口進行參數填充:
Query query=session.createQuery(“from Tuser user where user.name=? and user.age>?”);
Query.setString(0,”Erica”);
Query.setInteger(1,20);
HQL實用技術-參數綁定
除了順序佔位符之外,Hibernate還支持引用佔位符
Query query=session.createQuery(“from Tuser where name : =name”)
Query.setParameter(“name”,”Erica”)

還可以用JavaBean封裝查詢參數
class UserQuery{
 private String name;
 private Integer age;
 getter/setter
}
String hql=“from Tuser where name:=name and age:=age”
query=session.createQuery(hql)
userQuery uq=new UserQuery();
uq.setName(..);
uq.setAge(…);
query.setProperties(up)
Iterator it=query.iterate()

HQL實用技術-引用查詢
Hibernate提供了HQL可配置化的內置支持。
在實體映射中,通過query節點定義查詢語句
通過session.getNamedQuery(…)方法獲得Query
HQL實用技術-聯合查詢、子查詢
Hibernate支持多表聯合查詢、子查詢
語法與SQL類似
HQL實用技術-數據加載方式
在傳統的JDBC操作中,我們通常通過SQL語句加載所需要的數據進行處理,當SQL提交之後,這些數據就被讀取待用。
而在Hibernate世界裏,我們有更多選擇(針對關聯數據)
HQL實用技術-數據加載方式
即時加載(Immediate Loading)
當實體加載完成後,立即加載其關聯數據。
延遲加載(Lazy Loading)
實體加載時,其關聯數據並非立即獲取,而是當關聯數據第一次被訪問時再進行讀取
預先加載(Eager Loading)
實體及關聯對象同時讀取,這與即時加載類似。不過實體及其關聯數據是通過一條SQL語句(基於外連接)同時讀取
批量加載(Batch Loading)
對於即時加載和延遲加載,可以採用批量加載方式進行性能上的優化。
HQL實用技術-數據加載方式
即時加載(Immediate Loading)
<set … lazy=”false”>
Session.find方法執行時,Hibernate連接了兩條SQL,分別完成了Tuser和Taddress對象的加載。這就是即時加載的基本原理,當宿主實體(關聯主題)加載時,Hibernate會立即自動讀取關聯的數據並完成關聯屬性的填充
HQL實用技術-數據加載方式
延遲加載:
即時加載中,加載Tuser對象同時,即同時加載了其所關聯的Taddress對象。這就就導致如下性能損耗:讀取Tuser對象數據,不用地址信息時,必需付出同時讀取地址數據的性能代價。
延遲加載就是爲了解決這個問題。
<set … lazy=true>
在真正需要關聯的Address時,才加載Address
HQL實用技術-數據加載方式
預先加載
在一對一的例子中,我們已經使用過。預先加載即通過outer-join完成關聯數據的加載,這樣,通過一條SQL語句即可以完成實體及其關聯數據的讀取操作,相對即時讀取的兩條甚至若干條SQL而言,無疑這種機制在性能上帶來了更多的提升。
不過,對於集合類型,(也就是一對多,多對一,或者多對多關係中),我們並不推薦採用預先加載的方式,理由與即時加載一樣,對於集合,只要允許,儘量用延遲加載,避免性能損耗。
HQL實用技術-數據加載方式
預先加載
在特殊情況下,特別對於複雜的關聯關係,如多層關聯,Hibernate生成的Outer-JoinSQL可能太複雜,此時,我們應該根據情況判斷預先加載在當時環境中的可用性。同時,也可以調整hibernate.max-fetch-depth限定outer-join層次,一般設定到5層
HQL實用技術-數據加載方式
批量加載:
批量加載,就是通過批量提交多個限定條件,一次完成多個數據的讀取。
對於以下形式的SQL:
select * from T_User where id=1
select * from T_User where id=2
可以合成:
select * from T_User where id=1 or id=2
HQL實用技術-數據加載方式
這就是所謂批量加載機制,如果使用了批量加載機制,Hibernate在進行數據查詢操作前,會自動在當前session中尋找是否還有其他同類型待加載的數據,如果有,將條件合併在當前select語句一併提交,這樣,通過一次數據庫操作完成多個讀取任務。
實體配置時,可以通過batch-size打開批量加載機制,並限定每次批量加載的數量
<class name=”TUser” table=”T_User” batch-size=”5”>
batch-size應該設定爲一個合理數值(<10)
實體對象生命週期
一個持久化類的實例可能處於三種不同狀態中的某一種:
瞬態(transient)
持久化(persistent)
託管(detached)
瞬態(transient)
該實例從未與任何持久化上下文關聯過。
它沒有持久化標識(相當於主鍵值)。
持久化(persistent)
實例目前與某個持久化上下文有關聯。
它擁有持久化標識(相當於主鍵值),並且可能在數據庫中有一個對應的行。
對於某一個特定的持久化上下文,Hibernate保證持久化標識與Java標識(其值代表對象在內存中的位置)等價。
託管(detached)
實例曾經與某個持久化上下文發生過關聯,不過那個上下文被關閉了,或者這個實例是被序列化(serialize)到另外的進程。
它擁有持久化標識,並且在數據庫中可能存在一個對應的行。
對於託管狀態的實例,Hibernate不保證任何持久化標識和Java標識的關係。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章