Hibernate的系統 學習


Hibernate的系統 學習

一、Hibernate的介紹

 

1.什么是Hibernate?

          首先,hibernate是數據持久層的一個輕量級框架。數據持久層的框架有很多比如:iBATIS,myBatis,Nhibernate,Siena等等。

          並且Hibernate是一個開源的orm(object relations mapping)框架,提供了查詢獲取數據的方法,用面向對象的思想來操作數據庫,節省了我們開發處理數據的時間。

2.那使用Hibernate的優點呢?

          1.使用簡介的hql語句(Hibernate query language)。可以不使用傳統的insert,update等sql語句。比如insert一個對象,原來的做法是:insert into 表名稱 alue(值1,值2,值3,……),而現在的做法是:save(對象)。

          2.使用or映射。對象到關系數據庫之間的映射。是從對象的角度操作數據庫,再次體現了面向對象思想。原來的實體抽取方法:首先有了表,然后表映射實體對象。而現在Hibernate做法是:直接由對象映射到表。

          3.沒有侵入性,移植性比較好。什么是沒有侵入性?就是Hibernate采用了pojo對象。所謂的pojo對象就是沒有繼承Hibernate類或實現Hibernate接口。這樣的話,此類就是一個普通的Java類,所以移植性比較好。  

         4.支持透明持久化。透明是針對上層而言的。三層架構的理念是上層對下層的依賴,只是依賴接口不依賴具體實現。而Hibernate中的透明是指對業務邏輯層提供了一個接口session,而其他的都封裝隱藏。持久化是指把內存中的數據存放到磁盤上的文件中。

3.當然一個事物,不可能十全十美,即使如此優秀的Hibernate也有自己的弱點。比如:若是大量數據批量操作。則不適合使用Hibernate。並且一個持久化對象不能映射到多張表中。

4.Hibernate中核心5個接口

       1.Configuration接口:負責配置及啟動Hibernate,用來創建sessionFactory

       2.SessionFactory接口:一個SessionFactory對應一個數據源存儲,也就是一個數據庫對應一個SessionFactory。SessionFactory用來創建Session對象。並且SessionFactory是線程安全的,可以由多個線程訪問SessionFactory共享。

       3.Session接口:這個接口是Hibernate中常用的接口,主要用於對數據的操作(增刪改查)。而這個Session對象不是線程安全的。不能共享。

       4.Query接口:用於數據庫的查詢對象。

       5.Transaction接口:Hibernate事務接口。它封裝了底層的事務操作,比如JTA(;Java transcation architecture)所有的數據操作,比如增刪改查都寫在事務中。

基本的概念以及核心接口已經介紹,那Hibernate又是如何應用的呢?下篇博客將會介紹如何使用Hibernate?

  二、詳解Hibernate的搭建及使用
  

 1.創建普通的Java項目。

               因為hibernate是一個輕量級的框架,不像servlet,還必須需要tomcat的支持,Hibernate只要jdk支持即可。

       2.引入jar包。

               可以在項目中直接引入jar包,在:項目--->屬性--->然后如下圖:

              另一種辦法就是引入庫,相當於一個文件夾,把所有的jar包放到自己新建的文件夾中。在:窗體-->選項-->然后如下圖:

      3.提供Hibernate的配置文件。hibernate.cfg.xml文件。完成相應的配置。

   

    

[html]  view plain  copy
 
 print?
  1. <hibernate-configuration>  
  2.   
  3. <session-factory>  
  4.   
  5. <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>  
  6.   
  7. <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate_first</property>  
  8.   
  9. <property name="hibernate.connection.username">root</property>  
  10.   
  11. <property name="hibernate.connection.password">bjpowernode</property>  
  12.   
  13. <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>  
  14.   
  15. </session-factory>  
  16.   
  17. </hibernate-configuration>  


 

      在這里連接MySQL數據庫,解釋一下上面的標簽。按照順序來依次解釋:第一個是連接mySql的驅動;第二個是連接的url;url后面的hibernate_first是數據庫名字;第三個是和第四個分別是用戶名和密碼。第五個是方言。因為 hibernate對數據庫封裝,對不同的數據庫翻譯成不同的形式,比如drp中的分頁,若是使用Oracle數據庫,則翻譯成sql語句三層嵌套。若是使用mySql數據庫,則翻譯成limit語句。

    4.建立實體User類:

[java]  view plain  copy
 
 print?
  1. package com.bjpowernode.hibernate;  
  2.   
  3. import java.util.Date;  
  4.   
  5. public class User {  
  6.   
  7.     private String id;  
  8.       
  9.     private String name;  
  10.       
  11.     private String password;  
  12.       
  13.     private Date createTime;  
  14.       
  15.     private Date expireTime;  
  16.   
  17.     public String getId() {  
  18.         return id;  
  19.     }  
  20.   
  21.     public void setId(String id) {  
  22.         this.id = id;  
  23.     }  
  24.   
  25.     public String getName() {  
  26.         return name;  
  27.     }  
  28.   
  29.     public void setName(String name) {  
  30.         this.name = name;  
  31.     }  
  32.   
  33.     public String getPassword() {  
  34.         return password;  
  35.     }  
  36.   
  37.     public void setPassword(String password) {  
  38.         this.password = password;  
  39.     }  
  40.   
  41.     public Date getCreateTime() {  
  42.         return createTime;  
  43.     }  
  44.   
  45.     public void setCreateTime(Date createTime) {  
  46.         this.createTime = createTime;  
  47.     }  
  48.   
  49.     public Date getExpireTime() {  
  50.         return expireTime;  
  51.     }  
  52.   
  53.     public void setExpireTime(Date expireTime) {  
  54.         this.expireTime = expireTime;  
  55.     }  
  56. }  


 

    5.建立User.hbm.xml,此文件用來完成對象與數據庫表的字段的映射。也就是實體類的那些字段需要映射到數據庫表中呢。 

   

[html]  view plain  copy
 
 print?
  1. <?xml version="1.0"?>  
  2. <!DOCTYPE hibernate-mapping PUBLIC   
  3.     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
  4.     "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
  5. <hibernate-mapping>  
  6.     <class name="com.bjpowernode.hibernate.User">  
  7.         <id name="id">  
  8.             <generator class="uuid"/>  
  9.         </id>  
  10.         <property name="name"/>  
  11.         <property name="password"/>  
  12.         <property name="createTime"/>  
  13.         <property name="expireTime"/>  
  14.     </class>  
  15. </hibernate-mapping>  


 

    6.我們也映射完畢了,但是hibernate怎么知道我們映射完了呢,以及如何映射的呢?這就需要我們把我們自己的映射文件告訴hibernate,即:在hibernate.cfg.xml配置我們的映射文件。

 

[html]  view plain  copy
 
 print?
  1. <mapping resource="com/bjpowernode/hibernate/User.hbm.xml"/>  


 

    7.生成數據庫表。大家也看到了我們上述還沒有新建數據表呢,在第三步我們只是新建了數據庫而已。按照我們普通的做法,我們應該新建數據表啊,否則實體存放何處啊。這個別急,數據庫表這個肯定是需要有的,這個毋庸置疑,但是這個可不像我們原來需要自己親自動手建立哦,現在hibernate需要幫我們實現哦,如何實現嗯,hibernate會根據配置文件hibernate.cfg.xml和我們的映射文件User.hbm.xml會自動給我們生成相應的表,並且這個表的名字也給我們取好:默認是User。那如何生成表呢?

 

[java]  view plain  copy
 
 print?
  1. //默認讀取hibernate.cfg.xml文件  
  2.     Configuration cfg = new Configuration().configure();  
  3.       
  4.     SchemaExport export = new SchemaExport(cfg);  
  5.     export.create(true, true);  


 

 8.那我們就開始進行操作啦,我們添加一個用戶對象,看看hibernate是如何添加的呢?跟我們以前的做法有什么不同呢?

[java]  view plain  copy
 
 print?
  1. public class Client {  
  2.   
  3.     public static void main(String[] args) {  
  4.           
  5.         //讀取hibernate.cfg.xml文件  
  6.         Configuration cfg = new Configuration().configure();  
  7.           
  8.         //建立SessionFactory  
  9.         SessionFactory factory = cfg.buildSessionFactory();  
  10.           
  11.         //取得session  
  12.         Session session = null;  
  13.         try {  
  14.             session = factory.openSession();  
  15.             //開啟事務  
  16.             session.beginTransaction();  
  17.             User user = new User();  
  18.             user.setName("張三");  
  19.             user.setPassword("123");  
  20.             user.setCreateTime(new Date());  
  21.             user.setExpireTime(new Date());  
  22.               
  23.             //保存User對象  
  24.             session.save(user);  
  25.               
  26.             //提交事務  
  27.             session.getTransaction().commit();  
  28.         }catch(Exception e) {  
  29.             e.printStackTrace();  
  30.             //回滾事務  
  31.             session.getTransaction().rollback();  
  32.         }finally {  
  33.             if (session != null) {  
  34.                 if (session.isOpen()) {  
  35.                     //關閉session  
  36.                     session.close();  
  37.                 }  
  38.             }  
  39.         }  
  40.     }  
  41. }  


 

 第八步,我們可以看到,沒有我們熟悉的insert into表的sql語句了,那怎么添加進去的呢,到底添加了沒?讓我真實滴告訴你,確實添加進去了,不信的,可以自己嘗試哦,這也是hibernate的優點,對jdbc封裝的徹底,減少了我們對數據的操作時間哈。

 三、Hibernate的總結
  

看一下hibernate中整體的內容:

   我們一一介紹其中的內容。

  1. Hibernate出現的原因上篇博客已經介紹,可以參考《Hibernate介紹》
  2. Hibernate中的核心五大接口,在上篇博客中也已經介紹,可以參考《Hibernate介紹》
  3. 如何搭建Hibernate,請參考《八步詳解Hibernate的搭建及使用》
  4. 持久化對象的三種狀態。

分別為:瞬時狀態(Transient),持久化狀態(Persistent),離線狀態(Detached)。三種狀態下的對象的生命周期如下:

三種狀態的區別是:瞬時狀態的對象:沒有被session管理,在數據庫沒有;持久化狀態的對象:被session管理,在數據庫存在,當屬性發生改變,在清理緩存時,會自動和數據庫同步;離線狀態:沒有被session管理,但是在數據庫中存在。

5.測試工具Juit。

測試類需要繼承TestCase,編寫單元測試方法,方法名稱必須為test開頭,方法沒有參數沒有返回值,采用public修飾。其中在測試中,查詢對象時,使用get或者load兩種方法進行加載,這種方法的區別:get不支持延遲加載,而load默認情況下是支持延遲加載。並且get查詢對象不存在時,返回null;而load查詢對象不存在時,則拋出ObjectNotFoundException異常。

6.悲觀鎖和樂觀鎖解釋。

悲觀鎖為了解決並發性,跟操作系統中的進程中添加鎖的概念一樣。就是在整個過程中在事務提交之前或回滾之前,其他的進程是無法訪問這個資源的。悲觀鎖的實現方式有兩種:一種使用數據庫中的獨占鎖;另一種是在數據庫添加一個鎖的字段。hibernate中聲明鎖如下:

Account account = (Account)session.get(Account.class, 1, LockMode.UPGRADE);而net.sf.hibernate.LockMode類表示鎖模式,當取值LockMode.UPGRADE時,則表示使用悲觀鎖for update;而樂觀鎖是為了解決版本沖突的問題。就是在數據庫中添加version字段,每次更新時,則把自己的version與數據庫中的version進行比較,若是版本相比較低,則不允許進行修改更新。

7.H ibernate中的緩存機制。

緩存是什么呢?緩存是應用程序和數據庫之間的內存的一片區域。主要的目的是:為了減少對數據庫讀取的時間。當查詢數據時,首先在緩存中查詢,若存在,則直接取出,若不存在,然后再向數據庫中查詢。所以應該把經常訪問數據庫的數據放到緩存中,至於緩存中的數據如何不斷的置換,這也需要涉及一種淘汰數據的算法

談到這個hibernate中的緩存,你想到了什么呢?剛才敘述緩存時,是否感覺很熟悉,感覺從哪也聽過似的。嗯呢,是呢,是很熟悉,寫着寫着就很熟悉,這個剛才的緩存以及緩存的置換算法就和計算機組成中的cache類似。

好吧,來回到我們hibernate中的緩存。

hibernate中的緩存可以分為兩種:一級緩存,也稱session緩存;二級緩存,是由sessionFactory管理。

那一級緩存和二級緩存有什么區別呢?區別的關鍵關於:緩存的生命周期,也就是緩存的范圍不同。

那首先介紹一下緩存的生命周期,也就是緩存的范圍。

1.事務緩存,每個事務都有自己的緩存,當事務結束,則緩存的生命周期同樣結束,正如上篇博客中我們提到,對數據庫的操作,增刪改查都是放到事務中的,和事務保持同步,若是事務提交完畢,一般是不允許是再次對數據庫進行操作。所以session是屬於事務緩存的。

2.應用緩存,一個應用程序中的緩存,也就是應用程序中的所有事務的緩存。只有當應用程序結束時,此時的緩存的額生命周期結束。二級緩存就是應用緩存。

3.集群緩存,被一台機器或多台機器的進程共享。

這下明白了一級緩存和二級緩存的區別了吧。那一級緩存和二級緩存的共同點是:都是緩存實體屬性,

二級緩存一般情況都是由第三方插件實現的。第三方插件如:

EHCache,JbossCache(是由Jboss開源組織提供的),osCache(open symphony),swarmCache。前三種對hibernate中的查詢緩存是支持的,后一種是不支持hibernate查詢緩存。

那什么是hibernate查詢緩存呢?

查詢緩存是用來緩存普通屬性的,對於實體對象而言,是緩存實體對象的id。

8.hql查詢。

  hibernate query language。hql查詢中關鍵字不區分大小寫,但是類和屬性都是區分大小寫的。

  1.簡單屬性查詢。

  單一屬性查詢,返回屬性結果集列表,元素類型和實體類的相應的類型一致。

[java]  view plain  copy
 
 print?
  1. List students = session.createQuery("select name from Student").list();  
  2.   
  3. for (Iterator iter=students.iterator(); iter.hasNext();) {  
  4.   
  5. String name = (String)iter.next();  
  6.   
  7. System.out.println(name);  
  8.   
  9. }  


 

 //返回結果集屬性列表,元素類型和實體類中的屬性類型一致

多個屬性查詢,多個屬性查詢返回數組對象,對象數組的長度取決於屬性的個數,對象數組中的元素類型與實體類中屬性一致。

[java]  view plain  copy
 
 print?
  1. List students = session.createQuery("select id, name from Student").list();  
  2.   
  3. for (Iterator iter=students.iterator(); iter.hasNext();) {  
  4.   
  5. Object[] obj = (Object[])iter.next();  
  6.   
  7. System.out.println(obj[0] + ", " + obj[1]);  
  8.   
  9. }  


 

2.實體對象查詢

List students = session.createQuery("from Student").list();

當然這種hql語句,可以使用別名,as可以省去,如:from Student as s,若是使用select關鍵字,則必須使用別名。如:select s from Student as s.但是不支持select * from Student格式。

查詢中使用list和Iterate區別:

list查詢是直接運行查詢的結果,所以只有一句sql語句。而iterate方法則有可能會產生N+1條sql語句。這是怎么回事呢?要理解N+1條語句,首先得弄明白iterate是如何執行查詢的?

首先發出一條查詢對象ID的語句,然后根據對象的ID到緩存(緩存的概念上篇博客已經提到)中查找,若是存在查詢出此對象的其他的屬性,否則會發出N條語句,此時的N語句,是剛才第一次查詢的記錄條數。這種現象就是N+1sql語句。

其中list是默認情況下都發出sql語句,查詢出的結果會放到緩存中,但是它不會利用緩存,即使放進去,下次執行時,仍然繼續發出sql語句。

而:iterate默認情況下會利用緩存,若是緩存中有則不會發出N+1條語句。

3.條件查詢。

這種方式就是傳入參數,使用參數占位符“?”。也可以使用“:參數名”

Java代碼如下:

[java]  view plain  copy
 
 print?
  1. List students = session.createQuery("select s.id, s.name from Student s where s.name like ?")  
  2.   
  3. .setParameter(0, "%0%")  
  4.   
  5. .list();  
  6.   
  7. List students = session.createQuery("select s.id, s.name from Student s where s.name like :myname")  
  8.   
  9. .setParameter("myname",  "%0%")  
  10.   
  11. .list();  


 4.使用原生sql語句。

     和咱們原先寫入的sql語句一樣。在此不介紹了。

 5.外置命名查詢。

      這個聽起來有點晦澀,怎么理解呢?其實通俗的說就是把hql語句寫在外面,寫在映射文件中。使用標簽:

   

[html]  view plain  copy
 
 print?
  1. <query name="queryStudent">  
  2.   
  3. <![CDATA[ 
  4.  
  5. select s from Student s where s.id <? 
  6.  
  7. ]]>  
  8.   
  9. </query>  


 

    那在程序中是如何使用此標簽的呢?使用session.getNameQuery(),並進行賦值,代碼如下:

[java]  view plain  copy
 
 print?
  1. List students = session.getNamedQuery("queryStudent")  
  2.   
  3. .setParameter(0, 10)  
  4.   
  5. .list();  

 

 6.查詢過濾器。

    這個是什么意思呢?過濾器大家很熟悉吧,不熟悉的可以參考我的以前博客<>.原來我們接觸過編碼過濾器,編碼過濾器就是為了避免當時每個頁面需要設置編碼格式而提出的。這個查詢過濾器其實也是這個意思。若是代碼都需要某一句sql語句的話,可以考慮使用它。這樣可以避免每次都寫查詢語句。

    使用如下:首先在映射文件中配置標簽:

[html]  view plain  copy
 
 print?
  1. <filter-def name="testFilter">  
  2.   
  3. <filter-param type="integer" name="myid"/>  
  4.   
  5. </filter-def>  

 

  然后程序中如下使用並進行賦值:

[java]  view plain  copy
 
 print?
  1. session.enableFilter("testFilter")  
  2.   
  3. .setParameter("myid", 10);  


7.分頁查詢。

    分頁查詢,這個肯定不陌生,因為在做drp項目時,做的最多的是分頁,當時使用Oracle數據庫,分頁查詢涉及到三層嵌套。直接傳入的參數為:每頁的大小(記錄數),頁號。

     Hibernate中給我們已經封裝好了,只要設置開始的頁號以及每頁的大小即可,不用親自動手寫嵌套的sql語句。

     代碼如下:

[java]  view plain  copy
 
 print?
  1. List students = session.createQuery("from Student")  
  2.   
  3. .setFirstResult(1)  
  4.   
  5. .setMaxResults(2)  
  6.   
  7. .list();  

 

8.對象導航查詢。

這個什么意思呢?這個只要是用於一個類的屬性是另一個類的引用。比如:student類中有一個classes屬性。其中的classes也是一個類Class的引用。

當我們查詢的時候可以這樣使用:

[java]  view plain  copy
 
 print?
  1. List students = session.createQuery("from Student s where s.classes.name like '%t%'")  
  2.   
  3. .list();  


相當於:s.getClasses.getName(),直接使用get后面的屬性,然后首字母小寫。

 這種語法,是不是很熟悉?想想我們在哪是不是也用過?想起來了嗎?估計你猜出來啦,呵呵,是JSTL(jsp standard tag library)中。若是想進一步了解,可以參考我的博客哈,當時是轉載滴貌似。

9.連接查詢。

    連接分為:內連接和外連接,其中外連接分為左連接,右連接,完全連接。這個跟數據庫中的左右連接其實是一樣的。我們通俗解釋一下:

    左連接:以左邊為准,右邊即使沒喲匹配的,也要把這條記錄查詢出來,此時沒有匹配的右邊以null填充。

    右連接:以右邊為准,左邊即使沒有匹配的,也要把這條記錄查詢出來,此時沒有匹配的左邊以null填充。

   完全連接:只要一方存在即可。

   內連接:必須兩方都存在才可以查詢提取此記錄。

10.統計查詢。

     其實就是查詢count的記錄數。其中查詢出來的額count是long類型。

11.DML風格的操作。

     DML?其實DML=Data Manipulate Language(數據操作語言),舉個例子:

  

[java]  view plain  copy
 
 print?
  1. session.createQuery("update Student s set s.name=? where s.id<?")  
  2.   
  3. .setParameter(0, "王斌")  
  4.   
  5. .setParameter(1, 2)  
  6.   
  7. .executeUpdate();   

           

假若原來的名字是:李四,更新完數據庫后變成王斌,若是我們此時取出數據,其姓名是李四還是王斌?按照道理應該是王斌,但是結果確實李四,若不信,可以自己去實踐一下。

這個原因,是因為更新了數據庫,但是緩存中沒有更新,才會造成這種數據庫和緩存不同步的問題。

所以,我們應該盡量不使用這種形式。揚其長避其短嘛。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM