SSH框架之旅-hibernate(2)

hibernate

1.主鍵生成策略


1.1 主鍵的兩種類型

  • 自然主鍵:把數(shù)據(jù)表中的某一業(yè)務(wù)字段作為表的主鍵。如一張用戶表中,把用戶的用戶名作為用戶表的主鍵。這樣做的前提條件是,1.用戶的用戶名不能為空,2.用戶的用戶名不能重復(fù),用戶的用戶名不能修改。這樣盡管也是可以的,但不能很好的滿足業(yè)務(wù)需求的改變,所以不推薦使用自然主鍵的方式。
  • 代理主鍵:?jiǎn)为?dú)為數(shù)據(jù)表設(shè)置一個(gè)字段作為數(shù)據(jù)表的主鍵。作為主鍵的這個(gè)字段沒有業(yè)務(wù)含義,一般直接取名為id,通常為整數(shù)類型,因?yàn)檎鸵茸址凸?jié)省數(shù)據(jù)庫的空間,所以一般都是使用代理主鍵的方式設(shè)置數(shù)據(jù)表的主鍵。

注意:在開發(fā)中,建議使用代理主鍵。

1.2 hibernate 中主鍵的生成策略

  • assigned 自然主鍵類型
    在程序中設(shè)置主鍵。如果在映射表中不設(shè)置 generator 屬性,hibernate 默認(rèn)使用該主鍵生成策略。但不建議使用這種方式,盡量要減少手動(dòng)對(duì)主鍵的操作。
  • increment 代理主鍵類型
    用于整型類型,由 hibernate 自動(dòng)以遞增的方式生成,每次增量為一,但只有當(dāng)沒有其他進(jìn)程相同一張表中插入數(shù)據(jù)時(shí),才可以使用,不能在集群環(huán)境下使用。
  • identity 代理主鍵類型
    由底層數(shù)據(jù)庫設(shè)置主鍵,與 hibernate 無關(guān)。但前提是使用的數(shù)據(jù)庫要支持自動(dòng)增長數(shù)據(jù)類型,如 MySQL 是支持主鍵自動(dòng)生成的,但 Oracle 就不支持主鍵自動(dòng)生成。如果數(shù)據(jù)庫支持主鍵自增,是可以采用該主鍵生成策略的。
  • sequence 代理主鍵類型
    由底層數(shù)據(jù)庫根據(jù)序列生成主鍵,與 hibernate 無關(guān)。但前提是數(shù)據(jù)庫要支持序列,Oracle 是支持的。如果數(shù)據(jù)庫支持序列,是可以采用該主鍵生成策略的。
  • hilo 代理主鍵類型
    hibernate 生成主鍵,hilo 是 high low (高低位方式)的縮寫,是 hibernate 常用的一種生成方式,需要一張額外的表來保存 hi(高位)的值,并手動(dòng)設(shè)置 max_lo 的值,然后通過算法公式(hi * (max_lo + 1) + 0)來生成主鍵。這種生成策略可以跨數(shù)據(jù)庫,但由hilo算法生成的標(biāo)志只能保證在一個(gè)數(shù)據(jù)庫是唯一的。
  • natve 代理主鍵類型
    根據(jù)底層數(shù)據(jù)庫,自動(dòng)選擇identity、sequence、hilo 策略。但由于生成策略的控制權(quán)在 hibernate 手上,不建議采用,并且這種生成策略效率比較低。
  • uuid 代理主鍵類型
    由 hibernate 使用 128 為的UUID算法來生成標(biāo)識(shí)符(主鍵),該算法可以在網(wǎng)絡(luò)環(huán)境中生成唯一字符串的標(biāo)識(shí)符。長度是一個(gè) 32 位的十六進(jìn)制字符串,占用控空間比較大,對(duì)應(yīng)數(shù)據(jù)庫的char/varchar類型。這種生成策略與數(shù)據(jù)庫無關(guān),所以可以跨數(shù)據(jù)庫,方便數(shù)據(jù)庫移植,效率也很高,因?yàn)椴辉L問數(shù)據(jù)庫就可以生成主鍵值,并且可以保證唯一性。

2.持久化類


2.1 持久化類的編寫規(guī)則

實(shí)體類經(jīng)過 hibernate 操作轉(zhuǎn)換成持久化類,下面還是使用實(shí)體類說明規(guī)則。

  • 實(shí)體類提供無參的構(gòu)造方法。
    無參的構(gòu)造方法就算是不寫也可以,因?yàn)?jdk 會(huì)幫我們做,但最好加上這個(gè)無參的構(gòu)造方法。
  • 實(shí)體類的屬性要是私有的,并使用公開的 set 和 get 方法操作
    hibernate 在底層會(huì)將查詢到的數(shù)據(jù)進(jìn)行封裝,使用反射生成類的實(shí)例。
  • 實(shí)體類中要有屬性作為唯一值
    hibernate 要通過唯一的標(biāo)識(shí)區(qū)分內(nèi)存中是否有一個(gè)持久化類,在 java 中是通過地址區(qū)分是否是同一個(gè)對(duì)象的,在關(guān)系型數(shù)據(jù)庫的表中是通過主鍵區(qū)分是否有一條記錄的,在內(nèi)存中,hibernate 是不允許出現(xiàn)兩個(gè)OID (對(duì)象唯一標(biāo)識(shí)符)相同的持久化類的。
  • 實(shí)體類屬性的基本類型建議使用基本數(shù)據(jù)類型的包裝類
    包裝類和基本數(shù)據(jù)類型的默認(rèn)值是不同的,比如 int 類型的默認(rèn)值是 0,Integer 類型的默認(rèn)值是 null。并且包裝類的語義描述比基本數(shù)據(jù)類型更加清晰,比如,一個(gè)學(xué)生的成績(jī),可以是 0 分,也可以是 100 分,但如果這個(gè)學(xué)生沒有成績(jī),用基本的數(shù)據(jù)類型就很難表示了,但包裝類就可以用 null 來表示,這樣不會(huì)產(chǎn)生歧義。
  • 映射的實(shí)體類不要使用final關(guān)鍵字修飾
    hibernate 有延遲加載機(jī)制,這個(gè)機(jī)制會(huì)產(chǎn)生代理對(duì)象,產(chǎn)生代理對(duì)象是通過字節(jié)碼的增強(qiáng)技術(shù)來完成的,其實(shí)就是產(chǎn)生了當(dāng)前類的子類對(duì)象實(shí)現(xiàn)的,而是用 final 關(guān)鍵字修飾就無法產(chǎn)生子類。

2.2 持久化類的三種狀態(tài)

  • 瞬時(shí)態(tài)(臨時(shí)態(tài))(自由態(tài))
    瞬時(shí)態(tài)是對(duì)象只是 new 了出來,在內(nèi)存開辟了空間,但還沒有和 session 關(guān)聯(lián),也即是還沒有使用 session 操作內(nèi)存中的對(duì)象,這時(shí)候在數(shù)據(jù)庫里面是沒有記錄的。
  • 持久態(tài)
    new 出來的實(shí)體化類對(duì)象經(jīng)過 session 的操作,被加入到 session 的緩存中,并且與這個(gè)對(duì)象關(guān)聯(lián)的 session 也沒有關(guān)閉,這個(gè)時(shí)候就是持久態(tài),在數(shù)據(jù)庫中存在對(duì)應(yīng)的記錄,每條記錄對(duì)應(yīng)唯一的持久化對(duì)象,注意持久化對(duì)象是在還未提交事務(wù)錢就已經(jīng)是持久態(tài)了。
  • 托管態(tài)(游離態(tài))(離線態(tài))
    某個(gè)持久態(tài)的實(shí)例在和 session 對(duì)象關(guān)聯(lián)后,session 被關(guān)閉時(shí),這個(gè)對(duì)象就變成了托管態(tài),這個(gè)對(duì)象屬性值發(fā)生改變時(shí),hibernate 就無法檢測(cè)到,因?yàn)檫@個(gè)實(shí)例對(duì)象已經(jīng)失去了和 session 的關(guān)聯(lián)。

關(guān)于這三種狀態(tài)的理解,可以結(jié)合下面的 curd 操作和一級(jí)緩存來理解。

3.curd 操作


實(shí)體類的代碼

package cc.wenshixin.entity;

public class Notice {

    private int id; // 公告序號(hào)
    private String title; // 公告標(biāo)題
    private String content; // 公告內(nèi)容
    private String people; // 發(fā)布人
    private String date; // 發(fā)布日期

    public Notice()
    {
        
    }
    
    public Notice(String title, String content, String people, String date) {
        super();
        this.title = title;
        this.content = content;
        this.people = people;
        this.date = date;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getPeople() {
        return people;
    }

    public void setPeople(String people) {
        this.people = people;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    @Override
    public String toString() {
        return "Notice [id=" + id + ", title=" + title + ", content=" + content + ", people=" + people + ", date=" + date
                + "]";
    }

}

hibernate 自定義的工具類,方便操作 hibernate。

package cc.wenshixin.utility;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtility {
    private static Configuration cfg = null;
    private static SessionFactory sessionFactory = null;
    
    //靜態(tài)代碼塊
    static
    {
        //加載核心配置文件
        cfg = new Configuration().configure();
        sessionFactory = cfg.buildSessionFactory();
    }
    
    /*提供方法返回sessionFactory*/
    public static SessionFactory getSessionFactory()
    {
        return sessionFactory;
    }
    
    /*提供于本地線程綁定的session方法*/
    public static Session getSession()
    {
        return sessionFactory.getCurrentSession();
    }
}

下面的操作都是使用JUnit測(cè)試工具測(cè)試的代碼。

3.1 增加操作

增加操作讓持久化類從瞬時(shí)態(tài)變?yōu)槌志脩B(tài)。

@Test
    public void testSave()
    {
        //得到sessionFactory對(duì)象
        SessionFactory sessionFactory = HibernateUtility.getSessionFactory();
        //得session對(duì)象
        Session session = sessionFactory.openSession();
        //開啟事務(wù)
        Transaction tx = session.beginTransaction();
        
        /*執(zhí)行curd操作*/
        
        Notice notice = new Notice("實(shí)驗(yàn)室開放", "同學(xué)們課外可自由選擇實(shí)驗(yàn)", "admin", "2017-10-1");
        session.save(notice);
    
        //執(zhí)行事務(wù)
        tx.commit();
        //關(guān)閉session和sessionFactory
        session.close();
        sessionFactory.close();
    }

3.2 查詢操作

hibernate 的刪改操作都是基于查詢操作實(shí)現(xiàn)的。

@Test
    public void testGet()
    {
        //得到sessionFactory對(duì)象
        SessionFactory sessionFactory = HibernateUtility.getSessionFactory();
        //得到session
        Session session = sessionFactory.openSession();
        //開啟事務(wù)
        Transaction tx = session.beginTransaction();
        
        //執(zhí)行查詢操作
        Notice notice = session.get(Notice.class, 2);
        //這里要先重寫toString()方法
        System.out.println(notice.toString());
        
        //提交事務(wù)
        tx.commit();
        
        //關(guān)閉session和sessionFactory
        session.close();
        sessionFactory.close();
    }

3.3 刪除操作

下面展示了兩種方式來刪除一條記錄,但建議使用第一種,先查詢后刪除的方式,應(yīng)該避免第二種直接設(shè)置主鍵對(duì)應(yīng)屬性值的方式。

@Test
    public void testDelete()
    {
        //得到sessionFactory對(duì)象
        SessionFactory sessionFactory = HibernateUtility.getSessionFactory();
        //得到session對(duì)象
        Session session = sessionFactory.openSession();
        //開啟事務(wù)
        Transaction tx = session.beginTransaction();
        
        //執(zhí)行刪除操作
        //第一種方法
        //Notice notice = session.get(Notice.class, 3);
        
        //第二種方法
        //Notice notice = new Notice();
        //notice.setId(2);

        session.delete(notice);
        
        //提交事務(wù)
        tx.commit();
        
        //關(guān)閉session和sessionFactory
        session.close();
        sessionFactory.close();
    }

3.4 修改操作

先得到持久態(tài)的對(duì)象,再對(duì)這個(gè)對(duì)象進(jìn)行操作。

@Test
    public void testUpdate()
    {
        //得到sessionFactory對(duì)象
        SessionFactory sessionFactory = HibernateUtility.getSessionFactory();
        //得到session對(duì)象
        Session session = sessionFactory.openSession();
        //開啟事務(wù)
        Transaction tx = session.beginTransaction();
        
        //執(zhí)行更新操作
        Notice notice = session.get(Notice.class, 2);
        notice.setTitle("我改變了");
        session.update(notice);
        
        //執(zhí)行事務(wù)
        tx.commit();
        
        //關(guān)閉session和sessionFactory
        session.close();
        sessionFactory.close();
    }

3.5 增加或更新操作

saveOrUpdate()方法是更具持久化對(duì)象的狀態(tài)來做增加或者更新操作的,對(duì)象如果是瞬時(shí)態(tài),那么執(zhí)行事務(wù)就做增加操作,如果對(duì)象是托管態(tài),那么執(zhí)行事務(wù)就做更新操作,但此時(shí)要注意,更新操作要把持久化類的所有屬性都設(shè)置值,否則沒有設(shè)置屬性值的字段為null,下面的代碼就會(huì)產(chǎn)生這種情況,所以不推薦使用托管態(tài)修改數(shù)據(jù)表種的記錄。

@Test
    public void testSaveOrUpdate()
    {
        //得到sessionFactory對(duì)象
        SessionFactory sessionFactory = HibernateUtility.getSessionFactory();
        //得到session對(duì)象
        Session session = sessionFactory.openSession();
        //開啟事務(wù)
        Transaction tx = session.beginTransaction();
        
        //執(zhí)行增加或更新操作
        //Notice notice = new Notice("新的公告", "公告內(nèi)容", "admin", "2017-10-9");
        Notice notice = new Notice();
        notice.setId(4);
        notice.setPeople("admin");
        session.saveOrUpdate(notice);
        
        //提交事務(wù)
        tx.commit();
        
        //關(guān)閉session和sessionFactory
        session.close();
        sessionFactory.close(); 
    }

3.6 持久化類狀態(tài)之間的轉(zhuǎn)化

  • 瞬時(shí)態(tài)轉(zhuǎn)其他狀態(tài)
    瞬時(shí)態(tài)轉(zhuǎn)持久態(tài):執(zhí)行 session 對(duì)象的 save()方法或者 saveOrUpdate()方法
    瞬時(shí)態(tài)轉(zhuǎn)托管態(tài):為瞬時(shí)態(tài)對(duì)象設(shè)置持久化標(biāo)識(shí),也即是調(diào)用 setId()方法
Notice notice = new Notice(); //瞬時(shí)態(tài)
notice。setId(2); //托管態(tài)
  • 持久態(tài)轉(zhuǎn)其他狀態(tài)
    持久化對(duì)象可以通過 session 對(duì)象執(zhí)行 get()和 load()方法,或者 Query 查詢(后面會(huì)說到)從數(shù)據(jù)庫種獲得。
    持久態(tài)轉(zhuǎn)瞬時(shí)態(tài):執(zhí)行session的delete()方法
    持久態(tài)轉(zhuǎn)托管態(tài):執(zhí)行 session 的close()、clear() 或者 evict() 方法,evict()方法用于清除一級(jí)緩沖中的某一個(gè)對(duì)象,close()方法是用來關(guān)閉 session 對(duì)象,清除整個(gè)一級(jí)緩存,clear()方法用于清除一級(jí)緩存中的所有對(duì)象。
  • 托管態(tài)轉(zhuǎn)氣態(tài)狀態(tài)
    托管態(tài)對(duì)象是無法直接得到的,是由其他狀態(tài)對(duì)象轉(zhuǎn)化而來的,而托管態(tài)和瞬時(shí)態(tài)的區(qū)別就是 OID 有沒有值。
    托管態(tài)轉(zhuǎn)持久態(tài):執(zhí)行 session 的 update()、saveOrUpdate()或者lock()方法
    托管態(tài)轉(zhuǎn)瞬時(shí)態(tài):將托管態(tài)的持久化的 OID標(biāo)識(shí)設(shè)置為 null,也即是將作為主鍵的屬性值設(shè)置為 null

注意:由于持久化態(tài)對(duì)象的值改變,其實(shí)不用調(diào)用 update()方法或者 saveOrUpdate()方法,在執(zhí)行完事務(wù)后就可以自動(dòng)更新數(shù)據(jù)庫的(在一級(jí)緩存中會(huì)解釋自動(dòng)更新),但是還是建議把方法加上,便于閱讀代碼。

4.一級(jí)緩存


4.1 什么是一級(jí)緩存

首先我們要明白什么是緩存,數(shù)據(jù)庫本身其實(shí)就是一個(gè)文件系統(tǒng),并且我們知道使用流的方式操作文件效率不高,所以我們把數(shù)據(jù)放到內(nèi)存里面,這樣就可以直接讀取內(nèi)存里面的數(shù)據(jù),提高讀取的效率。

hibernate 框架提供了很多的優(yōu)化方式,一級(jí)緩沖就是優(yōu)化方式之一。hibernate 還有二級(jí)緩存,但現(xiàn)在已經(jīng)不適用了,使用 redis技術(shù)來代替了。

hibernate 的一級(jí)緩存就是指 session 緩存,session 緩沖就是一塊內(nèi)存空間,用來存放相互管理的 java 對(duì)象,在使用 hibernate 查詢對(duì)象時(shí),先根據(jù)對(duì)象的 OID(唯一標(biāo)識(shí)符)去一級(jí)緩存中查找,如果找到就直接從一級(jí)緩存中取出使用,不用再去數(shù)據(jù)庫查詢了,這樣就提高了查詢效率,如果一級(jí)緩存中沒有,就要去數(shù)據(jù)庫中查詢,然后把查到的數(shù)據(jù)信息放到一級(jí)緩存中。hibernate 的一級(jí)緩存的作用就是減少對(duì)數(shù)據(jù)庫的訪問。

4.2 一級(jí)緩存的特點(diǎn)

  • 1.hibernate 的一級(jí)緩存默認(rèn)時(shí)打開的。
  • 2.hibernate 的一級(jí)緩存使用范圍就是 session 范圍,是從 session 創(chuàng)建到 session 關(guān)閉。
  • 3.hibernate 的一級(jí)緩存,存儲(chǔ)數(shù)據(jù)必須是持久化數(shù)據(jù)。

4.3 驗(yàn)證一級(jí)緩存的存在

    Notice notice1 = session.get(Notice.class, 1);
    System.out.println(notice1);
    Notice notice2 = session.get(Notice.class, 1);
    System.out.println(notice2);
    //比較的是對(duì)象的指向的地址是否一樣
    System.out.println(notice1==notice2);

連續(xù)執(zhí)行查詢操作,觀察控制臺(tái)的輸出,發(fā)現(xiàn)只出現(xiàn)了一次查詢的 sql 語句,這就說明第二次的查詢不是在數(shù)據(jù)庫中查詢得到的,而是直接從 hibernate 的一級(jí)緩存中取的,并且比較兩個(gè)對(duì)象引用的地址也是true。

驗(yàn)證一級(jí)緩存

4.4 解釋持久化類自動(dòng)更新

在前面我們說持久化類改變屬性值后,不需使用 update()方法就可以自動(dòng)更新數(shù)據(jù)庫里面的記錄,我們需要指導(dǎo) hibernate 一級(jí)緩存的內(nèi)部結(jié)構(gòu)。在執(zhí)行完查詢操作后,把查詢到的數(shù)據(jù)放到緩沖區(qū),并且復(fù)制一份數(shù)據(jù)到快照區(qū),直接通過 set 方法改變持久化對(duì)象的屬性值,也會(huì)改變緩沖區(qū)里面的內(nèi)容,在提交事務(wù)時(shí)比較緩沖區(qū)和快照區(qū)里面的數(shù)據(jù)是否一致,如果不一致,就更新數(shù)據(jù)庫中的記錄,并更新快照區(qū)中的數(shù)據(jù)。快照區(qū)的作用就是確保一級(jí)緩存中的數(shù)據(jù)和數(shù)據(jù)庫中的數(shù)據(jù)一致。

持久化類自動(dòng)更新

5.事務(wù)操作


hibernate 是 jdbc 的輕量級(jí)封裝,hibernate 的事務(wù)處理就是數(shù)據(jù)庫的事務(wù)處理。

5.1 什么是事務(wù)

在數(shù)據(jù)庫操作上,一項(xiàng)事務(wù)是由一條或多條操作數(shù)據(jù)庫的 sql 語句組成的一個(gè)不可分割的工作單元。只有當(dāng)事務(wù)中的所有操作都正常完成,整個(gè)事務(wù)才會(huì)被提交到數(shù)據(jù)庫中。如果事務(wù)中由一項(xiàng)操作沒有完成,則整個(gè)事務(wù)就會(huì)被回滾。事務(wù)簡(jiǎn)單理解起來就是,一組邏輯上的操作,組成這組操作的各個(gè)單元,要么一起成功,要么一起失敗,具有統(tǒng)一性。

5.2 事務(wù)的四個(gè)特性詳解

事務(wù)有很嚴(yán)格的定義,需要同時(shí)滿足下面的四個(gè)特性,這四個(gè)特性通常稱之為 ACID 特性。

  • 原子型(Atomic):表示將事務(wù)中所做的操作捆綁成一個(gè)不可分割的單元,即對(duì)事務(wù)所進(jìn)行的數(shù)據(jù)修改等操作,要么全部執(zhí)行,要么全都不執(zhí)行。
  • 一致性(Consistency):表示事務(wù)完成時(shí),必須使所有的數(shù)據(jù)都保持一致狀態(tài)。
  • 隔離性(Isolation):指一個(gè)事務(wù)的執(zhí)行不能被其他事務(wù)干擾,即一個(gè)事務(wù)內(nèi)部的操作以及使用的數(shù)據(jù)對(duì)并發(fā)的其他事務(wù)都是隔離的,并發(fā)執(zhí)行的各個(gè)事務(wù)之間不能互相干擾。
  • 持久性(Durability):持久性也稱永久性,指一個(gè)事務(wù)一旦被提交,它對(duì)數(shù)據(jù)庫中的數(shù)據(jù)改變就應(yīng)該是永久性的。提交后其他事務(wù)對(duì)其他操作或故障不會(huì)對(duì)它有任何影響。

5.3 事務(wù)的并發(fā)問題

在實(shí)際應(yīng)用中,數(shù)據(jù)庫是要被多個(gè)用戶共同訪問的,在多個(gè)事務(wù)同時(shí)使用相同的數(shù)據(jù)時(shí),可能會(huì)發(fā)生并發(fā)的問題。

  • 臟讀:一個(gè)事務(wù)讀取到了另一個(gè)事務(wù)未提交的數(shù)據(jù)。
  • 不可重復(fù)度:一個(gè)事務(wù)讀到了另一個(gè)事務(wù)已經(jīng)提交的 update 的數(shù)據(jù),導(dǎo)致在同一個(gè)事務(wù)中的查詢結(jié)果不一致。
  • 虛讀/幻讀:一個(gè)事務(wù)讀到了另一個(gè)事務(wù)已經(jīng)提交的 insert 的數(shù)據(jù),導(dǎo)致在同一個(gè)事務(wù)中的多次查詢結(jié)果不一致。

5.4 事務(wù)的隔離級(jí)別

為了避免上面所說的事務(wù)并發(fā)問題發(fā)生,所以在標(biāo)準(zhǔn)的 SQL 規(guī)范中,定義了四個(gè)事務(wù)隔離級(jí)別,不同的隔離級(jí)別對(duì)事務(wù)的處理是不同的。

  • 讀未提交(Read Uncommitted, 1級(jí)):一個(gè)事務(wù)在執(zhí)行過程中,即可以訪問其事務(wù)未提交的新插入的數(shù)據(jù),又可以訪問未提交的修改數(shù)據(jù)。如果一個(gè)事務(wù)已經(jīng)開始寫數(shù)據(jù),而另一個(gè)事務(wù)則不允許同時(shí)進(jìn)行寫操作,但允許其他事務(wù)讀此行數(shù)據(jù),此隔離級(jí)別可防止丟失更新。
  • 已提交讀(Read Commited,2級(jí)):一個(gè)事務(wù)在執(zhí)行過程中,既可以訪問其他事務(wù)成功提交的新插入的數(shù)據(jù),又可以訪問成功修改的數(shù)據(jù)。讀取數(shù)據(jù)的事務(wù)允許其他事務(wù)繼續(xù)訪問該行數(shù)據(jù),但是未提交的寫事務(wù)將會(huì)禁止其他事務(wù)訪問該行。此隔離級(jí)別可有效防止臟讀。
  • 可重復(fù)讀(Repeated Read,4級(jí)):一個(gè)事務(wù)在執(zhí)行過程中,可以訪問其他事務(wù)成功提交的新插入的數(shù)據(jù),但不可以訪問成功修改的數(shù)據(jù)。讀取數(shù)據(jù)的事務(wù)將會(huì)禁止寫事務(wù)(但允許讀事務(wù)),寫事務(wù)則禁止任何其他事務(wù),此隔離級(jí)別可有效的防止不可重復(fù)讀和臟讀。
  • 序列化/串行化(Serializable,8級(jí)):提供嚴(yán)格的事務(wù)隔離,它要求事務(wù)序列化執(zhí)行,事務(wù)只能一個(gè)接著一個(gè)地執(zhí)行,但不能并發(fā)執(zhí)行。此隔離級(jí)別可有效的防止臟讀,不可重復(fù)讀和幻讀。

事務(wù)的隔離級(jí)別是由數(shù)據(jù)庫提供的,但并不是所有數(shù)據(jù)庫都支持四種隔離級(jí)別的。在使用數(shù)據(jù)庫時(shí),隔離級(jí)別越高,安全性越高,性能越低。在實(shí)際的開發(fā)中,不會(huì)選擇最高或者最低的隔離級(jí)別,使用數(shù)據(jù)庫默認(rèn)的即可。

5.5 hibernate 事務(wù)規(guī)范代碼

在 hibernate 中,可以通過代碼來操作管理事務(wù),如通過 Transaction tx = session.beginTransaction(); 開啟一個(gè)事務(wù),持久化操作后,通過 tx.commit(); 提交事務(wù),如果事務(wù)出現(xiàn)異常,要通過 tx.rollback(); 操作來撤銷事務(wù)(回滾事務(wù))。

除了在代碼中對(duì)事務(wù)開啟,提交和回滾操作外,還可以在 hibernate 的配置文件中對(duì)事務(wù)進(jìn)行配置。在配置文件中,可以設(shè)置事務(wù)的隔離級(jí)別。其具體的配置方法是在 hibernate.cfg.xml 文件中的 property 標(biāo)簽中進(jìn)行的。配置方法:<property name="hibernate.connection.isolation">4</property> ,并且我們?cè)谶M(jìn)行正真的事務(wù)管理時(shí),需要考慮到事務(wù)的應(yīng)用場(chǎng)景,事務(wù)的控制不應(yīng)該放在 DAO 層,而應(yīng)該放在 Service 層調(diào)用多個(gè) DAO 實(shí)現(xiàn)一個(gè)業(yè)務(wù)邏輯的操作。其實(shí)最主要的是如何保證在 Service 中開啟事務(wù)時(shí)使用的 Session 對(duì)象和 DAO 中多個(gè)操作使用的是同一個(gè) Session 對(duì)象。

事務(wù)處理的層

下面有兩種解決辦法。

    1. 可以在業(yè)務(wù)層獲取到 Session,并將 Session 作為參數(shù)傳遞給 DAO。
    1. 可以使用 ThreadLocal 將業(yè)務(wù)層獲取的 Session 綁定到當(dāng)前線程,然后在 DAO 中獲取 Session 時(shí)都從當(dāng)前的線程中獲取。

第二種方式時(shí)最優(yōu)的方案,而且具體的實(shí)現(xiàn),hibernate 已經(jīng)在內(nèi)部完成了,我們只需要配置一下。hibernate5 種提供了三種管理 Session 對(duì)象的方法。

    1. Session 對(duì)象的生命周期與本地線程綁定
    1. Session 對(duì)象的生命周期與 JTA(Java Transaction API,Java事務(wù)API,是一個(gè)Java企業(yè)版的應(yīng)用程序接口)事務(wù)綁定
    1. hibernate 委托程序管理 Session 對(duì)象的生命周期

在 hibernate 的配置文件中,hibernate.current_session_context_class 屬性用于指定 Session 管理方式,可選值有:1. tread,Session 對(duì)象的生命周期與本地線程綁定;2. jta,Session 對(duì)象的生命周期與 JTA 事務(wù)綁定;managed,hibernate 委托程序來管理 Session 對(duì)象的生命周期。在這里我們選擇 tread 值,在 hibernate.cfg.xml 中進(jìn)行配置:<property name="hibernate.current_session_context_class">thread</property>,并且在 hibernate 中提供了 getCurrentSession()方法來創(chuàng)建一個(gè) Session 和本地線程 TreadLocal 綁定的方法。

    /*提供于本地線程綁定的session方法*/
    public static Session getSession()
    {
        return sessionFactory.getCurrentSession();
    }

hibernate 提供的這個(gè)與本地線程綁定的 Session 可以不用關(guān)閉,當(dāng)線程執(zhí)行結(jié)束后,就會(huì)自動(dòng)關(guān)閉了。

下面給出事務(wù)操作的規(guī)范代碼寫法。

代碼結(jié)構(gòu)如下:

try {
  開啟事務(wù)
  提交事務(wù)
}catch() {
  回滾事務(wù)
}finally {
  關(guān)閉
}
    @Test
    public void testTx1()
    {
        Session session = null;
        Transaction tx = null;
        
        try{
            //得到與本地線程綁定的 Session
            session = HibernateUtility.getSession();
            //開啟事務(wù)
            tx = session.beginTransaction();
            
            //添加操作
            Notice notice = new Notice("本地線程綁定", "規(guī)范操作", "admin", "2017-10-8");
            session.save(notice);
            
            //提交事務(wù)
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
            //回滾事務(wù)
            tx.rollback();
        } finally {
            //不需要關(guān)閉session
        }
    }
    
    //下面的代碼只是上面代碼的對(duì)比
    @Test
    public void testTx2()
    {
        SessionFactory sessionFactory = null;
        Session session = null;
        Transaction tx = null;
        
        try{
            sessionFactory = HibernateUtility.getSessionFactory();
            //不是與本地線程綁定的 Session,類似于單例模式。
            session = sessionFactory.openSession();
            //開啟事務(wù)
            tx = session.beginTransaction();
            
            //添加操作
            Notice notice = new Notice("本地線程綁定", "規(guī)范操作", "admin", "2017-10-8");
            session.save(notice);
            
            //提交事務(wù)
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
            //回滾事務(wù)
            tx.rollback();
        } finally {
            //需要關(guān)閉session
            session.close();
            sessionFactory.close();
        }
    }

6.hibernate 查詢相關(guān)API的簡(jiǎn)單介紹


在前面,我們只進(jìn)行了簡(jiǎn)單的 curd 操作,對(duì)于查詢操作,hibernate 還有幾種不同的 API 可以選擇使用,在這里先簡(jiǎn)單介紹一下,在后面還會(huì)詳細(xì)敘述。

6.1 Query 對(duì)象

使用 query 對(duì)象,不需要寫 sql 語句,但要寫簡(jiǎn)單的 hql(hibernate query language,hibernate 的查詢語言) 語句。

hql 和 sql 語句的區(qū)別:

    1. hql 語句是直接使用實(shí)體類和屬性來做查詢
    1. sql 語句是要操作數(shù)據(jù)表和字段

hql語句的寫法:from 實(shí)體類的名稱

Query 對(duì)象的使用:

    1. 創(chuàng)建 query 對(duì)象
    1. 調(diào)用 query 對(duì)象里面的方法得到結(jié)果

示例代碼如下:

    @Test
    //查詢表中所有數(shù)據(jù)
    public void testQuery1()
    {
        Session session = HibernateUtility.getSession();
        Transaction tx = session.beginTransaction();
        
        Query<Notice> query = session.createQuery("from Notice");
        List<Notice> list = query.list();
        for(Notice notice : list)
        {
            System.out.println(notice);
        }
    }
    
    @Test
    //有條件的查詢
    public void testQuery2()
    {
        Session session = HibernateUtility.getSession();
        Transaction tx = session.beginTransaction();
        
        Query<Notice> query = session.createQuery("from Notice where title=?");
        query.setString(0, "實(shí)驗(yàn)室開放");
        List<Notice> list = query.list();
        for(Notice notice : list)
        {
            System.out.println(notice);
        }
    }

6.2 Criteria 對(duì)象

使用 criteria 對(duì)象,不需要寫語句,直接調(diào)用方法來實(shí)現(xiàn)。

criteria 對(duì)象的使用:

    1. 創(chuàng)建 criteria 對(duì)象
    1. 調(diào)用對(duì)象里面的方法得到結(jié)果

示例代碼如下:

    @Test
    //查詢表中所有數(shù)據(jù)
    public void testCriteria1()
    {
        Session session = HibernateUtility.getSession();
        Transaction tx = session.beginTransaction();
        
        Criteria criteria = session.createCriteria(Notice.class);
        List<Notice> list = criteria.list();
        for(Notice notice : list)
        {
            System.out.println(notice);
        }   
    }
    
    @Test
    //有條件的查詢
    public void testCriterial2()
    {
        Session session = HibernateUtility.getSession();
        Transaction tx = session.beginTransaction();
        
        Criteria criteria = session.createCriteria(Notice.class);
        criteria.add(Restrictions.eq("title", "實(shí)驗(yàn)室開放"));
        List<Notice> list = criteria.list();
        for(Notice notice : list)
        {
            System.out.println(notice);
        }
    }

6.3 SQLQuery 對(duì)象

從名字就可以看出是和 sql 有關(guān)的,直接寫 sql 語句,底層 hibernate 調(diào)用的是 sql 語句實(shí)現(xiàn)的。

SQLQuery 對(duì)象

    1. 創(chuàng)建 SQLQuery 對(duì)象
    1. 調(diào)用對(duì)象的方法得到結(jié)果

示例代碼如下:

@Test
    public void testSQLQuery()
    {
        Session session = HibernateUtility.getSession();
        Transaction tx = session.beginTransaction();
        
        SQLQuery sqlQuery = session.createSQLQuery("SELECT * FROM notice_content");
        List<Object[]> list = sqlQuery.list();
        for(Object[] objects : list)
        {
            System.out.println(Arrays.toString(objects));
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 本文包括:1、Hibernate的持久化類2、Hibernate 持久化對(duì)象的三個(gè)狀態(tài)(難點(diǎn))3、Hibernat...
    廖少少閱讀 1,484評(píng)論 0 13
  • Hibernate是一個(gè)開放源代碼的對(duì)象關(guān)系映射框架,它對(duì)JDBC進(jìn)行了非常輕量級(jí)的對(duì)象封裝,它將POJO與數(shù)據(jù)庫...
    蘭緣小妖閱讀 1,222評(píng)論 1 18
  • 這部分主要是開源Java EE框架方面的內(nèi)容,包括Hibernate、MyBatis、Spring、Spring ...
    雜貨鋪老板閱讀 1,424評(píng)論 0 2
  • Hibernate: 一個(gè)持久化框架 一個(gè)ORM框架 加載:根據(jù)特定的OID,把一個(gè)對(duì)象從數(shù)據(jù)庫加載到內(nèi)存中OID...
    JHMichael閱讀 1,995評(píng)論 0 27
  • 不惜一切的愛自己 不顧一切的活下去
    時(shí)一么么噠閱讀 212評(píng)論 0 1