Hibernate對象狀態詳解

Hibernate是一個完整的ORM解決方法,它幫助開發者屏蔽了底層對於數據庫訪問的細節,而且提供了對象狀態的概念。所以,對於性能要求不高的系統而言,若是使用Hibernate做爲持久化的解決方案,開發者能夠更多的去思考對象的狀態,而不是底層SQL的執行。可是若是當開發者須要去提高系統性能的時候,咱們就須要去更多的關注底層SQL的執行。sql

本文內容旨在討論Hibernate對象狀態(內容徹底參考Hibernate4.3官方手冊)數據庫

 Hibernate object states:緩存

1.Transient :session

當一個對象被剛剛new出來的時候而且和Hibernate中的session沒有關聯,此時它就是一個Transitent狀態。Transient狀態對象的特色是在數據庫中沒有記錄而且沒有oid,同時沒有session與其關聯。若是該對象再也不有引用指向其時,那麼該對象就會被GC所回收,從上面的圖中能夠清楚看到。app

 2.Persistent:dom

一個Persistent狀態的對象會在數據庫中存在1條記錄而且此時該對象就有oid,而且存在一個session與該對象關聯。ide

3.Detached:性能

Detached狀態的對象是在數據庫中存在記錄而且具備oid,可是不存在與之關聯的session。spa

 

Transitent------>Persistenthibernate

咱們可使用session的save或者saveOrUpdate或者persist方法來將瞬時狀態對象變成持久化狀態對象。

 1     public void testSave01() {
 2         Session session = null;
 3         try {
 4             session = HibernateUtil.openSession();
 5             session.beginTransaction();
 6             
 7             DomesticCat fritz  = new DomesticCat();
 8             fritz.setColor("Red");
 9             fritz.setSex('M');
10             fritz.setName("Fritz");
11             
12             Long id = (Long) session.save(fritz);
13             
14             System.out.println("Generated ID is:"+id);
15             
16             session.getTransaction().commit();
17         } catch (Exception e) {
18             if (session != null) {
19                 session.getTransaction().rollback();
20             }
21         } finally {
22             if (session != null) {
23                 session.close();
24             }
25         }
26     }

save方法存儲的特色:當save方法執行完畢以後會當即產生1條insert語句,而且會將對象的oid做爲返回值返回。與此同時,即便在事務未開啓或者事務已經提交的狀況下執行save方法一樣會產生insert語句。

persist方法存儲的特色:它不會當即爲對象分配oid,也不會當即產生insert語句。而是會在session.flush()的時候完成這項操做,一般當提交事務的時候,Hibernate會自動完成session.flush()方法的調用。除此以外,若是當事務未開啓或者事務已經提交的狀況下,執行persist方法,也不會產生任何的sql語句。

除了使用以上方法能夠將對象的狀態變成持久化以外,還可使用load方法或者query方法獲得一個persistent狀態的對象。對於persistent狀態的對象最大的特色就是當session.flush的時候(事務提交的時候),Hibernate會自動地去檢測當前持久化對象與session中維護的對象的狀態是否存在不一樣,若是不一樣,Hibernate會自動的完成更新操做,而不用開發者手動地去執行update語句。

    @Test
    public void persistent1(){
        Session session = null;
        try {
            session = HibernateUtil.openSession();
            session.beginTransaction();
            //此時對象的狀態爲持久化狀態
            DomesticCat domesticCat =  (DomesticCat) session.load(DomesticCat.class, new Long(1));
            domesticCat.setName("WangWang");
            //此時不會發送任何的sql
            session.update(domesticCat);
            //由於此時該對象已是持久化狀態的對象,因此也不會發送任何的sql語句
            session.save(domesticCat);
            //由於持久化對象的狀態已經發生了改變,因此會生成update語句
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            if(session != null){
                session.getTransaction().rollback();
            }
        }finally{
            if(session != null){
                session.close();
            }
        }
    }

使用load方法的注意事項:

load方法的注意事項:load方法查詢一個不存在的對象時,首先會使用 GGLIB 去返回一個代理對象,此時Hibernate不會與數據庫交互,當調用真實對象的具體方法時,Hibernate纔會發送sql到數據庫,若是此時沒有數據,Hibernate則會拋出ObjectNotFoundException

使用load獲取到代理對象以後,若是在sesion關閉以後纔去調用對象的方法,那麼因爲返回的代理對象還未初始化,此時會引起LazyInitationlException。(後面會在Hibernate的延遲加載分析中結合源碼來具體分析其實現)

若是使用get方法進行查詢,那麼Hibernate會當即發送select語句完成查詢,而且不會在返回代理對象,而是返回真實對象,若是查詢不到,則返回一個null。

 

除了使用load方法能夠將detached狀態轉變成persistent狀態,當咱們須要查詢多個對象的時候,一般會採用Hiberante爲咱們Query對象來完成列表的查詢。而後採用HQL替代傳統  的SQL語句。使用Query所查詢  到的對象一樣也是持久化對象。

    /**
     * 簡單查詢,查詢列表
     */
    @SuppressWarnings("unchecked")
    @Test
    public void query1() {
        Session session = null;
        try {
            session = HibernateUtil.openSession();
            session.beginTransaction();
            // HQL是基於對象的查詢方式,此時查詢到全部對象都是持久化狀態
            List<DomesticCat> cats = (List<DomesticCat>) session
                    .createQuery("from DomesticCat cat where cat.id > ?")
                    .setParameter(0, new Long(2)).list();
            for (DomesticCat cat : cats) {
                cat.setSex('M');
            }
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }

以上對N個對象修改了狀態以後,當事務提交的時候,就會產生N條update語句。

 

Detached ---->Persistent

還能夠將detached狀態的對象修改爲persitent狀態:這裏咱們藉助文檔中的例子給出一個典型的場景應用

the application loads an object in the first session(應用第一次使用一個session加載完成對象,session關閉)

the object is passed up to the UI tier (對象返回給視圖層)

some modifications are made to the object (用戶操做,對象狀態被修改)

the object is passed back down to the business logic tier(對象被傳遞到業務邏輯層,調用持久化層)

the application persists these modifications by calling update() in a second session(此時會使用一個新的session完調用update方法完成對象狀態的改變,同時會當即發送1條sql語句到數據庫)

使用update方法注意事項:

1.更新一個瞬時狀態的對象

@Test
    public void update1(){
        Session session = null;
        try {
            session = HibernateUtil.openSession();
            session.beginTransaction();
            DomesticCat cat = new DomesticCat();
            cat.setName("haha");
            cat.setSex('M');
            cat.setColor("Black");
            //直接update一個瞬時對象,Hibernate會拋出異常
            session.update(cat);
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            if(session != null){
                session.getTransaction().rollback();
            }
        }finally{
            if(session != null){
                session.close();
            }
        }
    }

此時會產生org.hibernate.TransientObjectException: The given object has a null identifier: org.plx.hibernate.domain.DomesticCat

因而可知:Hibernateupdate的時候會去檢測當前對象的oid是否存在,若是不存在,Hibernate就認爲這個對象是瞬時狀態的對象,因此會對外拋出異常。

2.更新一個數據庫中沒有的對象,可是該對象具備oid

@Test
    public void update2(){
        Session session = null;
        try {
            session = HibernateUtil.openSession();
            session.beginTransaction();
            DomesticCat cat = new DomesticCat();
            cat.setId(new Long(100));
            cat.setName("haha");
            cat.setSex('M');
            cat.setColor("Black");
            /*
             * 此時數據庫裏其實並無id爲100的記錄,可是會發送sql語句
             * Hibernate會根據返回的結果去作出判斷,若是返回的結果是0
             * 即表示不存在這條記錄,Hiberante一樣會拋出異常
             */
            session.update(cat);
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            if(session != null){
                session.getTransaction().rollback();
            }
        }finally{
            if(session != null){
                session.close();
            }
        }
    }

org.hibernate.StaleStateException:Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1   

底層確定是經過使用Jdbc中的executeUpdate()方法來獲得影響數據庫中的記錄數,此時爲0,Hibernate也會給咱們拋出異常。

3.在session中存在兩個相同標識的對象

    @Test
    public void update3(){
        Session session = null;
        try {
            session = HibernateUtil.openSession();
            session.beginTransaction();
            DomesticCat cat = new DomesticCat();
            cat.setName("haha");
            cat.setSex('M');
            cat.setColor("Black");
            //此時會產生一條sql語句
            session.saveOrUpdate(cat);
            Long id = cat.getId();
            //建立一個與session中具備相同id的對象
            DomesticCat cat2 = new DomesticCat();
            cat2.setId(id);
            /*
             * 執行時session會去檢測該對象的id,
             * 當發現該對象的id與session中的對象的id值相同,
             * 此時Hiberante也會拋出異常
             */
            session.update(cat2);
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            if(session != null){
                session.getTransaction().rollback();
            }
        }finally{
            if(session != null){
                session.close();
            }
        }
    }
    

可是若是Session中已經一個持久化對象,若是此時再次建立一個新的對象,調用update方法的時候就會拋出異常,Hibernate不容許在session中存在兩個具備相同id的同類型的對象。

所以對於saveOrUpdate判斷對象狀態的本質就是看對象的oid是否存在。若是存在,就調用update方法,若是不存在就調用save方法。

Hibernate中還爲咱們提供了一個merge方法:

    @Test
    public void merge1(){
        Session session = null;
        try {
            session = HibernateUtil.openSession();
            session.beginTransaction();
            DomesticCat cat = new DomesticCat();
            cat.setName("haha");
            cat.setSex('M');
            cat.setColor("Black");
            //此時會產生一條sql語句
            session.save(cat);
            Long id = cat.getId();
            //建立一個與session中具備相同id的對象
            DomesticCat cat2 = new DomesticCat();
            cat2.setId(id);
            cat2.setName("mimi");
            /*
             * 此時會將cat2的狀態拷貝到cat對象中
             */
            session.merge(cat2);
            //此時cat的name已經變成了mimi,再也不是haha
            System.out.println(cat.getName());
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            if(session != null){
                session.getTransaction().rollback();
            }
        }finally{
            if(session != null){
                session.close();
            }
        }
    }

session調用merge方法首先會去判斷session中是否存在與給定對象相同id的對象,若是存在,就將給定對象的狀態拷貝至與給定對象相同id的持久化對象,可是給定對象此時依舊是遊離狀態,它的狀態不會發生任何的改變。若是session中不存在與給定對象就有相同id值的對象,那麼Hibernate會去數據庫中加載或者從新建立出一個新的對象。

Persistent ---->Detached

當session被關閉或者session的一級緩存被清空時(即調用clear()方法),那麼對象狀態就會從persist轉變成detached。注意的是:session.flush()方法只是將持久化對象的狀態與數據庫中的數據同步而已,所以會發送sql語句,可是session中依舊會保存中數據,不會被清空,所以不會去改變持久化對象的狀態。

Persistent ---->Transitent

當調用session.delete方法以後,一個持久化對象就會變成瞬時對象,此時就會產生1條delete的sql語句,而且對象也不會被session所管理。

相關文章
相關標籤/搜索