SSH框架之旅-hibernate(2)

hibernate

1.主鍵生成策略


1.1 主鍵的兩種類型

  • 自然主鍵:把數(shù)據(jù)表中的某一業(yè)務(wù)字段作為表的主鍵。如一張用戶表中磨取,把用戶的用戶名作為用戶表的主鍵骂蓖。這樣做的前提條件是鸭蛙,1.用戶的用戶名不能為空,2.用戶的用戶名不能重復(fù),用戶的用戶名不能修改卓练。這樣盡管也是可以的隘蝎,但不能很好的滿足業(yè)務(wù)需求的改變,所以不推薦使用自然主鍵的方式襟企。
  • 代理主鍵:單獨為數(shù)據(jù)表設(shè)置一個字段作為數(shù)據(jù)表的主鍵嘱么。作為主鍵的這個字段沒有業(yè)務(wù)含義,一般直接取名為id顽悼,通常為整數(shù)類型曼振,因為整型要比字符型節(jié)省數(shù)據(jù)庫的空間,所以一般都是使用代理主鍵的方式設(shè)置數(shù)據(jù)表的主鍵蔚龙。

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

1.2 hibernate 中主鍵的生成策略

  • assigned 自然主鍵類型
    在程序中設(shè)置主鍵木羹。如果在映射表中不設(shè)置 generator 屬性甲雅,hibernate 默認(rèn)使用該主鍵生成策略。但不建議使用這種方式坑填,盡量要減少手動對主鍵的操作抛人。
  • increment 代理主鍵類型
    用于整型類型,由 hibernate 自動以遞增的方式生成脐瑰,每次增量為一妖枚,但只有當(dāng)沒有其他進(jìn)程相同一張表中插入數(shù)據(jù)時,才可以使用苍在,不能在集群環(huán)境下使用绝页。
  • identity 代理主鍵類型
    由底層數(shù)據(jù)庫設(shè)置主鍵,與 hibernate 無關(guān)忌穿。但前提是使用的數(shù)據(jù)庫要支持自動增長數(shù)據(jù)類型抒寂,如 MySQL 是支持主鍵自動生成的,但 Oracle 就不支持主鍵自動生成掠剑。如果數(shù)據(jù)庫支持主鍵自增屈芜,是可以采用該主鍵生成策略的。
  • sequence 代理主鍵類型
    由底層數(shù)據(jù)庫根據(jù)序列生成主鍵朴译,與 hibernate 無關(guān)井佑。但前提是數(shù)據(jù)庫要支持序列,Oracle 是支持的眠寿。如果數(shù)據(jù)庫支持序列躬翁,是可以采用該主鍵生成策略的。
  • hilo 代理主鍵類型
    hibernate 生成主鍵盯拱,hilo 是 high low (高低位方式)的縮寫盒发,是 hibernate 常用的一種生成方式例嘱,需要一張額外的表來保存 hi(高位)的值,并手動設(shè)置 max_lo 的值宁舰,然后通過算法公式(hi * (max_lo + 1) + 0)來生成主鍵拼卵。這種生成策略可以跨數(shù)據(jù)庫,但由hilo算法生成的標(biāo)志只能保證在一個數(shù)據(jù)庫是唯一的蛮艰。
  • natve 代理主鍵類型
    根據(jù)底層數(shù)據(jù)庫腋腮,自動選擇identity、sequence壤蚜、hilo 策略即寡。但由于生成策略的控制權(quán)在 hibernate 手上,不建議采用袜刷,并且這種生成策略效率比較低聪富。
  • uuid 代理主鍵類型
    由 hibernate 使用 128 為的UUID算法來生成標(biāo)識符(主鍵),該算法可以在網(wǎng)絡(luò)環(huán)境中生成唯一字符串的標(biāo)識符水泉。長度是一個 32 位的十六進(jìn)制字符串善涨,占用控空間比較大,對應(yīng)數(shù)據(jù)庫的char/varchar類型草则。這種生成策略與數(shù)據(jù)庫無關(guān)钢拧,所以可以跨數(shù)據(jù)庫,方便數(shù)據(jù)庫移植炕横,效率也很高源内,因為不訪問數(shù)據(jù)庫就可以生成主鍵值,并且可以保證唯一性份殿。

2.持久化類


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

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

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

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

  • 瞬時態(tài)(臨時態(tài))(自由態(tài))
    瞬時態(tài)是對象只是 new 了出來,在內(nèi)存開辟了空間检眯,但還沒有和 session 關(guān)聯(lián)厘擂,也即是還沒有使用 session 操作內(nèi)存中的對象,這時候在數(shù)據(jù)庫里面是沒有記錄的锰瘸。
  • 持久態(tài)
    new 出來的實體化類對象經(jīng)過 session 的操作刽严,被加入到 session 的緩存中,并且與這個對象關(guān)聯(lián)的 session 也沒有關(guān)閉避凝,這個時候就是持久態(tài)舞萄,在數(shù)據(jù)庫中存在對應(yīng)的記錄,每條記錄對應(yīng)唯一的持久化對象管削,注意持久化對象是在還未提交事務(wù)錢就已經(jīng)是持久態(tài)了倒脓。
  • 托管態(tài)(游離態(tài))(離線態(tài))
    某個持久態(tài)的實例在和 session 對象關(guān)聯(lián)后,session 被關(guān)閉時含思,這個對象就變成了托管態(tài)崎弃,這個對象屬性值發(fā)生改變時,hibernate 就無法檢測到茸俭,因為這個實例對象已經(jīng)失去了和 session 的關(guān)聯(lián)吊履。

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

3.curd 操作


實體類的代碼

package cc.wenshixin.entity;

public class Notice {

    private int id; // 公告序號
    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測試工具測試的代碼腾窝。

3.1 增加操作

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

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

3.2 查詢操作

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

@Test
    public void testGet()
    {
        //得到sessionFactory對象
        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è)置主鍵對應(yīng)屬性值的方式。

@Test
    public void testDelete()
    {
        //得到sessionFactory對象
        SessionFactory sessionFactory = HibernateUtility.getSessionFactory();
        //得到session對象
        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)的對象唇敞,再對這個對象進(jìn)行操作蔗草。

@Test
    public void testUpdate()
    {
        //得到sessionFactory對象
        SessionFactory sessionFactory = HibernateUtility.getSessionFactory();
        //得到session對象
        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()方法是更具持久化對象的狀態(tài)來做增加或者更新操作的,對象如果是瞬時態(tài)疆柔,那么執(zhí)行事務(wù)就做增加操作咒精,如果對象是托管態(tài),那么執(zhí)行事務(wù)就做更新操作旷档,但此時要注意模叙,更新操作要把持久化類的所有屬性都設(shè)置值,否則沒有設(shè)置屬性值的字段為null鞋屈,下面的代碼就會產(chǎn)生這種情況范咨,所以不推薦使用托管態(tài)修改數(shù)據(jù)表種的記錄。

@Test
    public void testSaveOrUpdate()
    {
        //得到sessionFactory對象
        SessionFactory sessionFactory = HibernateUtility.getSessionFactory();
        //得到session對象
        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)化

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

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

4.一級緩存


4.1 什么是一級緩存

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

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

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

4.2 一級緩存的特點

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

4.3 驗證一級緩存的存在

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

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

驗證一級緩存

4.4 解釋持久化類自動更新

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

持久化類自動更新

5.事務(wù)操作


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

5.1 什么是事務(wù)

在數(shù)據(jù)庫操作上骨田,一項事務(wù)是由一條或多條操作數(shù)據(jù)庫的 sql 語句組成的一個不可分割的工作單元耿导。只有當(dāng)事務(wù)中的所有操作都正常完成,整個事務(wù)才會被提交到數(shù)據(jù)庫中态贤。如果事務(wù)中由一項操作沒有完成舱呻,則整個事務(wù)就會被回滾。事務(wù)簡單理解起來就是悠汽,一組邏輯上的操作箱吕,組成這組操作的各個單元,要么一起成功柿冲,要么一起失敗茬高,具有統(tǒng)一性。

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

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

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

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

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

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

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

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

  • 讀未提交(Read Uncommitted柱嫌, 1級):一個事務(wù)在執(zhí)行過程中,即可以訪問其事務(wù)未提交的新插入的數(shù)據(jù)屯换,又可以訪問未提交的修改數(shù)據(jù)编丘。如果一個事務(wù)已經(jīng)開始寫數(shù)據(jù),而另一個事務(wù)則不允許同時進(jìn)行寫操作趟径,但允許其他事務(wù)讀此行數(shù)據(jù)瘪吏,此隔離級別可防止丟失更新。
  • 已提交讀(Read Commited蜗巧,2級):一個事務(wù)在執(zhí)行過程中,既可以訪問其他事務(wù)成功提交的新插入的數(shù)據(jù)蕾盯,又可以訪問成功修改的數(shù)據(jù)幕屹。讀取數(shù)據(jù)的事務(wù)允許其他事務(wù)繼續(xù)訪問該行數(shù)據(jù),但是未提交的寫事務(wù)將會禁止其他事務(wù)訪問該行级遭。此隔離級別可有效防止臟讀望拖。
  • 可重復(fù)讀(Repeated Read,4級):一個事務(wù)在執(zhí)行過程中挫鸽,可以訪問其他事務(wù)成功提交的新插入的數(shù)據(jù)说敏,但不可以訪問成功修改的數(shù)據(jù)。讀取數(shù)據(jù)的事務(wù)將會禁止寫事務(wù)(但允許讀事務(wù))丢郊,寫事務(wù)則禁止任何其他事務(wù)盔沫,此隔離級別可有效的防止不可重復(fù)讀和臟讀。
  • 序列化/串行化(Serializable枫匾,8級):提供嚴(yán)格的事務(wù)隔離架诞,它要求事務(wù)序列化執(zhí)行,事務(wù)只能一個接著一個地執(zhí)行干茉,但不能并發(fā)執(zhí)行谴忧。此隔離級別可有效的防止臟讀,不可重復(fù)讀和幻讀。

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

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

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

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

事務(wù)處理的層

下面有兩種解決辦法粱年。

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

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

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

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

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

hibernate 提供的這個與本地線程綁定的 Session 可以不用關(guān)閉干旧,當(dāng)線程執(zhí)行結(jié)束后,就會自動關(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
        }
    }
    
    //下面的代碼只是上面代碼的對比
    @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的簡單介紹


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

6.1 Query 對象

使用 query 對象赏半,不需要寫 sql 語句,但要寫簡單的 hql(hibernate query language淆两,hibernate 的查詢語言) 語句。

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

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

hql語句的寫法:from 實體類的名稱拂酣。

Query 對象的使用:

    1. 創(chuàng)建 query 對象
    1. 調(diào)用 query 對象里面的方法得到結(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, "實驗室開放");
        List<Notice> list = query.list();
        for(Notice notice : list)
        {
            System.out.println(notice);
        }
    }

6.2 Criteria 對象

使用 criteria 對象秋冰,不需要寫語句,直接調(diào)用方法來實現(xiàn)婶熬。

criteria 對象的使用:

    1. 創(chuàng)建 criteria 對象
    1. 調(diào)用對象里面的方法得到結(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", "實驗室開放"));
        List<Notice> list = criteria.list();
        for(Notice notice : list)
        {
            System.out.println(notice);
        }
    }

6.3 SQLQuery 對象

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

SQLQuery 對象

    1. 創(chuàng)建 SQLQuery 對象
    1. 調(diào)用對象的方法得到結(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)容合作請聯(lián)系作者
  • 序言:七十年代末虽另,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子饺谬,更是在濱河造成了極大的恐慌捂刺,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異族展,居然都是意外死亡森缠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門仪缸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贵涵,“玉大人,你說我怎么就攤上這事恰画”雒” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵拴还,是天一觀的道長跨晴。 經(jīng)常有香客問我,道長自沧,這世上最難降的妖魔是什么坟奥? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮拇厢,結(jié)果婚禮上爱谁,老公的妹妹穿的比我還像新娘。我一直安慰自己孝偎,他們只是感情好访敌,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著衣盾,像睡著了一般寺旺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上势决,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天阻塑,我揣著相機(jī)與錄音,去河邊找鬼果复。 笑死陈莽,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的虽抄。 我是一名探鬼主播走搁,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼迈窟!你這毒婦竟也來了私植?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤车酣,失蹤者是張志新(化名)和其女友劉穎曲稼,沒想到半個月后索绪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡躯肌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年者春,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片清女。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡钱烟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嫡丙,到底是詐尸還是另有隱情拴袭,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布曙博,位于F島的核電站拥刻,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏父泳。R本人自食惡果不足惜般哼,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望惠窄。 院中可真熱鬧蒸眠,春花似錦、人聲如沸杆融。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脾歇。三九已至蒋腮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間藕各,已是汗流浹背池摧。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留激况,地道東北人险绘。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像誉碴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子瓣距,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359

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

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