欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Hibernate的Session_flush與隔離級別代碼詳解

 更新時間:2024年06月13日 15:33:18   作者:lavimer  
這篇文章主要介紹了Hibernate的Session_flush與隔離級別代碼詳解,分享了相關(guān)代碼示例,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下

本文研究的主要是Hibernate的Session_flush與隔離級別,具體介紹和實(shí)例如下。

概念介紹

我們先來看一些概念:

1.臟讀:臟讀又稱為無效數(shù)據(jù)的讀出,是指在數(shù)據(jù)庫訪問中,事物T1將某一值修改,然后事物T2讀取該值,此后T1因?yàn)槟撤N原因撤銷對該值的修改,這就導(dǎo)致了T2所讀取的數(shù)據(jù)是無效的。臟讀就是指當(dāng)一個事物正在訪問數(shù)據(jù),并且對數(shù)據(jù)進(jìn)行了修改,而這種修改還沒有提交到數(shù)據(jù)庫中,這時,另外一個事物也訪問這個數(shù)據(jù),然后使用了這個數(shù)據(jù)。因?yàn)檫@個數(shù)據(jù)還是沒有提交的數(shù)據(jù),那么另外一個事物讀到的這個數(shù)據(jù)就是臟數(shù)據(jù),依據(jù)臟數(shù)據(jù)所做的操作是不正確的。

2.不可重復(fù)讀:比如我在讀一個帖子,我查出來的數(shù)據(jù)是張三、李四,然后我一刷新發(fā)現(xiàn)最開始的張三變成了張八,這就是所謂的不可重復(fù)讀,因?yàn)槲易x出的數(shù)據(jù)沒重復(fù)了嘛。

3.幻讀:我在查數(shù)據(jù)的時候,開始查出來的記錄為3條,我一刷新,發(fā)現(xiàn)記錄變?yōu)榱?條,這就是幻讀。

4.提交讀:提交了之后才可以讀取,Oracle默認(rèn)就是這個,這種方式是不存在臟讀的。

5.可重復(fù)度:很顯然是和不可重復(fù)讀相反的,它可以避免不可重復(fù)讀,但是這個不能避免幻讀。

6.序列化:這種方式非常嚴(yán)格,通俗的說就是,當(dāng)我在做一件事情的時候,其他任何人都不能做,非常安全,但是效率極低。

隔離級別

下面我們通過實(shí)際的例子來體會Hibernate清除緩存的應(yīng)用。

Hibernate映射數(shù)據(jù)庫和主鍵的生成策略有關(guān)。

案例一

UUID的方式生成主鍵的例子:

public class User {
	private String uid;
	private String uname;
	private Date birthday;
	public String getUid() {
		return uid;
	}
	public void setUid(String uid) {
		this.uid = uid;
	}
	public String getUname() {
		return uname;
	}
	public void setUname(String uname) {
		this.uname = uname;
	}
	public Date getBirthday() {
		return birthday;
	}
	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
}

User.hbm.xml:

<?xml version="1.0"?> 
<!DOCTYPE hibernate-mapping PUBLIC 
  "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 
<!-- package表示實(shí)體類的包名 -->   
<hibernate-mapping package="com.lixue.bean"> 
 <!-- class結(jié)點(diǎn)的name表示實(shí)體的類名,table表示實(shí)體映射到數(shù)據(jù)庫中table的名稱 --> 
 <class name="User" table="t_user"> 
  <id name="uid"> 
   <!-- 通過UUID的方式生成 --> 
   <generator class="uuid"/> 
  </id> 
  <property name="uname"/> 
  <property name="birthday"/> 
 </class> 
</hibernate-mapping> 

測試方法:

/** 
  * 測試uuid主鍵生成策略 
  */ 
 public void testSave1(){ 
  /*定義的Session和事物*/ 
  Session session = null; 
  Transaction transaction = null; 
   
  try { 
   /*獲取session和事物*/ 
   session = HibernateUtils.getSession(); 
   transaction = session.beginTransaction(); 
    
   /*創(chuàng)建用戶*/ 
   User user = new User(); 
   user.setUname("喜羊羊"); 
   user.setBirthday(new Date()); 
    
   /** 
    * 因?yàn)閁ser的主鍵生成策略為uuid,所以調(diào)用完save之后,只是將User納入到Session管理 
    * 不會發(fā)出insert語句,但是ID已經(jīng)生成,PersistenceContext中的existsInDatebase狀態(tài)為false 
    */ 
   session.save(user); 
    
   /** 
    * 調(diào)用flush,Hibernate會清理緩存(將session->insertions中臨時集合中的對象插入數(shù)據(jù)庫,在清空臨時集合) 
    * 此時并不能在數(shù)據(jù)庫中看到數(shù)據(jù),但是如果數(shù)據(jù)庫的隔離級別設(shè)置為未提交讀, 
    * 那么我們可以看到flush過的數(shù)據(jù),并且PersistenceContext中existsInDatabase狀態(tài)為true 
    */ 
   session.flush(); 
    
   /** 
    * 提交事物 
    * 默認(rèn)情況下,commit操作會執(zhí)行flush清理緩存, 
    * 所以不用顯示的調(diào)用flush 
    * commit后數(shù)據(jù)是無法回滾的 
    */ 
   transaction.commit(); 
  } catch (Exception e) { 
   e.printStackTrace(); 
   transaction.rollback(); 
  } finally{ 
   HibernateUtils.closeSession(session); 
  } 
 } 

我們可以通過斷點(diǎn)調(diào)試程序:

1.由于User的主鍵生成側(cè)率為UUID,調(diào)用save()方法之后,只能將User對象納入Session管理,不會發(fā)出insert語句,但是ID已經(jīng)生成了(注:save之后又兩個地方很重要,首先是session->actionQueue->insertions->elementData數(shù)組中有某個元素存儲了我們的對象,這是一個臨時集合對象,另外還有一個就是PersistenceContext->EntityEntries->map->table->某個數(shù)組元素->value存儲了該對象,value下面還有一個屬性那就是existsInDatabase代表數(shù)據(jù)庫中是否有對應(yīng)的數(shù)據(jù))。如圖:

2.調(diào)用完flush()方法之后,會清空session中的actionQueue的臨時存儲的值,然后將PersistenceContext中的existsInDatabase的值設(shè)為true,表示此時,數(shù)據(jù)庫中有對應(yīng)的數(shù)據(jù),但是此時打開數(shù)據(jù)庫打開表是看不到數(shù)據(jù)的,因?yàn)槲覀僊ySQL數(shù)據(jù)庫默認(rèn)的隔離級別為提交讀,即,必須提交才能讀取數(shù)據(jù),調(diào)用commit()方法之后,數(shù)據(jù)庫中有數(shù)據(jù)。

案例二

native方式生成主鍵的例子:

public class User1 {
	private Integer uid;
	private String uname;
	private Date birthday;
	public Integer getUid() {
		return uid;
	}
	public void setUid(Integer uid) {
		this.uid = uid;
	}
	public String getUname() {
		return uname;
	}
	public void setUname(String uname) {
		this.uname = uname;
	}
	public Date getBirthday() {
		return birthday;
	}
	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
}

User1.hbm.xml:

<?xml version="1.0"?> 
<!DOCTYPE hibernate-mapping PUBLIC 
  "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 
<!-- package表示實(shí)體類的包名 -->   
<hibernate-mapping package="com.lixue.bean"> 
 <!-- class結(jié)點(diǎn)的name表示實(shí)體的類名(賦值映射文件的時候要記得修改類名,否則會出現(xiàn)bug),table表示實(shí)體映射到數(shù)據(jù)庫中table的名稱 --> 
 <class name="User1" table="t_user1"> 
  <id name="uid"> 
   <!-- 自增長 --> 
   <generator class="native"/> 
  </id> 
  <property name="uname"/> 
  <property name="birthday"/> 
 </class> 
</hibernate-mapping> 

測試方法:

/** 
  * 測試native主鍵生成策略 
  */ 
 public void testSave2(){ 
  /*定義的Session和事物*/ 
  Session session = null; 
  Transaction transaction = null; 
  try { 
   /*獲取session和事物*/ 
   session = HibernateUtils.getSession(); 
   transaction = session.beginTransaction(); 
   /*創(chuàng)建用戶*/ 
   User1 user = new User1(); 
   user.setUname("小李"); 
   user.setBirthday(new Date()); 
   /** 
    * 因?yàn)閁ser1的主鍵生成策略是native,所以調(diào)用Session.save()后,將執(zhí)行insert語句,并且會清空臨時集合對象 
    * 返回由數(shù)據(jù)庫生成的ID,納入Session的管理,修改了Session中existsInDatabase狀態(tài)為true, 
    * 如果數(shù)據(jù)庫的隔離級別設(shè)置為未提交讀,那么我們可以看到save過的數(shù)據(jù) 
    */ 
   session.save(user); 
   transaction.commit(); 
  } catch (Exception e) { 
   e.printStackTrace(); 
   transaction.rollback(); 
  } finally{ 
   HibernateUtils.closeSession(session); 
  } 
 } 

通過斷點(diǎn)調(diào)試程序:

1.由于主鍵的生成策略為native,所以調(diào)用save()方法之后,將執(zhí)行insert語句,并且會清空臨時集合對象中的數(shù)據(jù),返回由數(shù)據(jù)庫生成的ID。

2.將對象納入session管理,修改了PersistenceContext中的existsInDatabase屬性為true(表示數(shù)據(jù)庫中有對應(yīng)的數(shù)據(jù),但是看不到,因?yàn)楦綦x界別的原因)

案例三

我們再來測試一下Hibernate的另一個方法,那就是evict(),表示將對象從session逐出。

針對UUID主鍵生成策略的程序,在來一個測試方法:

/** 
  * 測試uuid主鍵生成策略 
  */ 
 public void testSave3(){ 
  /*定義的Session和事物*/ 
  Session session = null; 
  Transaction transaction = null; 
  try { 
   /*獲取session和事物*/ 
   session = HibernateUtils.getSession(); 
   transaction = session.beginTransaction(); 
   /*創(chuàng)建用戶*/ 
   User user = new User(); 
   user.setUname("胡"); 
   user.setBirthday(new Date()); 
   /** 
    * 因?yàn)閁ser的主鍵生成策略為uuid,所以調(diào)用完save之后,只是將User納入到Session管理 
    * 不會發(fā)出insert語句,但是ID已經(jīng)生成。Session中的existsInDatebase狀態(tài)為false 
    */ 
   session.save(user); 
   /*將user對象從session中逐出,即從PersistenceContext的EntityEntries屬性中逐出*/ 
   session.evict(user); 
   /** 
    * 無法成功提交,因?yàn)镠ibernate在清理緩存時,在session的insertions臨時集合中取出user對象進(jìn)行insert 
    * 操作后,需要更新entityEntries屬性中的existsInDatabase為true,而我們調(diào)用了evict方法 
    * 將user從session的entityEntries中逐出了,所以找不到existsInDatabase屬性,無法更新,拋出異常 
    */ 
   transaction.commit(); 
  } catch (Exception e) { 
   e.printStackTrace(); 
   transaction.rollback(); 
  } finally{ 
   HibernateUtils.closeSession(session); 
  } 
 } 

通過斷點(diǎn)調(diào)試:

1.由于使用的是UUID的主鍵生成策略,所以調(diào)用save()方法之后,不會發(fā)送insert語句,只是將對象納入了session管理,ID已經(jīng)生成,數(shù)據(jù)庫中沒有與之對應(yīng)的數(shù)據(jù)(即existsInDatabase屬性值為false)。

2.調(diào)用evict()之后,將User對象從session中逐出,即從PersistenceContext的EntityEntries屬性中逐出。

3.當(dāng)我再調(diào)用commit()方法時,我們會發(fā)現(xiàn),我們的數(shù)據(jù)保存不了,因?yàn)橐婚_始我們的existsInDatabase屬性為false,即數(shù)據(jù)庫中不存在對應(yīng)數(shù)據(jù),緊接著我們又調(diào)用了evict()將PersistenceContext中的對象屬性(existsInDatabase屬性也包括在內(nèi))全刪除了,但是actionQueue中的臨時存儲數(shù)據(jù)還沒被刪除。我們只調(diào)用commit()方法時會先隱式的調(diào)用flush()方法,這個方法的作用之前也講過,它會將actionQueue中的臨時對象進(jìn)行insert操作,然后將PersistenceContext中的existsInDatabase屬性值設(shè)為true,但很遺憾,PersistenceContext中并沒有existsInDatabase屬性,所以會出現(xiàn)錯誤,導(dǎo)致無法保存。

為此,我們改進(jìn)上述程序:

/** 
  * 測試uuid主鍵生成策略 
  */ 
 public void testSave4(){ 
  /*定義的Session和事物*/ 
  Session session = null; 
  Transaction transaction = null; 
  try { 
   /*獲取session和事物*/ 
   session = HibernateUtils.getSession(); 
   transaction = session.beginTransaction(); 
   /*創(chuàng)建用戶*/ 
   User user = new User(); 
   user.setUname("胡"); 
   user.setBirthday(new Date()); 
   /** 
    * 因?yàn)閁ser的主鍵生成策略為uuid,所以調(diào)用完save之后,只是將User納入到Session管理 
    * 不會發(fā)出insert語句,但是ID已經(jīng)生成。PersistenceContext中的existsInDatebase狀態(tài)為false 
    */ 
   session.save(user); 
   /** 
    * flush后Hibernate會清理緩存,會將user對象保存到數(shù)據(jù)庫中,將session中的insertions中的user對象 
    * 清除,并且設(shè)置PersistenceContext中existsInDatabase的狀態(tài)為true 
    */ 
   session.flush(); 
   /*將user對象從session中逐出,即從PersistenceContext的EntityEntries屬性中逐出*/ 
   session.evict(user); 
   /** 
    * 可以成功提交,因?yàn)镠ibernate在清理緩存時,在session的insertions集合中無法 
    * 找到user對象(調(diào)用flush時清空了),所以就不會發(fā)出insert語句,也不會更新session中的existsInDatabase的狀態(tài) 
    */ 
   transaction.commit(); 
  } catch (Exception e) { 
   e.printStackTrace(); 
   transaction.rollback(); 
  } finally{ 
   HibernateUtils.closeSession(session); 
  } 
 } 

注:修改后的程序我們在save之后顯示的調(diào)用了flush()方法,再調(diào)用evict()方法。

通過斷點(diǎn)調(diào)試:

1.因?yàn)檫€是UUID的生成策略,所以在調(diào)用save之后,不會發(fā)出insert語句,只是將對象納入session管理,PersistenceContext中的existsInDatabase屬性為false。

2.調(diào)用完save()之后,我們又調(diào)用了flush()方法,這個方法的作用是清理緩存,即發(fā)出insert語句,將session中的insertions中的臨時對象插入到數(shù)據(jù)庫,然后清空該臨時集合,并且將PersistenceContext中的existsInDatabase屬性設(shè)置為true。

3.調(diào)用完flush()之后又調(diào)用evict()方法,它的作用是將user對象從session中清除,即清除PersistenceContext的EntityEntries屬性。

4.調(diào)用完evict()方法之后又調(diào)用commit()方法,它的會隱式的先調(diào)用flush()方法,而flush的作用是清除緩存,即將session->insertions臨時集合中的對象insert到數(shù)據(jù)庫中,但是我們之前就調(diào)用了flush()方法(注:調(diào)用完這個方法之后會清空臨時集合),所以臨時集合根本就沒有對象,所以不會發(fā)出insert語句。也不會去更新PersistenceContext中的existsInDatabase狀態(tài)。可以成功提交。

案例四

我們再來考慮下native方式的主鍵生成策略中使用evict()方法:

/** 
  * 測試native主鍵生成策略 
  */ 
 public void testSave5(){ 
  /*定義的Session和事物*/ 
  Session session = null; 
  Transaction transaction = null; 
  try { 
   /*獲取session和事物*/ 
   session = HibernateUtils.getSession(); 
   transaction = session.beginTransaction(); 
   /*創(chuàng)建用戶*/ 
   User1 user = new User1(); 
   user.setUname("馬"); 
   user.setBirthday(new Date()); 
   /** 
    * 因?yàn)閁ser1的主鍵生成策略是native,所以調(diào)用Session.save()后,將執(zhí)行insert語句, 
    * 返回由數(shù)據(jù)庫生成的ID,納入Session的管理,修改了Session中existsInDatabase狀態(tài)為true,并且清空了臨時集合 
    * 如果數(shù)據(jù)庫的隔離級別設(shè)置為未提交讀,那么我們可以看到save過的數(shù)據(jù) 
    */ 
   session.save(user); 
   /*將user對象從session中逐出,即從PersistenceContext的EntityEntries屬性中逐出*/ 
   session.evict(user); 
   /** 
    * 可以成功提交,因?yàn)镠ibernate在清理緩存的時候在session的insertions集合中 
    * 無法找到user對象,所以就不會發(fā)出insert語句,也不會更新session中的existtsInDatabase的狀態(tài) 
    */ 
   transaction.commit(); 
  } catch (Exception e) { 
   e.printStackTrace(); 
   transaction.rollback(); 
  } finally{ 
   HibernateUtils.closeSession(session); 
  } 
 } 

通過調(diào)試:

1.由于主鍵生成策略為native,所以調(diào)用完save方法之后,馬上就會發(fā)出insert語句,返回由數(shù)據(jù)庫生成的ID,將對象納入session管理,修改PersistenceContext中的existsInDatabase屬性為true即數(shù)據(jù)庫中有與之對應(yīng)的數(shù)據(jù),并且會清空臨時集合中的對象。但是由于MySQL隔離級別的原因我們在沒有提交之前是看不到數(shù)據(jù)的。

2.調(diào)用完save之后又調(diào)用evict()方法,將對象從session中逐出,即從PersistenceContext中的EntityEntries中逐出。

3.調(diào)用完evict()方法之后又調(diào)用commit()方法,此時是可以成功保存提交的,因?yàn)檎{(diào)用commit()之前會隱式調(diào)用flush()方法,即清理緩存,去臨時集合中找對象insert到數(shù)據(jù)庫,但是會發(fā)現(xiàn)臨時集合中已經(jīng)沒有數(shù)據(jù)了,所以不會發(fā)出insert語句,也就不會去更新PersistenceContext中的existsInDatabase屬性。

通過上述幾個案例,我們可以看出,有時候我們需要顯示的調(diào)用flush()方法,去清理緩存。另外,從上面我們也發(fā)現(xiàn)了一個問題,那就是當(dāng)我們save()了數(shù)據(jù),沒提交之前是看不到數(shù)據(jù)的,即數(shù)據(jù)庫的隔離界別限制了,現(xiàn)在我們來說說MySQL的隔離級別:

1.查看MySQL數(shù)據(jù)庫當(dāng)前的隔離級別:

select @@tx_isolation;

注:從圖中,我們可以看出,MySQL數(shù)據(jù)庫默認(rèn)的隔離級別為可重復(fù)讀,也就是說不會出現(xiàn)不可重復(fù)讀,即必須提交之后才能讀。

2.修改MySQL當(dāng)前的隔離級別(假設(shè)修改為未提交讀,即沒有提交就可以讀):

set transaction isolation level read uncommited;

總結(jié)

以上就是本文關(guān)于Hibernate的Session_flush與隔離級別代碼詳解的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!

相關(guān)文章

  • 淺談Java工程讀取resources中資源文件路徑的問題

    淺談Java工程讀取resources中資源文件路徑的問題

    下面小編就為大家?guī)硪黄獪\談Java工程讀取resources中資源文件路徑的問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • java自定義封裝StringUtils常用工具類

    java自定義封裝StringUtils常用工具類

    這篇文章主要為大家詳細(xì)介紹了java自定義封裝StringUtils常用工具類,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-03-03
  • Spring Boot實(shí)現(xiàn)功能的統(tǒng)一詳解

    Spring Boot實(shí)現(xiàn)功能的統(tǒng)一詳解

    這篇文章主要介紹了Spring Boot統(tǒng)一功能的處理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • java實(shí)現(xiàn)馬踏棋盤游戲

    java實(shí)現(xiàn)馬踏棋盤游戲

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)馬踏棋盤游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • MybatisPlus使用queryWrapper如何實(shí)現(xiàn)復(fù)雜查詢

    MybatisPlus使用queryWrapper如何實(shí)現(xiàn)復(fù)雜查詢

    這篇文章主要介紹了MybatisPlus使用queryWrapper如何實(shí)現(xiàn)復(fù)雜查詢,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教。
    2022-01-01
  • 聊聊springboot中整合log4g2的問題

    聊聊springboot中整合log4g2的問題

    這篇文章主要介紹了springboot中整合log4g2的方法,自定義文件名需要在application.yml中配置,在config中配置log4g2.xml文件,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2021-11-11
  • Maven構(gòu)建時跳過部分測試的實(shí)例

    Maven構(gòu)建時跳過部分測試的實(shí)例

    下面小編就為大家分享一篇Maven構(gòu)建時跳過部分測試的實(shí)例,具有很好的參考價值,希望對大家有所幫助
    2017-11-11
  • Spring cloud config 配置文件加密方式

    Spring cloud config 配置文件加密方式

    這篇文章給大家介紹了Spring cloud config 配置文件加密方式,非常不錯,具有一定的參考借鑒價值,感興趣的朋友跟隨腳步之家小編一起學(xué)習(xí)吧
    2018-05-05
  • SpringBoot統(tǒng)一返回JSON格式實(shí)現(xiàn)方法詳解

    SpringBoot統(tǒng)一返回JSON格式實(shí)現(xiàn)方法詳解

    這篇文章主要介紹了SpringBoot統(tǒng)一返回JSON格式實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-02-02
  • java使用JOptionPane猜數(shù)字游戲

    java使用JOptionPane猜數(shù)字游戲

    這篇文章主要為大家詳細(xì)介紹了java使用JOptionPane猜數(shù)字游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-05-05

最新評論