序言sql
這算是hibernate的最後一篇文章了,下一系列會講解Struts2的東西,而後說完Struts2,在到Spring,而後在寫一個SSH如何整合的案例。以後就會在去講SSM,在以後我本身的我的博客應該也差很少能夠作出來了。基本上先這樣定下來,開始完成hibernate的東西把。這章結束後,我會將我一些hibernate的資料奉上,供你們一塊兒學習。數據庫
---WZY緩存
1、概述安全
這章總的分兩大塊來說解,session
第一大塊,hibernate的事務管理。,對於hibernate的事務管理來講,若是以前學過數據庫的事務管理,那麼在這裏就順風順水了。若是沒學過,第一次遇到,那也不要緊,我會詳細解釋其中的內容。併發
第二大塊,hibernate的二級緩存機制。這個看起來好高大上啊,若是歷來沒了解過二級緩存的人確定以爲他很難,可是學過會發現,真的是so easy。最起碼會知道什麼是二級緩存,做用是什麼。ide
2、hibernate的事務管理學習
2.一、回顧數據庫的事務的特性和隔離級別測試
2.1.一、什麼是事務?atom
事務是一組業務邏輯,好比A去銀行給B轉錢,A轉了100塊給B(update語句更新A的錢減小一百),B收到錢(update語句更新B的錢增長一百),這轉錢整個過程稱爲事務,注意,不要以爲A轉錢給B,A作完了操做,就是事務了,要記住事務是一組業務邏輯,兩個在一塊兒纔是事務。在好比有一個數字5,如今有一個事務A,事務A作的事情就是將5變爲4,該事務要作的事情有,拿到5,而後5-1,而後數據庫中的5變爲了4,這纔算這個事務A真正的成功了,能夠那麼說,業務邏輯太抽象了,事務就是一組操做,只有整個操做在一塊兒纔算是一個事務。就向上面說的,轉錢也是一個事務,它作的操做就只有兩個,A減小錢,B增長錢。
2.1.二、事務的特性ACID
A(atomicity):原子性:事務不可被劃分,是一個總體,要麼一塊兒成功,要麼一塊兒失敗
C(consistence):一致性,A轉100給B,A減小了100,那麼B就要增長100,增長減小100就是一致的意思
I(isolation):隔離性,多個事務對同一內容的併發操做。
D(durability):持久性,已經提交的事務,就已經保存到數據庫中,不能在改變了。
2.1.三、事務隔離性產生的問題
跟線程安全差很少,多個事務對同一內容同時進行操做,那麼就會出現一系列的併發問題。必定要注意,這個是兩個或者多個事務同時對一個內容進行操做,是同時,而不是先事務A操做完,而後事務B在操做,這個要理解清楚。
2.1.3.一、髒讀:一個事務 讀到 另外一個事務 沒有提交的數據。
A給B轉100塊錢(事務A),A在ATM機上轉,將100塊錢放到ATM機裏了,而後ATM機會最後詢問A,肯定轉帳100給B嗎,A還沒點肯定這個時候,B在另一個ATM機上就發現帳戶上多了100塊錢,而後高興的取走了這100塊錢,B取錢(事務B)可是此時A以爲不行,以爲仍是拿現金給B比較好,而後就點了取消,把放到ATM機中的100塊錢給拿了回來。這其中,A轉錢給B(事務A)、B取錢(事務B),也就發生了事務B讀到了事務A沒有提交的數據,也就是髒讀。注意:要明白事務是什麼,才能理解這些東西。還有,剛開始接觸可能會以爲這是不可能發生的呀,怎麼會讀到尚未提交的數據呢?這裏只是說明這個是一個問題,現實生活中確定沒有呀,銀行也不會出現這種問題,由於已經所有解決掉了,可是這種問題確定是存在的,該如何解決呢?這就是咱們後面須要討論的東西。如今只須要知道事務隔離會產生這種問題就好了。
2.1.3.二、不可重複讀:一個事務 讀到 另外一個事務 已經提交的數據(update更新語句)
解釋:有時候不可重複讀是一個問題,有時候卻不是。這要看業務需求是怎麼樣的,就比如銀行轉錢的事情,事務A(A給B轉錢),事務B(B取錢),A在ATM機上插卡轉錢給B,同時B也將銀行卡插入ATM機中準備查看A是否轉了錢給B,當A事務結束後,也就是A轉帳成功後,B就查到了本身帳戶上多了100塊錢,這就是事務B讀到了事務A已經提交的數據。這個例子不可重複讀就不是個問題。此業務邏輯中,就不須要解決這個問題。在別的業務中,可能這個就是個問題了。好比,一個公司,每月15號給員工結算工資,工資數是從上個月15號到這個月15號根據每一個人提交的工做量來結算的,可是會計師A在這個月15號從數據庫中拿每一個員工的工做記錄量的數據作工資的統計的同時,員工B在次提交了一次工做量(將數據庫中B的工做量增長了),此時會計師A在結算員工B的工資時,就會把前面30天的工做量和他今天提交的工做量一塊兒算工資,到了下個月15號,又會把員工B此次提交的工做量算在它當前月的總工做量中,這樣一來,就至關於給B多算了一次工做量的工資。這樣就不合理了。
2.1.3.4、虛度(幻讀):一個事務 讀到 另外一個事務 已經提交的數據(insert插入語句)
這個跟不可重複讀描述的問題是同樣的,可是其針對的事務不同,不可重複讀針對的是作更新的事務,好比轉錢呀等,都是作更新的事務,而這個虛讀針對的是作插入的事務,好比,在工地有不少人作事,工地有規定,作了事的中午才包飯,到了中午的時候,工地負責人要去給作事的工人買盒飯,就要統計人數,如今工地也會用電腦了,負責人就到電腦上查有多少人作事,來決定買多少盒飯,就在查人數的時候,另外一個專門招工人的負責人招到一個工人,就把該工人的信息輸入到數據庫裏面,而後買盒飯的負責人在電腦上一查數據庫,發現有N我的,剛招進來的工人也在其中,而後就也給那個沒作事的,剛招進來的員工買了盒飯,這是不符合規定的。這只是舉一個這樣的例子,幫助你們理解,一個盒飯也不貴,以爲無所謂,可是若是是涉及很重要的東西時,就不能出現這種問題。
2.1.四、事務隔離級別,用於解決隔離問題
2.1.4.一、read uncommitted :讀未提交,一個事務 讀到 另外一個事務 沒有提交的數據,存在問題3個,解決0個問題
2.1.4.二、read committed:讀已提交,一個事務 讀到 另外一個事務 已經提交的數據,存在問題2個,解決1個問題(髒讀問題)
2.1.4.三、repeatable read:可重複讀,一個事務 讀到重複的數據,即便另外一個事務已經提交。存在問題1個,解決2個問題(髒讀、不可重複讀)
2.1.4.四、serializable:單事務,同時只有一個事務能夠操做,另外一個事務掛起(暫停),存在問題0個,解決3個問題(髒讀、不可重複讀、虛讀)
注意:必定要搞清楚上面三個問題(髒讀、不可重複讀、幻讀)是什麼樣的狀況,你才能知道這四種隔離級別爲何可以解決這些問題。切記,若是看個人話仍是以爲這幾個問題模糊不清,就請留言告訴我你的疑問,由於若是不明白這三個問題,那麼後面你將一直會混淆。
2.1.五、使用MySQL來進行隔離級別的演示
這裏就不寫代碼了,直接上文字,讓大家熟悉一下事務隔離級別的使用。
順便說一句,MySQL默認的隔離級別:repeatable read Oracle默認的隔離級別:read committed
MySQL默認事務提交的,也就是在cmd中每執行一條sql語句就是一個事務,因此若是你要進行實驗就必須先關閉MySQL的自動事務提交,並改成手動,set autocommit=0
2.1.5.一、read uncommitted
A隔離級別:讀未提交,會發生髒讀問題
AB同時開始事務,
A先查詢 --正常數據
B更新,但未提交
A在查詢 --讀到B沒有提交的數據
B回滾 --B沒有提交數據,回滾的話,就至關於剛纔的更新語句並無執行
A再查詢 --讀到回滾後的數據,也就是原來的正常數據。
2.1.5.二、read committed
A隔離級別:讀已提交
AB同時開啓事務
A 先查詢 --正常
B 更新,但未提交
A再查詢 --獲得的仍是以前的數據,並無拿到B沒有提交的數據, 解決問題:髒讀
B 提交
A 再查詢 -- 已經提交的數據。問題:不可重複讀(到這裏就不要在糾結爲何不可重複讀是個問題了,上面已經解釋清楚了,根據不一樣的業務,多是問題,也可能不是)
2.1.5.三、repeatable read
A隔離界別:可重複讀,保證當前事務中讀到的是重複的數據
AB 同時開啓事務
A 先查詢 --正常
B 更新,但未提交
A 再查詢 -- 以前數據,解決:髒讀
B 提交
A 再查詢 -- 以前數據,解決:不可重複讀
A 回滾|提交
A 再查詢 -- 更新後數據,新事務得到最新數據
2.1.5.三、serializable
A隔離級別:串行化,單事務
AB 同時開啓事務
A 先查詢 --正常
B 更新 -- 等待 (對方事務A結束或者超時B才能進行。)
2.1.六、丟失更新問題 lost update
這個丟失更新問題也是屬於事物隔離性產生的問題之一,可是不一樣上面所說的三個,上面所說的髒讀、不可重複讀、虛讀,都是一個事務 拿到了 另外一個事務所提交或者未提交的數據而產生的問題,而丟失更新並不拿對方事務所提交的數據,那丟失更新描述的是一個什麼樣的問題呢?
A 查詢數據,username = 'jack' ,password = '1234'
B 查詢數據,username="jack", password="1234'
A更新密碼,用戶名不變 username='jack',password='456' //A將密碼更新完後,將其保存到數據庫中了
B更新用戶名,username='rose',password='1234' //B更新以後,數據庫中的數據就爲 username='rose',password='1234'
丟失更新:最後更新數據,將前面更新的數據給覆蓋了。那A以後就發現本身剛設置的密碼登陸不上了,這就出現了丟失更新問題,解決的方法有兩種
解決方法一:
樂觀鎖
認爲丟失更新必定不會發生,很是樂觀,在數據庫表中添加一個字段,能夠說是標識字段把,用於記錄操做次數的,好比若是對有人拿到了該行記錄作了更新操做,該字段就加1。而後下一個拿到該記錄的人要先將拿到的記錄的標識和數據庫中該記錄的標識作對比,若是同樣,則能夠修改,而且修改後標識(版本)+1,若是不同,先從數據庫中查詢,而後在作更新。舉個例子
A 查詢數據,username = 'jack' ,password = '1234',version=1
B 查詢數據,username="jack", password="1234',version=1 //AB同時拿到數據庫中數據,且version讀爲1
A更新密碼,用戶名不變 username='jack',password='456',version=2 //先和數據庫中該行記錄的version作對比,拿到的version是1,跟數據庫中同樣,因此能作更新,A將密碼更新,version+1,而後將其保存到數據庫中(注意,這裏寫的是A更新以後的的數據。 不要搞混了。)
B更新用戶名,username='rose',password='1234',version=1 //B想要更新時,先和數據庫中該條記錄的版本號作對比,發現不同,而後查詢
B從新查詢數據, 用戶名不變 username='jack',password='456',version=2 //而後在進行對比,此次version同樣了,B就能夠實現更新操做了。
B更新用戶名,username='rose',password='456',version=3 //更新後,version+1
解決方法二:
悲觀鎖
認爲丟失更新必定會發生,此時採用數據庫鎖機制,也就是至關於誰操做了該記錄行,就會在上面加把鎖,別人進不去,只有等你操做完以後,該鎖就釋放,別人就能夠操做了。跟那個隔離級別單事務差很少。可是鎖也分不少種。
讀鎖:共享鎖,你們能夠一塊兒讀數據,可是不能一塊兒操做(更新,刪除,插入等)
寫鎖:排他鎖,只能一個進行寫,也就是上面咱們說的原理。
2.二、hibernate中對事務產生的隔離性問題以及解決方案
上面經過很大的篇幅講解數據庫的事務相關問題,就是爲了講解hibernate中的事務作鋪墊,懂了上面這些,那麼這裏就順風順水了。
2.2.一、hibernate中設置事務隔離級別,隔離級別是爲了解決事務隔離性產生的問題的。
在hiberante.cfg.xml文件中配置 hibernate.connection.isolation 隔離級別
有四種隔離級別可選擇,後面的數字表示在設置隔離級別的時候,直接寫數字也是能夠表明對應的隔離級別的,好比 hibernate.connection.isolation 4 跟hibernate.connection.isolation Repeatable read 是同樣的。
Read uncommoitted isolation 1
Read committed isolation 2
Repeatable read isolation 4
Serializable isolation 8
2.2.二、hibernate中丟失更新問題的解決
悲觀鎖: 就是認爲必定會發生丟失更新問題,採起鎖機制
User user = (User) session.load(User.class,1,LockMode.UPGRADE);
樂觀鎖:
hibernate 爲Customer表 添加版本字段
1) 在User類 添加 private Integer version; 版本字段
2) 在User.hbm.xml 定義版本字段
<!-- 定義版本字段 -->
<!-- name是屬性名 -->
<version name="version"></version>
若是產生了丟失更新就會報異常
總結:
一、若是知道了數據庫中事務的知識,那麼在hibernate中就很是簡單,只是簡單的配置一下就OK了。因此在hibernate的事務講解這裏篇幅就比較少,重要的仍是須要弄懂前面的知識。很重要。
3、hibernate的二級緩存
說點廢話,二級緩存理解起來真的很是很是簡單,你們不要以爲怕,就三個內容,知道什麼是二級緩存,如何使用它,就沒了。
3.一、什麼是二級緩存
咱們知道一級緩存,而且一級緩存的做用範圍就在session中,每一個session都有一個本身的一級緩存,而二級緩存也就是比一級緩存的做用範圍更廣,存儲的內容更多,咱們知道session是由sesssionFactory建立出來的,一個sessionFactory可以建立不少個session,每一個session有本身的緩存,稱爲一級緩存,而sessionFactory也有本身的緩存,存放的內容供全部session共享,也就是二級緩存。 是否是很簡單?還不理解看下面我畫的一張圖就一目瞭然了。
一級緩存:保存session中,事務範圍的緩存(通俗點講,就是session關閉後,該緩存就沒了,其緩存只能在session的事務開啓和結束之間使用)
二級緩存,保存在SessionFactory,進程範圍內的緩存(進程包括了多個線程,也就是咱們上面說的意思,A線程可能拿到一個session進行操做,B線程也可能拿到一個session進行操做,可是A和B讀能訪問到SessionFactory中的緩存,也就是二級緩存,這裏只是拿A,B說事,可能有一個線程剛建立出來session,也能拿到二級緩存中的數據)
3.二、二級緩存的做用?優勢
這個可能要到實際開發工做中才會知道二級緩存有哪些用處,如今給出一些我認爲比較好的答案,由於我還沒真正到去工做,因此目前也只是理解其內容。
。。。。
3.三、二級緩存的內部結構
類緩存區域
好比:session.get(Customer.class,1); //這就是類緩存區域
集合緩存區域
customer.getOrders(); //就是存放Orders集合的內容的緩存區域就叫集合緩存區域
更新時間戳區域
查詢緩存區域
這幾個在後面會詳細講到,如今不講,想直接看的就跳過看後面測試實例。
3.四、二級緩存的併發訪問策略配置。
爲何要講解這個呢?想一下,二級緩存是sessionFactory中的,其sessionFactory建立出來的session均可以共享它,因此其中就會出現併發問題,也就是咱們一開始講的一些事務隔離性問題。爲了解決這些問題,hibernate也提供了相應的方法,就是二級緩存的併發訪問策略,總共有四種,經過一張表格來看把,經過下面這張圖,咱們應該就知道了其實跟開始講的差很少,換了一個名詞而已。而且若是要使用二級緩存,就必須配置這個病房訪問策略,否則是用不了二級緩存的,就比如你已經有了二級緩存,其中也有內容,可是你沒有設置訪問方式,就訪問不出來。
在配置二級緩存以後,就要相應的配上其二級緩存併發訪問策略。
3.五、如何配置二級緩存?
分兩大步,第一步單純配置二級緩存,第二步配置二級緩存的併發訪問策略。第三步,配置ehcache.xml
3.5.一、配置二級緩存
要使用二級緩存,須要導入其它的組件來幫咱們完成,而hibernate自己是有一個默認的二級緩存組件,可是日常咱們不用,通常是用EhCacheProvider,來了解一下hibernate所能支持的二級緩存組件提供商
一、咱們使用Ehcache,因此導入其jar包
二、在hibernate.cfg.xml文件中配置,開啓二級緩存,
三、在hibernate.cfg.xml文件中配置二級緩存提供商
開啓二級緩存就只須要三步,就上面三步,而後開始配置咱們的併發訪問策略。
3.5.二、配置二級緩存的併發訪問策略
二級緩存組件所能支持的併發訪問策略。 爲何
今天咱們要用的就是EHCache,它不支持Transactional這種併發訪問策略,因此咱們使用的是read-write這種併發訪問策略,read-write提供的就是read committed事務隔離級別,可以防止髒讀,具體的功能就看上面的表格。
配置方案有兩種
一、xxx.hbm.xml文件中配置
能夠在<class>標籤下配置<cache usage=」read-write」> 類級別
能夠在<set>標籤下配置<cache usage=」read-write」> 集合級別
二、hibernate.cfg.xml文件中配置
3.5.三、配置ehcache.xml文件
將 ehcache-1.5.0 jar包中 ehcache-failsafe.xml 更名 ehcache.xml 放入 src。 這很是簡單。若是使用的別的組件緩存,則在相同位置也有相似的文件。
3.六、測試二級緩存的存在
3.6.一、搭建測試環境
而且將上面的步驟本身搭建好,三步走,注意:使用的業務邏輯是Dept-Staff,也就是部門和職工的關係。
一、配置二級緩存,也就是將一些組件緩存提供商弄好
二、配置併發訪問策略,這裏其實就自由配置了,若是你想在二級緩存中只存取某個類的值,那麼就在xxx.hbm.xml中配置一下其訪問策略
這裏,我先只對dept進行read-write的訪問策略,而後等會進行測試
三、添加ehcache.xml
3.6.二、測試二級緩存是否存在
3.6.三、注意get/load能夠從二級緩存中獲取數據,而query的list不能從二級緩存獲取數據,可是其查詢結果會存入二級緩存。
小結:
hql作的查詢可以存入一級緩存和二級緩存,可是不可以從二級緩存中拿數據
get\load可以將其查詢數據插入一級緩存和二級緩存,也可以從一級二級緩存中拿數據。
3.6.四、一級緩存數據會同步二級緩存
注意:若是是用hql來更新修改name,那麼就不會同步二級緩存,連一級緩存中的數據也不會改變,hql是直接針對數據庫來進行修改的,而set()方法是經過hibernate中的快照區,而不是直接對數據庫進行操做。
3.6.五、測試二級緩存中的四種緩存區域
3.6.5.一、類緩存區域
類緩存區域,經過id查詢到的對象,就將其放入類緩存區域,其區域中緩存的都是PO對象,而不是單獨的一些屬性,值,而是完整的對象,好比上面測試是否有二級緩存,就是將id爲2的dept對象存入二級緩存中,而不是就存dept的name,或者是id,若是隻存dept的name,或者別的字段屬性,那麼就會放入查詢緩存中,而不是放入類緩存區域。
3.6.5.二、集合緩存區域
記得之前說的集合級聯關係嗎,其實這個也同樣,好比這個Dept-Staff的例子中,dept.getStaffSet(); 這查詢出來的就將其放入集合緩存區域,其區域內也所有是PO對象。
設置二級緩存的訪問策略,因爲要測試集合緩存,那麼其set中就要設置訪問策略,而且注意一點,集合級別的緩存依賴於類級別的緩存,也就是說,set中設置cache還不夠,由於要緩存staff,因此必須將staff.hbm.xml也設置訪問策略。同時咱們只是測試集合緩存,那麼對查詢出來的Dept能夠不設置訪問策略了,有沒有他讀不要緊,由於咱們不須要用。也就是下圖中第一個用紅框框起來的,
staff.hbm.xml
測試從dept獲取到的staff是否可以存入二級緩存中。
3.6.5.三、時間戳緩存區域
存放了對於查詢結果相關的表進行插入,更新,刪除操做的時間戳,Hibernate經過時間戳緩存區域來判斷被緩存的查詢結果是否過時,若是過時了則從數據庫中拿數據,沒過時則直接從緩存中拿數據。通俗點講,就三步
一、查詢結果放到二級緩存中,此時記錄一個時間爲T1
二、當有操做直接更改了數據庫的數據時,好比使用hql語句,就會直接對數據庫進行修改,而不會改變緩存中的數據。此時記錄時間爲T2
三、當下次在查詢記錄時,會先將T1和T2進行比較,若是T2>T1,則說明緩存中的數據不是最新的,那麼就從數據庫中拿出正確的數據,若是T2<T1,就說明沒有對數據庫進行過什麼修改操做,那麼就能夠直接從緩存中獲取數據。
解惑:若是沒有T1和T2的比較,那麼會出現咱們查詢到的數據不是準確的,由於就像上面第二步所說的,數據庫的數據會和緩存中的數據不同,什麼讀不作就從緩存中拿數據,就會出現錯誤。
測試:
3.6.5.四、查詢緩存區域
這個在將類緩存區域差很少已經解釋過了,就是經過查詢的結果不是一個po對象,而是一些零散的字段屬性,那麼就存入該區域,可是使用它是須要兩個東西
一、在hibernate.cfg.xml文件中配置
二、在使用query操做時,須要指定是否從查詢緩存中獲取數據
測試:
以上寫的訪問策略全是使用的xxx.hbm.xml來配置,如今我將上面例子的使用hibernate.cfg.xml來配置訪問策略的代碼寫一下。
上圖說了兩個緩存區域,時間戳緩存區域不用設置,而查詢緩存區域就經過別的方式去設置了。 這就是咱們所要講解的四個緩存區域。很簡單把。
4、總結
這篇文章本該早寫完的,可是中途由於一些事情而耽誤了,今天上午終於將它完結了。hiberante我以爲差很少就這些東西了。 但願對你們有所幫助