天天看點

Hibernate 知識收納.

 一、樂觀鎖和悲觀鎖

悲觀鎖(Pessimistic Lock), 顧名思義,就是很悲觀,每次去拿資料的時候都認為别人會修改,是以每次在拿資料的時候都會上鎖,這樣别人想拿這個資料就會block直到它拿到鎖。傳統的關系型資料庫裡邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。

樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿資料的時候都認為别人不會修改,是以不會上鎖,但是在更新的時候會判斷一下在此期間别人有沒有去更新這個資料,可以使用版本号等機制。樂觀鎖适用于多讀的應用類型,這樣可以提高吞吐量,像資料庫如果提供類似于write_condition機制的其實都是提供的樂觀鎖。

兩種鎖各有優缺點,不可認為一種好于另一種,像樂觀鎖适用于寫比較少的情況下,即沖突真的很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。但如果經常産生沖突,上層應用會不斷的進行retry,這樣反倒是降低了性能,是以這種情況下用悲觀鎖就比較合适。

悲觀鎖:

String hqlStr = "from TUser as user where user.name='Erica'";
Query query = session.createQuery(hqlStr);
query.setLockMode("user",LockMode.UPGRADE); //加鎖
List userList = query.list();//執行查詢,擷取資料      

Hibernate的加鎖模式有:

Ø LockMode.NONE : 無鎖機制。

Ø LockMode.WRITE :Hibernate在 Insert 和 Update 記錄的時候會自動擷取。

Ø LockMode.READ : Hibernate在讀取記錄的時候會自動擷取。

       ---以上這三種鎖機制一般由 Hibernate 内部使用,如 Hibernate 為了保證 Update 過程中對象不會被外界修改,會在 save 方法實作中自動為目标對象加上 WRITE 鎖。

Ø LockMode.UPGRADE :利用資料庫的 for update 子句加鎖。

Ø LockMode. UPGRADE_NOWAIT :Oracle 的特定實作,利用 Oracle 的for update nowait 子句實作加鎖。

              -- 上面這兩種鎖機制是我們在應用層較為常用的,加鎖一般通過以下方法實作:

Criteria.setLockMode
Query.setLockMode
Session.lock       

二、多态

第一種方案:一個子類對應一張表。

實作方式:在父類的配置檔案中配置子類的實作方式,當然,也可以在子類中單獨配置:

<!-- 可單獨寫在Student.hbm.xml裡 -->
         <union-subclassname="com.jomoo.entity.Student"table="student"extends="com.jomoo.entity.People">
                   <propertyname="studentNumber"column="studentNumber"type="string"></property>
         </union-subclass>
         <!-- 可單獨寫在Teacher.hbm.xml裡 -->
         <union-subclass name="com.jomoo.entity.Teacher" table="teacher" extends="com.jomoo.entity.People">
                   <property name="salary" column="salary" type="string"></property>
         </union-subclass>      

第二種方案:使用一張表表示所有繼承體系下的類的屬性的并集。

這種政策是使用<subclass>标簽來實作的。因為類繼承體系下會有許多個子類,要把多個類的資訊存放在一張表中,必須有某種機制來區分哪些記錄是屬于哪個類的。Hibernate中的這種機制就是,在表中添加一個字段,用這個字段的值來進行區分。在表中添加這個标示列使用<discriminator>标簽來實作。

第三種方案:每個子類使用一張表隻存儲它特有的屬性,然後與父類所對應的表以一對一主鍵關聯的方式關聯起來。

每個子類使用一張表隻存儲它特有的屬性,然後與父類所對應的表以一對一主鍵關聯的方式關聯起來。這種政策是使用<joined-subclass>标簽來定義子類的。父類、子類都對應一張資料庫表。在父類對應的資料庫表中,它存儲了所有記錄的公共資訊,實際上該父類對應的表會包含所有的記錄,包括父類和子類的記錄;在子類對應的資料庫表中,這個表隻定義了子類中所特有的屬性映射的字段。子類對應的資料表與父類對應的資料表,通過一對一主鍵關聯的方式關聯起來。

三、緩存

緩存是介于應用程式和實體資料源之間,其作用是為了降低應用程式對實體資料源通路的頻次,進而提高了應用的運作性能。

緩存的媒體一般是記憶體,是以讀寫速度很快。但如果緩存中存放的資料量非常大時,也會用硬碟作為緩存媒體。

Hibernate 的緩存包括 Session 的緩存和 SessionFactory 的緩存,其中 SessionFactory 的緩存又可以分為兩類:内置緩存和外置緩存。Session 的緩存是内置的,不能被解除安裝,也被稱為 Hibernate 的第一級緩存。SessionFactory 的内置緩存和 Session 的緩存在實作方式上比較相似,前者是 SessionFactory 對象的一些集合屬性包含的資料,後者是指 Session 的一些集合屬性包含的資料。SessionFactory 的内置緩存中存放了映射中繼資料和預定義SQL語句,映射中繼資料是映射檔案中資料的拷貝,而預定義SQL語句是在 Hibernate 初始化階段根據映射中繼資料推導出來,SessionFactory 的内置緩存是隻讀的,應用程式不能修改緩存中的映射中繼資料和預定義SQL語句,是以 SessionFactory 不需要進行内置緩存與映射檔案的同步。SessionFactory 的外置緩存是一個可配置的插件。在預設情況下,SessionFactory 不會啟用這個插件。外置緩存的資料是資料庫資料的拷貝,外置緩存的媒體可以是記憶體或者硬碟。SessionFactory 的外置緩存也被稱為Hibernate的第二級緩存。

一級緩存和二級緩存的差別:

Hibernate的一級緩存依賴于 session 的生命周期,無法解除安裝,隻會被單個 session 所持有,一旦 session 的生命周期結束,一級緩存也就消失了,是以,一級緩存不會發生并發。

Hibernate的二級緩存是一個可配置的插件,為所有 session 所共同通路,是以他不适用于高并發的資料。由于二級緩存要保證資料的一緻性,即資料源和緩存中資料的一緻性,當頻繁修改資料時,就需要不斷通路資料庫以保證一緻性。這樣緩存變得沒有意義了。

Hibernate 知識收納.
Hibernate 知識收納.

四、狀态

hibernate的三種狀态

1、 瞬時态:剛 new 出來的一個對象,隻是在記憶體中占據了一個空間,可被垃圾回收器回收,沒有被儲存到資料庫,也沒有與session相關聯。

2、 持久态:對象被儲存到資料庫中,且緩存在 session 對象中,是一個持久的狀态。

3、 脫離态:session 關閉了,原來 session 的緩存脫離了 session 的控制,但是又有一個資料庫的記錄與之相對應。但需要重新對這條記錄改變時,脫離狀态被重新回到持久狀态。

五、其他

1、什麼是SessionFactory?什麼是Session?http session 和 hibernate session 有什麼差別?

    SessionFactory 接口負責初始化 Hibernate。它充當資料存儲源的代理,并負責建立 Session 對象。這裡用到了工廠模式。需要注意的是 SessionFactory 并不是輕量級的,因為一般情況下,一個項目通常隻需要一個 SessionFactory 就夠,當需要操作多個資料庫時,可以為每個資料庫指定一個 SessionFactory 。

   在 hibernate 中的 session 并不是 http 中所說的 session,一般把 HttpSession 對象稱為使用者會話。

   而 hibernate 中的 Session 呢?是用來表示,應用程式和資料庫的一次互動(會話)。在這個Session中,包含了一般的持久化方法(CRUD)。而且,Session 是一個輕量級對象(線程不安全),通常将每個 Session 執行個體和一個資料庫事務綁定,也就是每執行一個資料庫事務,都應該先建立一個新的 Session 執行個體,在使用 Session 後,還需要關閉 Session。

2、n+1問題?

一般而言說 n+1 意思是,無論在一對多還是多對一當查詢出 n 條資料之後,每條資料會關聯的查詢1次他的關聯對象,這就叫做 n+1。

下面是幾種解決方案:

  • 設定 @ManyToOne 的fetch屬性值為 fetchType.LAZY,這種方式解決後,後面的 n 條 sql 語句按需而發。但是有個弊端,就是如果需要級聯查詢就無法擷取級聯對象了。
  • 設定 @BatchSize(size=5)(該注解要加在類上面,跟@Entity在同一位置),這樣發出的sql語句減少。這個設定在一定程度上提高了效率。
  •  join fetch , 如使用"from Student s left join fetch s.group g", 進行表連接配接查詢,此時就發1條SQL語句。
  •  使用QBC查詢,預設效果與3相同:

              1、fetch="select" 會另外發出一條語句查詢集合。

              2、設定 fetch="join" 采用外連接配接集合的 lazy 失效。

              3、這隻 fetch="subselect" 另外發出一條 select 語句抓取前面查詢到的所有的實體對象的關聯集合 fetch隻對 HQL 查詢産生影響其他的則不會。

3、Hibernate中的 get 和 load 加載有什麼差別?

當我們使用 session.load() 方法來加載一個對象時,此時并不會發出sql語句,目前得到的這個對象其實是一個代理對象,這個代理對象隻儲存了實體對象的 id 值,隻有當我們要使用這個對象,得到其它屬性時,這個時候才會發出 sql 語句,從資料庫中去查詢我們的對象。

相對于load的延遲加載方式,get 就直接的多,當我們使用session.get()方法來得到一個對象時,不管我們 使不使用這個對象,此時都會發出sql語句去從資料庫中查詢出來。

4、髒讀和幻讀?

髒讀:是指當一個事務正在通路資料,且對資料做了修改,但是還沒有送出到資料庫。另一個事務查詢到這個資料、這個資料就是髒資料,依靠這個資料所做的操作就是不正确的、

幻讀:比如第一個事務對資料庫的全部行進行了修改,同時第二個事務插入一條資料到資料庫中,這時第一個事務就會發現,資料庫中有一條記錄沒有被修改,像發生了幻覺一樣、