1. 程式人生 > >JavaEE學習筆記之SSH—Hibernate(2)

JavaEE學習筆記之SSH—Hibernate(2)

物件關係對映

ORM 解決的主要問題就是物件-關係的對映,域模型和關係模型都分別建立在概念模型的基礎上,域模型是面向物件的,關係模型是面向關係的,一般情況下,一個持久化類和一個表對應,類的每個例項對應表中的一條記錄。 (可能存在類中多個屬性對應一列的情況,對映組成關係)
ORM中介軟體採用元資料來描述物件-關係對映細節,元資料通常採用XML格式,並且存放在專門的物件-關係對映檔案中,如果希望把ORM軟體整合到自己的java應用中,使用者首先要配置物件-關係對映檔案(**.hbm.xml)。

session.save(customer)
執行過程:

1)運用反射機制,獲得customer物件的型別Customer.class

2)參考物件-關係對映元資料,瞭解Customer類對應的表,以及屬性對應的列,Customer類和其他類關係。

3)根據以上對映資訊,生成SQL語句
  insert into h_customer values(id,name,password,telphone);
4)呼叫JDBC API,執行以上的SQL語句。

JDBC與Hibernate比較

1,JDBC: java中嵌入SQL,不便於維護
Hibernate: 無須編寫SQL語句;由Hibernate通過讀取對映檔案在執行時自動生成SQL
save(student)-> insert into customer values(?,?,?);

2,JDBC:查詢資料,需要手動封裝成物件;儲存物件,需要手動插入值
Hibernate: 可自動實現物件和表中記錄的轉換

3,JDBC:查詢記憶體中已存在的物件,修改屬性值,均需要和資料庫進行互動
Hibernate:通過Dirty checking(髒檢查)避免互動;

Hibernate的工作原理

1,Hibernate框架根據hibernate.cfg.xml的配置的資訊來和資料庫進行通訊

2,Hibernate框架根據具體的對映檔案**.hbm.xml 來儲存,更新,刪除,查詢物件。

Hibernate API

Configuration介面:
Configuration物件用於配置並且根啟動Hibernate,Hibernate應用通過Configuration例項還指定物件-關係對映檔案的位置或者動態配置Hibernate的屬性,然後建立SessionFactory例項

Configuration config = new Configuration
(); Config.configure(); SessionFactory factory = config.buildSessionFactory(); SessionFactory

用來構造Session的例項物件,它的特點:

1)執行緒安全: 該例項物件可以被多個執行緒共享

2)重量級:該例項物件的構造和銷燬消耗系統資源,所以一般在應用程式啟動的時候就構造例項物件,一般一個數據庫對應一個SessionFactory的例項物件,如果要訪問多個數據庫,就需要建立多個該例項物件。

3)SessionFactory例項物件中維護了一個很大的快取,存放了一些預定義的SQL語句和XML配置檔案的資訊,另外還維持了一個Hibernate的第二級快取(快取了所有Session物件所載入的POJO物件),用來儲存在該生命週期中的一些業務物件,但是這個第二級快取預設是關閉的,必須在xml中配置才可以開放

Session session = factory.openSession();

Session(別名:持久化管理器)
用來對物件的儲存,更新,刪除和查詢
特點:

1)執行緒不安全,要避免同一個Session物件被多個執行緒共享,一般一個執行緒對應一個Session

2)輕量級:可以隨意的構造和銷燬該例項物件。

3)Session物件中維護了一個快取,稱為Hibernate的第一級快取(快取了當前Session物件所載入的POJO物件)每個Session物件都有自己的快取。

Query
用來查詢單個或者多個物件,利用HQL語句(Hibernate Query Language)面向物件的

Query query = session.createQuery("from Student where studentId=1");
Student s = (Student)query.uniqueResult();

Criteria
功能同上,以面向物件的形式和資料庫進行復雜的CRUD操作,還適用於動態查詢。

Transaction
用來處理事務的,事務的啟動,提交,回滾

OID
在java語言中,按照記憶體地址來識別或區分同一個類的不同物件,關係型資料庫按主鍵值來識別或區分同一個表的不同記錄,Hibernate使用OID來統一兩者之間的矛盾,OID是關係資料庫中的主鍵(通常為代理主鍵)在java物件模型中的等價物。在執行時,Hibernate根據OID來維持java物件和資料庫表中記錄的對應關係。

為了保證持久化物件的OID的唯一性和不可變性,通常由Hibernate或底層資料庫來為OID賦值,可以將OID的setId()方法設定為private型別,以禁止java應用程式隨便修改OID。

<generator>子元素用來設定識別符號生成器

Hibernate提供了識別符號生成器介面:

net.sf.hibernate.id.IdentifierGenerator,並且提供了很多內建實現。

net.sf.hibernate.id.IdentityGenerator     --縮寫名-- identity

net.sf.hibernate.id.IncrementGenerator   --縮寫名-- increment

主鍵生成方式

1,increment

其生成方式與底層資料庫無關,大部分資料庫都支援,該方式的實現機制是在當前應用例項中維持一個變數,以儲存著當前的最大值,之後每次需要生成主鍵的時候將此值加1作為轉。其不足之處是當多個執行緒併發對資料庫表進行寫操作時,可能出現相同的主鍵值,發生主鍵重複的衝突,所以在多執行緒併發操作的時候不應該使用該方法。

<generator class="increment" />

2,identity:

與底層資料庫有關,要求資料庫支援Identity,如MySQL中auto_increment,SQL Server中的identity。支援的資料庫有MySQL,SQL Server,DB2,Sybase,但是不支援oracle.支援併發。

<generator class="identity" />

3,assigned

主鍵由外部程式負責生成,無需Hibernate參與。—-如果要由程式程式碼來指定主鍵,就採有這種。

<generator class="assigned" />

4,sequence

使用序列生成主鍵,需要底層資料庫支援
在資料庫中建一個序列

create sequence seq_name increment by 1 start with 1;

在對映檔案中指定使用序列的名字

<generator class="sequence">
       <param name="sequence">seq_name</param>
</generator>

5,hilo

通過hi/lo演算法生成主鍵,需要一個表來儲存主鍵的資訊,
在資料庫中建一張表:

create table hi_value(next_hi number not null);
insert into hi_value(next_hi) values(1);
commit;

對映檔案中需要指明這些表資訊

<generator class="hilo">
       <param name="table">hi_value</param>
       <param name="column">next_hi</param>
       <param name="max_lo">100</param>
</generator>

6,seqhilo

與hilo 類似,通過hi/lo 演算法實現的主鍵生成機制,
只是主鍵歷史狀態儲存在Sequence中,適用於支援Sequence的資料庫,
在資料庫中建一個序列

create sequence seq_name increment by 1 start with 1; 
<generator class="seqhilo">
    <param name="sequence">seq_name</param>
    <param name="max_lo">100</param>
</generator>

7, native:

由Hibernate根據不同的資料庫方言,
自行判斷採用identity、hilo、sequence其中一種作為Hibernate主鍵生成方式,native的 優點是與底層性無關,便於不同資料庫之間的移植,由Hibernate根據不同資料庫選擇主鍵的生成方式。在oracle中需要建立叫 Hibernate_sequence名字的sequence,如果設定了Hibernate.hbm2ddl.auto屬性,不需要手動建立序列,前提 是資料庫帳號必須有Create Sequence這種高階許可權。mysql等資料庫則不用建立sequence。

 <generator class="native" />

8,foreign

外來鍵生成方式,依賴其他表的主鍵,在主鍵一對一對映中需要使用到

<generator class="foreign>
  <param name="property">husband</param>
</generator>

9,uuid.hex:

採用基於128位的演算法生成唯一值,並編製成32位長度的唯一字串作為主鍵值,uuid.hex的優點是支援大部分資料庫,缺點就是要佔用較大的儲存空間。對於併發Insert要求較高的系統,推薦採用uuid.hex 作為主鍵生成機制。

<generator class="uuid.hex" />

10,uuid.string:

使用UUID演算法,UUID被編碼為一個16個字元長的任意ASCII字元組成的字串。不能在PostgreSQL資料庫中使用。uuid.string同uuid.hex類似,需要佔很大的儲存空間。

11,select

使用觸發器生成主鍵(主要用於早期的資料庫主鍵生成機制,少用)

hibernate內建對映型別

Hibernate的型別對映型別從總體上可以分為兩種:內建型別對映和客戶型別對映。

內建型別對映負責把一些常見的java型別對映到sql型別

客戶型別對映可以將使用者定義的java類對映到資料庫表的相應欄位。

內建型別對映:

(1) Java基本型別的Hibernate對映:

這裡寫圖片描述

(2) Java時間和日期型別的Hibernate的型別對映:

這裡寫圖片描述

執行例項:

<property name=”name” column=”Name” type=”string”/>
 <property name=”birthday” column=”Brithday” type=”date”/>
 <property name=”registereddate” column=”Registereddate” type=”timestamp”/>

下圖為以上配置對應的資料庫表:

這裡寫圖片描述

(3) JDK自帶的個別Java類的Hibernate對映型別:

這裡寫圖片描述

注:

一個java型別對應多個Hibernate對映型別的場合。例如,如果持久化類的屬性為java.util.Date型別,對應的Hibernate對映型別可以是date,time或timestamp。此時必須根據對應的資料庫表的欄位的SQL型別,來確定Hibernate對映型別。如果欄位為Date型別,則hibernate對映為date,如果為TIME則為time,如果為TIMESTAMP則為timestamp。

客戶型別對映:

以下例子可以把User類的Integer型別的phone屬性對映到user表的varchar型別的phone欄位

User.java

public class User implements Serializable{
    private static final long serialVersionUID = -5925415258301826484L;
    private Long id;  
    private String name;  
    private Integer phone;  
    private Date birthday;  
    private Timestamp registerTime;
    public User() {
        super();
    }
    public User(Long id, String name, Integer phone, Date birthday,
            Timestamp registerTime) {
        super();
        this.id = id;
        this.name = name;
        this.phone = phone;
        this.birthday = birthday;
        this.registerTime = registerTime;
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getPhone() {
        return phone;
    }
    public void setPhone(Integer phone) {
        this.phone = phone;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    public Timestamp getRegisterTime() {
        return registerTime;
    }
    public void setRegisterTime(Timestamp registerTime) {
        this.registerTime = registerTime;
    }


}

注意:這個類要實現序列化介面

JAVA類的資料 ,如果要進行相關的儲存工作(如寫檔案,網路傳輸,寫資料庫等),那麼這個資料的類 就必須實現序列化介面 (java.io.Serializable). 

序列化 :  把物件轉換 為二進位制資料(如網路傳輸,儲存資料庫等),必須實現序列化介面 (java.io.Serializable).

持久化 :  把物件儲存 在介質上(如寫檔案,讀檔案不是), 沒有介面實現,一般指方法呼叫. 

資料的持久化是序列化的又一個典型的應用,物件只有在序列化之後才能進行持久化儲存,從持久化儲存介質載入的資料通過反序列化轉變成執行時物件。

user.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE hibernate-mapping PUBLIC   
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
<hibernate-mapping package="com.briup.test">  
    <class name="User" table="user" dynamic-insert="true" dynamic-update="true">  
        <id name="id" column="id" type="long">  
            <generator class="native" />  
        </id>  
        <property name="name" column="name" type="string" not-null="true" />  
                <!-- 自定義型別對映 -->  
                <property name="phone" column="phone" type="com.briup.test.PhoneUserType" not-null="true" />  
        <property name="birthday" column="birthday" type="date" />  
        <property name="registerTime" column="registerTime" type="timestamp" insert="false" update="false"/>  
    </class>  
</hibernate-mapping>  

將對映檔案新增到配置檔案中hibernate.cfg.xml

<mapping resource="com/briup/test/user.hbm.xml" /> 

主方法Test.java

/**
 * 測試自定義型別對映
 * @author Administrator
 *
 */
public class Test {

    public static void main(String[] args) {
        Configuration config = new Configuration().configure();
        Session session = config.buildSessionFactory().openSession();

    }

}

結果:

這裡寫圖片描述

說明:

PhoneUserType實現了org.hibernate.UserType介面的所有方法(11個)

sqlTypes() 設定資料庫中對應表的欄位的型別,這裡是varchar

returnedClass() 設定資料庫欄位型別對應的Java型別,這裡時Integer

equals(Object x, Object y) hibernate會呼叫此方法比較當前物件是否和它的快照相同,引數x代表當前值,y代表有deepCopy()方法生成的屬性快照值

hashCode(Object x) 獲得當前屬性的hash碼

deepCopy(Object value) hibernate呼叫此方法生成快照的值,由於本例中Interger為不可變類,故直接返回value引數

isMutable() 判斷自定義屬性類是否為可變類 ,此處Integer為不可變類,故返回false

disassemble(Object value) 當hibernate把物件儲存到第二快取中時,呼叫此方法獲得自定義屬性的序列化資料

assemble(Serializable cached, Object owner)    當hibernate從第二快取中載入物件到session 時,呼叫此方法獲得自定義屬性(此例為:phone)的反序列化資料

nullSafeGet(ResultSet resultSet, String[] names, Object owner) 當hibernate從資料庫中載入物件時,呼叫此方法取得自定義屬性的值

nullSafeSet(PreparedStatement statement, Object value, int index) 當hibernate把物件持久化到資料庫時,呼叫此方法把自定義屬性的值新增到SQL 語句中

replace(Object original, Object target, Object owner) 當session呼叫merge()方法把物件A融合到物件B時,呼叫此方法獲得替代B物件的自定義屬性的值