Hibernate從入門到了解

目錄:java


首發日期:2018-07-31mysql

修改:sql

  • 2018-08-11:從頭回顧了一遍,對各個地方增長了講解,以幫助讀者更好地瞭解。修改了關係映射中的錯誤表述,因爲重複次數問題,致使那裏我填混亂了。
  • 2018-10-25:把原來的版本修改爲了markdown版本的,從新整理了順序和內容解釋,把圖片都轉存到個人圖牀,並從新發布了博文。



Hibernate的介紹與執行流程


  • Hibernate是一個持久層的框架(持久層作的事一般都是將數據保存到數據庫中)。
  • Hibernate是基於ORM的框架。ORM把對象和數據表創建了關係,使得能夠經過操做對象來操做表。ORM能夠根據對象與數據表的映射關係來處理SQL語句,類對象的建立能夠對應數據表記錄的插入操做,類對象的更改能夠對應數據表記錄update操做。基於ORM以後,對於對象的操做會自動生成對應的SQL語句來處理數據表,而不須要像JDBC那樣須要本身去拼接SQL語句,省去了不少麻煩。

題外話:數據庫

什麼是ORM(對象關係映射):ORM 將數據庫中的表與面嚮對象語言中的類創建了一種對應關係,【ORM能夠說是參照映射來處理數據的模型,好比說:須要建立一個表,能夠定義一個類,而這個類存在與表相映射的屬性,那麼能夠經過操做這個類來建立一個表】apache

爲何須要ORM框架:JDBC的API是最底層的操做數據庫的API了,但明顯的是,使用它很麻煩,由於每個交互都須要你去編寫(包括編寫SQL語句之類的);而使用ORM,它幫你定義了關係和封裝了API,使得你能夠遠離底層,好比你能夠調用save(類對象),這可讓你很輕鬆的將這個對象的各個屬性插入到與類創建了關係的對應數據表中。api


運行流程:



1.當使用Hibernate時,Hibernate須要先去加載一個核心配置文件,讀取Hibernate的核心配置信息(數據庫鏈接信息、日誌管理、數據表映射關係等)【在讀取的過程當中,會將讀取的配置信息逐一配置給對應的組件(鏈接池、日誌等等)。】;緩存

2.載入核心配置文件的過程的最後是讀取核心配置文件中標註的「映射關係文件」。「映射關係文件」定義着持久化類與數據表之間的映射關係。(在讀取映射關係文件的過程當中,同時會判斷是否有對應的數據表,若是沒有表可能會根據類與表的映射關係來建立表,表創建以後,再創建映射);安全

3.核心配置和映射關係都讀取完畢以後。Hibernate的基本運行配置就配置完畢了。在前面的加載配置中,返回的是一個Configuration對象,Configuration對象主要是用來獲取配置文件的。markdown

4.經過Configuration對象的buildSessionFactory方法獲取一個sessionFactory對象,sessionFactory對象真正負責解析配置文件中的配置選項並配置給各個組件,在不少時候,sessionFactory對象至關於一個鏈接池,它解析了Hibernate的配置並負責分發會話鏈接。

5.經過sessionFactory對象的openSession方法來獲取一個Session對象,有了Session對象就至關於有了JDBC中的Connection對象,它至關於與數據庫的會話鏈接。咱們可使用Session對象來操做數據庫。

6.對於增刪改操做,咱們須要事務管理對象Transaction對象的協助。能夠經過Session對象獲取一個Transaction對象,Transaction對象負責數據庫的事務管理。

7.對於增刪改操做,須要開啓事務對象Transaction,但對於查詢操做,是不須要事務的。對於增刪改操做,能夠經過Session對象的調用save\delete\update方法來操做數據庫;對於查詢操做,能夠經過get\load等方法來操做數據庫。


Hibernate運行環境搭建

下面的運行環境基於的是Hibernate5.3.0

【不過更建議使用低一點的版本的,好比可使用5.0.x版本的】

【聽說Hibernate5.x要求JDK版本最低要1.7?不過我沒嘗試過,若是發現報錯,能夠考慮一下多是由於這個問題。】
【若是你會maven的話,也能夠嘗試自查一下maven搭建Hibernate】

1.在官網下載Hibernate5
下載方法能夠參考一下這個:https://jingyan.baidu.com/article/7e4409530e67d82fc0e2eff9.html



2.解壓下載的壓縮包



3.在解壓出來的文件夾中,lib存放着Hibernate運行所須要的jar包。lib目錄下有好幾個文件夾,每一個文件夾下有Hibernate不一樣功能需求的jar包。




4.由於這裏僅僅作最基礎的運行,因此僅僅將required中的11個jar包導入到工程中。

若是你還須要其餘功能,能夠按照前面圖中標註不一樣功能的文件夾來選擇添加。好比想要使用C3P0,能夠將optional文件夾中c3p0文件夾下的包都導入。


5.導入Hibernate以外的包:因爲Hibernate是持久層的框架,因此它還須要數據庫驅動包。另外,Hibernate依賴slf4j日誌接口,因此也須要導入。

  • 數據庫驅動包:
    • 【版本能夠比較隨意】
  • 日誌包
    • 【這是Hibernate的日誌接口,若是要擴展日誌功能,須要這個接口】【這個能夠在lib\spatial中找到】
    • 【slf4j只是一個日誌接口,沒有具體的日誌功能,這裏咱們使用log4j來實現日誌功能,但slf4j不能直接到log4j,它須要轉換器,這個就是轉換器】【這個須要你本身下載】
    • 【這個也須要你本身下載】


6.導入包以後,運行環境就基本搭建完畢了。後面都是具體需求具體配置了。


Hibernate的基礎示例



這個基礎示例做用是讓你瞭解Hibernate的使用須要作什麼?先讓你瞭解須要作什麼,後面再討論每一個流程裏面可以作哪些操做。這個示例能夠與上面的運行流程結合瞭解。


1.運行環境搭建,導入須要的依賴包。

2.建立持久類Person,持久類用於跟數據表創建映射關係,類的成員變量與數據表的字段對應

public class Person {
    
    private Long pid;
    private String username;
    private Integer age;
    private Integer address;
    public Long getPid() {
        return pid;
    }
    public void setPid(Long pid) {
        this.pid = pid;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public Integer getAddress() {
        return address;
    }
    public void setAddress(Integer address) {
        this.address = address;
    }
}


3.建立映射文件Person.hbm.xml,這個文件的意義是將持久類與數據表的屬性創建映射關係(具體配置先不說,只在圖內簡單介紹,下面再具體介紹),以便使用ORM操做來管理數據表。這個映射文件與持久類Person位於同一個包下。
映射文件主要包括類與表的對應,以及它們之間的成員變量與字段的對應。


4.建立核心配置文件:hibernate.cfg.xml,這個核心配置文件包含了數據庫鏈接信息、hibernate自身配置(方言,是否顯示SQL語句)、映射文件路徑等信息

【上圖有個自動建表操做:數據表也能夠交給Hibernate建立(但不建議,由於方言會影響使用什麼語句來建立數據表,有時候方言與數據庫版本之間會有一些問題。因此十分建議提早建立,否則你可能會遇到其餘問題】



5.Hibernate的核心配置文件和映射文件已經寫好了,那麼下面的重點就是獲取對象來操做了。使用Configuration來讀取核心配置文件,返回結果是Configuration對象,這個對象包含了Hibernate的配置信息(就比如工廠有了設計圖紙),下面的操做會自動讀取hibernate.cfg.xml中的配置。



6.使用前面獲取到的Configuration對象來獲取一個SessionFactory對象,SessionFactory對象是解析了Hibernate的配置信息。(能夠說SessionFactory對象就是一個獲取到圖紙以後,已經作好準備開工的工廠)



7.SessionFactory對象能夠翻譯成session工廠,可使用它來獲取Session對象,Session對象創建了與數據庫的會話鏈接,至關於JDBC中的Connection對象,咱們可使用Session對象來與數據庫交互。【Transaction是事務對象,負責管理事務,對於增刪改都須要事務對象。】

上面的過程就是經過保存一個對象來將數據存儲到數據表中的過程。


持久類的編寫


持久類的介紹

  • 持久類是用於跟數據表產生映射關係的類。 一個沒有映射關係的類不是持久化類。
  • 持久類的定義與普通的javabean沒有什麼區別,若是你瞭解javabean,那麼你應該知道javabean有三個標準:
    • 1.屬性私有化
    • 2.有無參構造函數 (Hibernate一樣須要利用反射來建立對象,從數據表中獲取數據記錄時會轉成一個個對象)
    • 3.提供私有的屬性的公有getter和setter函數。
    • 除了以上與javabean相同的要求以外,還須要一個惟一標識屬性(也能夠稱爲OID,對象標識(Object identifier-OID))。它至關於數據表中的主鍵,Hibernate利用惟一標識屬性(OID)來特異性地識別每一個對象。

一個簡單示例:

public class Person {
    private Long pid;//惟一標識屬性,用來映射表中的主鍵
    private String username;
    private Integer age;
    private Integer address;
    public Long getPid() {
        return pid;
    }
    public void setPid(Long pid) {
        this.pid = pid;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public Integer getAddress() {
        return address;
    }
    public void setAddress(Integer address) {
        this.address = address;
    }
}


幾個考慮遵照的規則:

1.持久化類的屬性儘可能使用包裝類類型。【由於基礎數據類型一般存在默認值而且一般不爲null,這個默認值也會存儲到數據表中,若是不注意就會引發歧義。,null在數據表中是空的意思,在數據表中它的意義是明確的,但若是默認值是0的話,意思就是不明確了。好比某個字段用0,1表明男女,那麼默認值爲0的話就很差肯定是不是忘記設置性別仍是已經設置了。】

2.持久化類不要使用final修飾,否則沒法使用延遲加載功能(這個東西將會在後面的CRUD中load()方式的查詢中講),延遲加載功能依賴於代理對象,使用final修飾以後就沒法產生代理對象了。


補充:

  • 持久類還有三種狀態須要認識,由於涉及到與數據庫的交互和緩存區,因此留到後面緩存那裏再講。



Hibernate核心文件配置


核心配置文件的配置方式有使用properties和xml這兩種,咱們主要使用xml版,由於xml版能夠導入映射文件,而使用properties版的就須要本身在代碼中引入映射文件


使用XML配置核心文件:

  • 命名要求:hibernate.cfg.xml 【保存路徑:src目錄下,不然須要在.configure()的時候傳入它的路徑】
  • XML須要約束,那麼它的約束頭去哪裏找:它在依賴包hibernate-core-5.3.0.Final.jar下的org.hibernate下的hibernate-configuration-3.0.dtd的註釋中(hibernate5與hibernate3的類似,因此使用的是3.0的dtd)。
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
  • xml文件結構:根標籤爲hibernate-configuration標籤,hibernate-configuration標籤裏面包含了一個session-factory標籤,session-factory裏面使用property標籤寫配置信息。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
    
<hibernate-configuration>
    <session-factory>
        <!-- 設置hibernate屬性 -->
        <property name="配置屬性名" >屬性值</property>
        <!-- 引入映射文件 -->
        <mapping resource="映射文件路徑"/>
    </session-factory>
</hibernate-configuration>


基礎配置選項

基礎配置選項是必需要配置的選項

  • 數據驅動
    • 格式:<property name="hibernate.connection.driver_class" >數據驅動</property>
    • 例如:<property name="hibernate.connection.driver_class" >com.mysql.jdbc.Driver</property>
  • 數據庫url
    • 格式:<property name="hibernate.connection.url">url</property>
    • 例如:<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate</property>
  • 數據庫用戶名
    • 格式:<property name="hibernate.connection.username">用戶名</property>
    • 例如:<property name="hibernate.connection.username">root</property>
  • 數據庫用戶密碼
    • 格式:<property name="hibernate.connection.password">密碼</property>
    • 例如:<property name="hibernate.connection.password">123456</property>
  • SQL方言:定義了怎麼轉換SQL語句,就好像使用谷歌翻譯須要肯定轉成哪一種語言 【下面會講能填哪些值】
    • 格式:<property name="hibernate.dialect">方言</property>
    • 例如:<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
  • 映射關係文件:【注意,映射關係文件要放在最下面!它是核心配置中最後加載的】
    • 格式:<mapping resource="hbm文件的路徑"/>
    • 例如:<mapping resource="work/domain/Person.hbm.xml"/>


可選配置:

可選配置是說明下面的配置是可選的 ,能夠不配置

  • 顯示對應的sql語句:使用hibernate操做對象,會轉成對應的sql語句,能夠設置sql語句是否顯示到控制檯。
    • 格式:<property name="hibernate.show_sql">true或false</property>【true是顯示,false是不顯示】
    • 例如:<property name="hibernate.show_sql">true</property>
  • 格式化顯示的sql語句 :當選擇顯示sql語句後,默認的是一行顯示的,使用格式化後,是多行的。
    • 格式:<property name="hibernate.format_sql">true或false</property> 【true是美化,false是不美化】
    • 例如:<property name="hibernate.format_sql">true</property>
  • 是否自動建表hibernate.hbm2ddl.auto :
    • 格式:<property name="hibernate.hbm2ddl.auto">create或creat-drop或update或validate</property>
      • 不配的時候:不使用hibernate的建表功能
      • create:不論有沒有表,每次都從新建立表。
      • update:若是沒有表,就建立;若是有了,就用已有;若是定義的表結構跟現有的不符合,那麼修改爲定義的表結構。
      • create-drop:不論有沒有表,每次都從新建立表,而且在用完後就會刪除。
      • validate:不會建立表,只會使用已經有了的表,因此若是結構不符合會報錯。
    • 例如:·<property name="hibernate.hbm2ddl.auto">update</property>

下面給出一個配置好了的hibernate.cfg.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
    
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class" >com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate2</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">123456</property>
        
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
        <property name="hibernate.hbm2ddl.auto">update</property>
        
        <!-- 其餘 -->
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>
        
        <mapping resource="work/domain/Customer.hbm.xml"/>
    </session-factory>
</hibernate-configuration>


那麼,去哪裏看能配什麼,配哪些值呢?

在hibernate解壓包的project的etc下面有一個hibernate.properties,裏面有hibernate的核心配置信息。想使用什麼功能,ctrl+f查找一下便可。

這裏提醒一下,對於Hibernate5.3以前的版本,對於mysql方言,它或許可以支持下圖中顯示的方言。但要提醒的是,若是你使用了mysql5以上的版本而且Hibernate是5.3,那麼mysql方言你必須使用Mysql5XXXX(在原來的基礎上加個5)

除了在hibernate.properties中,咱們還能夠經過依賴包來查看。好比依賴包中的org.hibernate.dialect包含的就是方言的class文件,你看名字就可以瞭解有哪些方言了。


使用properties配置核心配置:

  • 命名要求:hibernate.properties
  • 配置選項:在xml的配置方法中,相信你已經瞭解了有哪些選項能夠配置了。xml版的和properties版的配置選項是同樣的。
  • 格式:相似hibernate.connection.driver_class=com.mysql.jdbc.Driver


核心配置文件的加載:

  • xml版本:使用new Configuration().configure()會自動查找文件名爲hibernate.cfg.xml的配置文件來加載配置信息。

  • properties版本: 使用new Configuration()來讀取hibernate.properties 中的配置信息,映射文件須要使用configure.addRrsource(文件路徑)來導入。


c3p0鏈接池的配置:

hibernate有本身的內置鏈接池,但不少時候,咱們會考慮使用別的鏈接池,c3p0就是經常使用的選擇。要使用c3p0鏈接池,也須要在覈心配置文件中進行配置,下面介紹怎麼配置。

1.首先須要配置hibernate的鏈接提供者,改爲由C3P0提供:

2.而後配置c3p0的選項:

<!-- 配置C3P0鏈接池 -->
<property  name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<!-- 最少可用鏈接數 -->
<property name="hibernate.c3p0.min_size">5</property>
<!-- 最大可用鏈接數 -->
<property name="hibernate.c3p0.max_size">20</property>
<!--設定數據庫鏈接的過時時間,以秒爲單位 -->
<property name="hibernate.c3p0.timeout">120</property>


注意,要記得導入c3p0的依賴包,它在hibernate解壓包的lib的optional的c3p0中。

補充:

  • 選項hibernate.XXX前面的hibernate.是能夠省去的。
  • 事務管理也須要核心配置文件的配置,這部分將在事務管理那裏再講。
  • 還有不少的配置,但都相對少用,有須要再去properties查找一下便可。



Hibernate映射文件配置


映射文件的結構大概以下:


怎麼配置映射文件:

  • 映射文件名要求:類名.hbm.xml,例如Person.hbm.xml

  • 標準xml首部:

  • xml約束:hibernate的映射文件使用的是she對於映射文件,它的dtd能夠在依賴包hibernate-core-5.3.0.Final.jar下的org.hibernate下的hibernate-mapping-3.0.dtd的註釋中中獲取。(hibernate5與hibernate3的類似,一般使用hibernate-mapping-3.0.dtd的)。

<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
  • 配置選項:
    • hibernate-mapping 標籤:根標籤,class標籤是它的子標籤
    • class標籤:用來定義持久類與數據表的映射關係。
      • 屬性:
        • name:對應的持久類的全路徑 【全限定名,就是用.劃分的路徑】
        • table:對應的數據表的名字
        • catalog:數據庫名【比較少用】
      • 常見子標籤:id,property,set(涉及一對多,多對多),many-to-one(涉及一對多)
    • id標籤:用來創建類中的惟一標識屬性(OID)與表中的主鍵的對應關係,OID能夠惟一標識每個持久類類對象。
      • 屬性:
        • name:對應的持久類的惟一標識屬性的名字
        • column:對於表中的主鍵名【若是表中的主鍵與持久類中的惟一標識屬性同名,那麼能夠省去column】
        • length:定義數據類型的長度
        • type:字段的數據類型
      • 常見子標籤:generator(用來定義主鍵生成策略,會在下面具體講。)
    • property標籤:用來創建類中的普通屬性與表的字段的對應關係
      • 屬性:
        • name:對應的持久類的屬性名
        • column:對於表中的字段名【若是表中的字段名與持久類中的屬性同名,那麼能夠省去column】
        • length:定義數據類型的長度
        • type:字段的數據類型【type和length主要是用來定義字段的屬性的,若是你不關心字段的屬性,只須要映射關係,那麼能夠省去。】【若是定義了type和length,那麼會根據策略來判斷是否去更改表結構;沒有表的時候,會根據這些屬性來建立新表;若是沒表也沒有這些關於字段的屬性的屬性,那麼會使用默認值。好比varchar長度會達到255】
        • not-null:字段是否爲空。【也是用來定義字段的屬性的,能夠不用】
        • unique:字段是否有惟一屬性。【也是用來定義字段的屬性的,能夠不用】


主鍵生成策略:

<id name="pid">
    <generator class="native"/>
</id>

id標籤用來配置主鍵,generator標籤是id標籤的子標籤,它是用來配置主鍵生成策略的(主鍵的值的策略),generator的class的值經常使用的有以下幾個:

  • increment:使用Hibernate自增加機制來生成主鍵。使用這個屬性以後,使用自動建表,表會有主鍵屬性。但它是線程不安全的,它的原理是獲取數據庫中主鍵當前最大值來肯定新主鍵的值。因此它只能用於單線程。
  • identity: 使用數據庫自帶的自增加機制,適用於DB二、SQL Server、MySQL、Sybase。
  • sequence:使用數據庫自帶的自增加機制,適用於oralce、DB、SAP DB、PostgerSQL。
  • uuid:隨機生成字符串做爲主鍵的值。
  • native:使用數據庫自帶的自增加機制,會根據不一樣的數據庫來採用identity或sequence。
  • assigned:放棄管理,由本身調用setter函數給主鍵設置值。
  • foreign:使用關聯表的主鍵值做爲主鍵的值。主要用於一對一關係中。【這個這裏不講,由於太少用了】

補充:

  • 映射文件的配置中還有一個重頭戲-- 一對多、多對多關係的配置,由於這是一個重點內容,因此分到下面的關係映射中來說。


Hibernate與數據庫的鏈接


  • 上面已經講述了配置核心配置文件,配置映射文件,也就是說基本的準備已經作好了,接下來的內容就是Hibernate利用這些配置來與數據庫進行交互了。
  • 這小節主要講Hibernate與數據庫的鏈接,下一節纔會講到操做數據表。
  • 要了解Hibernate與數據庫的鏈接的,首先要了解如下幾個類。


Configuration:

  • 做用:它負責加載核心配置文件,在上面的核心配置文件中有講到核心配置文件的加載,這裏再次提一下。
  • 使用:
    • 對於xml版的核心配置文件:只須要Configuration cfg = new Configuration().configure() 返回的是讀取了hibernate.cfg.xml配置的Configuration對象
    • 對於properties版的核心配置文件:因爲properties版的沒法導入映射文件,因此須要加載配置:Configuration cfg = new Configuration(),而後再導入映射文件:configuration.addResource("work/domain/Customer.hbm.xml");


SessionFactory:

  • 做用:加載了核心配置文件以後,就須要進行hibernate與數據庫的鏈接了,SessionFactory對象在不少時候均可以直接認爲是一個鏈接池對象(主要功能)。它能夠由Configuration類對象調用buildSessionFactory方法來獲取。它調用openSession方法能夠建立一個Session對象(至關於數據庫鏈接會話對象)。
  • 使用:
    • 獲取 SessionFactory:SessionFactory factory = configure.buildSessionFactory()
    • 建立Session:Session session=factory.openSession()


Session:

  • 做用:SessionFactory對象至關於一個鏈接池對象,那麼能夠從它那裏獲取鏈接,返回的是一個Session對象,Session對象就至關於jdbc中的Connection對象,咱們可使用Session對象來操做數據庫。
  • 使用:
    • 建立Session:Session session = factory.openSession();
    • session有不少操做數據表的方法,如何使用session來操做數據庫,咱們下面再講。這裏主要講如何進行鏈接。


Transaction:

  • 做用:Hibernate的增刪改操做須要事務管理,Transaction是事務管理對象,它能夠經過Session對象來獲取。
  • 使用:
    • 獲取:Transaction transaction = session.beginTransaction();【這樣就爲這個session開啓了事務】
    • 提交事務:transaction.commit();
    • 回滾事務:transaction.rollback();


瞭解上面對象的做用與使用以後,咱們就能夠得出如下與數據庫創建鏈接並交互的步驟:



Hibernate操做數據表之增刪查改CRUD



瞭解了怎麼創建與數據庫的鏈接以後,下面就是CRUD的內容了。

【不要忘了!Hibernate的增刪改須要事務管理】

新增:

1.建立持久類對象,給持久類對象設置屬性。

2.session對象調用save(Object obj)來保存到數據庫,對象的數據會根據映射關係存儲到對應的數據表中。


查詢:

查詢是一個很重要的內容,考慮到這篇博文已經很長了的,因此我留到另一篇博文去講述了。

詳情點擊下面超連接。查詢


修改:

修改有兩種方法:
1.建立一個對象,給對象的惟一標識屬性(OID)賦予一個值,給各個屬性賦值,而後session.update(這個對象)【這樣會把OID對應的數據庫記錄的全部字段都改爲對象的對應屬性,因此若是不正確賦值的話,數據庫中字段的值會出錯。】

public void test1() {
        Configuration cfg = new Configuration().configure();
        SessionFactory factory = cfg.buildSessionFactory();
        Session session = factory.openSession();
        Transaction transaction = session.beginTransaction();
        Person person = new Person();
        person.setId(1L);
        person.setUsername("葫蘆娃");
        session.update(person);
        //這樣的缺點是沒有set的屬性值都是null,並會寫到數據表中
        transaction.commit();
        session.close();
    }

2.先查詢,再修改【推薦操做,查詢出來後,對象中存儲着原有的屬性】:先查詢出對象,修改好對象的指定屬性,而後session.update(這個對象)
【這裏給的例子使用的查詢是OID查詢,更多查詢方法請查看"查詢"那裏的用法】

public void test1() {
        Configuration cfg = new Configuration().configure();
        SessionFactory factory = cfg.buildSessionFactory();
        Session session = factory.openSession();
        Transaction transaction = session.beginTransaction();
        Person person = session.get(Person.class, 2L);
        person.setUsername("葫蘆娃");
        session.update(person);
        transaction.commit();
        session.close();
    }


刪除:

刪除有兩種方法:
1.建立一個對象,給對象的惟一標識屬性賦予一個值,而後session.delete(這個對象)
2.先查詢,再刪除:先查詢出對象,而後session.delete(這個對象)【推薦先查詢,再刪除,若是涉及到表與表之間的關聯(外鍵),那麼須要先查詢再刪除,否則查找不到外鍵,從而沒法級聯處理,這個後面講

【這裏給的例子使用的查詢是OID查詢,更多查詢方法請查看"查詢"那裏的用法】

public void test2() {
        Configuration cfg = new Configuration().configure();
        SessionFactory factory = cfg.buildSessionFactory();
        Session session = factory.openSession();
        Transaction transaction = session.beginTransaction();
        Person person = session.get(Person.class, 2L);
        session.delete(person);
        transaction.commit();
        session.close();
    }



緩存


緩存:是一種優化的方式,將數據存入到內存中,內存要比硬盤快,普通的查詢是查詢存儲在硬盤中的數據庫數據,使用緩存後能夠直接從緩存中獲取以前存儲下來的數據,不須要從數據源中從新獲取。


  • session管理着Hibernate的一級緩存。
    • 緩存區中有一塊特殊的區域--快照區,緩存區存儲着「本地對象」,快照區存儲着"查詢到的對象"。
    • 緩存區是用來存儲被session管理的持久類對象的(剛剛查詢得來的對象、新建立的並經過session保存了的對象都會保存到緩存區),一般來講都是處於「持久態」的對象;
    • 而快照區存儲的是查詢來的對象,主要用於校驗本地對象的數據跟數據表是否一致。查詢到的對象是能夠修改的,修改的是緩存區中的對象,但快照區只會存儲上次查詢結果。
    • 一個新建的對象是不會存儲到緩存區的,只有它被session管理了,纔會存儲到緩存區中,而且不會存儲到快照區中。


緩存對效率的影響:

  • 查詢一個持久類對象時,若是這個持久類對象緩存區已經有了,那麼不會再發送SQL語句,而是從緩存區中獲取對象,避免了重複發送SQL。
  • 修改一個持久類對象後,若是緩存區的對象與快照區的對象的數據不同,那麼會自動發SQL語句來更新到數據表中,這樣不須要顯式update。


上面瞭解了緩存區以後,有提到存儲到緩存區的一般都是持久態對象,下面瞭解一下持久類對象的三種狀態。

持久化類對象的三種狀態:


  • 瞬時態:新建的持久類,惟一標識屬性(OID)沒有值的時候,沒有被session管理的時候。

  • 持久態:惟一標識屬性有值,被session管理的時候(要麼是用session剛從數據庫中查詢出來的;要麼是已經使用過session保存到數據庫中,而且尚未被刪除的。)。持久態的對象會存儲到緩存區中,用來提升效率。

  • 脫管態:惟一標識屬性有值,不被session管理的時候。

  • 持久類狀態轉換圖:



事務管理


事務的開啓、提交與回滾:

  • 開啓事務:Transaction transaction = session.beginTransaction();

  • 事務提交:transaction.commit();

  • 事務回滾:transaction.rollback();

public void test1() {
        Configuration configuration = new Configuration().configure();
        SessionFactory factory = configuration.buildSessionFactory();
        Session session = factory.openSession();
        Transaction transaction = session.beginTransaction();
        Account account = session.get(Account.class, 1L);
        Account account2 = session.get(Account.class, 2L);
        try {
            account.setMoney(account.getMoney()-100);
            account2.setMoney(account2.getMoney()+100);
            transaction.commit();//不發生異常的時候,提交
        }catch (Exception e) {
            transaction.rollback();//發生異常,回滾
        }finally {
            session.close();
        }
        
    }


事務隔離級別配置:

事務隔離級別配置要在覈心配置文件中配置:

<property name="hibernate.connection.isolation">4</property>

值能夠爲一下四個:【左邊是值,右邊是級別,值表明的級別的意義就不說了,是sql那邊的內容。】

1:Read Uncommitted
2:Read Committed
4:Repeatable Read
8:Serializable


不一樣函數中的事務管理:

咱們從上面學會了怎麼使用事務--用session來開啓。但你可能遭遇兩個「動做」不在一個session中的狀況,這種狀況是很常常見的,由於分工越細效率越高。例如銀行的轉錢操做,這種須要一方加錢,一方減錢,一般來講咱們不會把加錢減錢都定義到一個函數中,而是把他們分開,這樣別的狀況須要加錢時就能夠直接調用函數了。而不一樣的session之間的事務是隔離的,這時候兩個操做處於不一樣session的話就沒辦法進行統一的事務管理了。

@Test
    public void test2() {
        Configuration configuration = new Configuration().configure();
        SessionFactory factory = configuration.buildSessionFactory();
        Session session = factory.openSession();
        Transaction transaction = session.beginTransaction();
        Account account = session.get(Account.class, 1L);
        Account account2 = session.get(Account.class, 2L);
        reduceMondy(account,100.0);
        addMoney(account2,100.0);
        transaction.commit();//不發生異常的時候,提交
        session.close()
    }

    public void reduceMondy(Account account, double d) {
        Configuration configuration = new Configuration().configure();
        SessionFactory factory = configuration.buildSessionFactory();
        Session session = factory.openSession();
        Transaction transaction = session.beginTransaction();
        account.setMoney(account.getMoney()-d);
        session.update(account);
        transaction.commit();
        session.close()
    }
    public void addMoney(Account account, double d) {
        Configuration configuration = new Configuration().configure();
        SessionFactory factory = configuration.buildSessionFactory();
        Session session = factory.openSession();
        Transaction transaction = session.beginTransaction();
        account.setMoney(account.getMoney()+d);
        int a=10/0;//此次出一下異常,測試二者的事務是不一樣的,結果,一方錢少了,一方沒加錢
        session.update(account);
        transaction.commit();
        session.close()
    }


你可能會想到把某個session做爲形參傳進去,這樣兩個函數用的就是一個session了,這是一個解決方法。

但Hibernate提供了一個更好的解決方案。咱們可使用getCurrentSession來獲取session(須要在覈心配置文件中配置,下面將如何配置),getCurrentSession是經過當前線程來獲取一個session,當調用getCurrentSession來獲取session時,Hibernate會把這個session存儲到線程中,其餘函數調用getCurrentSession來獲取session時,獲取的session是同一個,因此同一個線程執行不一樣的函數依然能夠很好的進行事務管理。

@Test
    public void test2() {
        Configuration configuration = new Configuration().configure();
        SessionFactory factory = configuration.buildSessionFactory();
        Session session = factory.getCurrentSession();
        Transaction transaction = session.beginTransaction();
        Account account = session.get(Account.class, 1L);
        Account account2 = session.get(Account.class, 2L);
        try {
            reduceMondy(account,100.0);
            addMoney(account2,100.0);
            transaction.commit();
        }
        catch (Exception e) {
            transaction.rollback();//發生異常,回滾
        }
    }

    public void reduceMondy(Account account, double d) {
        Configuration configuration = new Configuration().configure();
        SessionFactory factory = configuration.buildSessionFactory();
        Session session = factory.getCurrentSession();
        //當使用同一個session時,事務的開啓應該在調用這個兩個函數的地方開啓
        account.setMoney(account.getMoney()-d);
        session.update(account);
    }
    public void addMoney(Account account, double d) {
        Configuration configuration = new Configuration().configure();
        SessionFactory factory = configuration.buildSessionFactory();
        Session session = factory.getCurrentSession();//從線程中獲取session
        //當使用同一個session時,事務的開啓應該在調用這個兩個函數的地方開啓
        account.setMoney(account.getMoney()+d);
        int a=10/0;//此次出一下異常,測試二者的事務是不一樣的
        session.update(account);
    }

因此不少時候,factory.getCurrentSession()取代了Session session = factory.openSession()。

getCurrentSession的正常使用須要核心配置文件加上一個配置選項:<property name="hibernate.current_session_context_class">thread</property>

採用getCurrentSession()建立的Session在commit或rollback後會自動關閉,而採用OpenSession()必須手動關閉。


關係映射:



在上面的基礎示例中,演示的是一個單表的例子。而事實上,項目中的表與表之間是有關係的。而且咱們常常須要利用這些關係來獲取數據。

一對多關係:

【例子演示以班級與學生之間的一對多關係(假設一個班級能夠有多個學生,一個學生只能有一個班級)爲例】

「一」方的配置:

  • 在「一」的一方的持久類中須要增長一個「多」一方持久類組成的集合(這裏的集合最好提早new一下,省得後面再去賦值,new了以後,後面就能夠直接get來add了)。

  • 在數據庫一對多關係管理中,一般在多的一方的表中建立外鍵。因此就算持久類多了一個集合,也不會映射到數據表中。這個集合主要是用來幫助Hibernate處理它們之間的關係的,不會在表中生成新的字段。
  • 建立「一」一方的映射關係文件,除了基本字段關聯以外,還使用set標籤來定義關係。在set標籤中,name屬性是集合對象的變量名,子標籤key中的column屬性是「多」一方的外鍵字段名。子標籤one-to-many的class屬性是「多」一方的類的全路徑。

「多」方的配置

  • 在多的一方的持久類中增長一個「一」一方的持久類對象,多出來的「一」一方的類的對象是用來映射成外鍵字段的(對象跟外鍵的映射關係須要在映射文件中配置),從面向對象來講就是利用對象的包含關係來映射多對一的關係。(在數據表中咱們使用外鍵來定義一對多的關係,在類中,咱們使用對象包含對象來定義一對多的關係。這個O類相對於M類來講就是M類對應表的外鍵)

  • 在數據庫一對多關係管理中,一般在多的一方的表中建立外鍵。因此上面增長的「一」一方的對象會映射成外鍵(這個映射規則須要定義,下面講)。
  • 建立「多」一方的映射關係文件,除了基本字段關聯以外,還使用many-to-one標籤來定義關係。many-to-one標籤中,name屬性是那個M中存儲的O的對象的變量名,class屬性是O的類的全路徑,column屬性是「一」一方的對象要映射成的外鍵的字段名(這個字段名會出如今「多」一方的表中,並使用「一」一方的主鍵值做爲值)。


其餘配置與測試類

  • 在hibernate.cfg.xml中導入映射文件

  • 編寫測試類,將一一方的對象和多一方的對象互相創建上關係(把一一方的對象設置到多一方的對象的屬性中,把多一方的對象添加到一一方的集合中),而後使用save保存起來。

    建立兩個學生(李白、杜甫)、一個班級(唐朝);給學生的grade屬性設置爲班級對象並將兩個學生加入到班級對象的學生集合中;把三個對象都保存起來。

public void test3() {
            Configuration configure = new Configuration().configure();
            SessionFactory factory = configure.buildSessionFactory();
            Session session = factory.openSession();
            Transaction transaction = session.beginTransaction();
            //建立兩個學生一個班級
            Student s1=new Student();
            Student s2=new Student();
            s1.setName("李白");
            s2.setName("杜甫");
            Grade g=new Grade();
            g.setCname("唐朝");
            //設置關係
            s1.setGrade(g);
            s2.setGrade(g);
            g.getStudents().add(s1);
            g.getStudents().add(s2);
            //保存
            session.save(s1);
            session.save(s2);
            session.save(g);
            transaction.commit();
        }

結果:

結果顯示了它們創建的一對多的關係,操做一方對象會給外鍵字段字段賦值。


補充

  • 上面說的一一方的那個集合不會映射到數據表中,僅僅做爲Hibernate關係管理依據,若是這個關係你不怎麼用到的話能夠不配置,若是查詢中常常要用到經過多一方查詢一一方,而不使用經過一一方查詢多一方的話,那麼這個關係就是沒有用處的,這時候一一方的關係能夠不配置,僅僅配置多一方的。【想了解更詳細,能夠了解一下這個:https://blog.csdn.net/u011781521/article/details/71172874】


多對多關係:

(假設一方爲A,一方爲B)

1.建立A的持久類,裏面有一個B類對象組成的集合【例子演示以一個商品能夠屬於多個購物車,一個購物車能夠有多個商品爲例。】
建立購物車類ShopCar:


2.建立B的持久類,裏面有一個A類對象組成的集合,
建立產品類Product:


3.建立A的hbm,使用property配置好基本屬性,使用set配置配置集合:set中的name的值是自身存儲的對方對象的集合的變量名,table是中間表的名稱(自動建立);key中的column的值是自身在中間表映射的外鍵字段名;many-to-many中class的值是對方類的全路徑,column的值是對方類在中間表映射的外鍵字段名。
建立ShopCar.hbm.xml:


4.建立B的hbm,像第三步同樣配置好

建立Product.hbm.xml:



5.在hernate.cfg.xml中導入映射文件

6.編寫測試類,將一個A類對象與多個B類對象創建關聯,將一個B類對象與多個A類對象創建關聯,把全部A和B的對象保存,查看中間表的數據。

編寫測試類,創建兩個Product,創建兩個ShopCar,讓他們之間相互都創建起關聯。

public void test4() {
        Configuration configure = new Configuration().configure();
        SessionFactory factory = configure.buildSessionFactory();
        Session session = factory.openSession();
        Transaction transaction = session.beginTransaction();
        //新建兩個訂單,兩個產品
        ShopCar o1=new ShopCar();
        ShopCar o2=new ShopCar();
        o1.setCustomer("馬雲");
        o2.setCustomer("馬化騰");
        Product p1=new Product();
        Product p2=new Product();
        p1.setProduct_name("聚寶盆");
        p2.setProduct_name("招財貓");
        //相互創建關係
        o1.getProducts().add(p1);
        o2.getProducts().add(p1);
        o1.getProducts().add(p2);
        o2.getProducts().add(p2);
        //保存
        session.save(o1);
        session.save(o2);
        session.save(p1);
        session.save(p2);
        transaction.commit();
    }

結果:



級聯操做:


  • 什麼是級聯操做?當表之間有關係的時候,對一個表作操做(刪除、修改),會對應去更改關聯的表(例如修改班級的id時會對應去修改學生記錄中的班級id)。
  • 【注意!這裏的關係相對於表而言,但對於對象來講,是對象中是否存儲着另外一方的對象(固然還須要映射關係文件的幫助),因此只有在對象上關聯上的纔會級聯,沒有關聯上的不會級聯】(這就是爲何上面的修改和刪除要先查詢再操做的緣由)

一對多的級聯操做設置:

  • 級聯操做由cascade屬性影響。
    • save-update 【級聯保存或修改】
      • many-to-one中配置了,那麼"多"一方的對象保存時,會同時保存創建了關係的"一"一方的對象。
      • set中配置了,那麼"一"一方的對象保存時,會同時保存創建了關係的"多"一方的對象。
    • delete【級聯刪除】
      • 這個比較少有與多對多,一般使用於一對多中的「一」一方(一個球隊解散了,就刪除對應的球員信息)。
  • 一一方的cascade出如今set標籤中。
  • 多一方的cascade出如今many-to-one標籤中。
<many-to-one name="grade" class="work.domain.Grade"  column="gid" cascade="save-update"></many-to-one>

多對多的級聯操做設置:

  • cascade出如今set中的cascade
  • set中配置了save-update,那麼一方的對象保存時,會同時保存創建了關係另外一方的對象。
<set name="shopcars" table="shopcar_product" cascade="save-update">
            <key column="product_id"></key>
            <many-to-many class="work.domain.ShopCar" column="shopcar_id"></many-to-many>
</set>


示例:

在學生hbm的many-to-one中加上cascade="save-update":那麼只保存學生對象,也能夠同時保存學生對象所對應的班級對象。

<many-to-one name="grade" class="work.domain.Grade"  column="gid" cascade="save-update"></many-to-one>
public void test5() {
            Configuration configure = new Configuration().configure();
            SessionFactory factory = configure.buildSessionFactory();
            Session session = factory.openSession();
            Transaction transaction = session.beginTransaction();
            //建立兩個學生一個班級
            Student s1=new Student();
            s1.setName("林則徐");
            Grade g=new Grade();
            g.setCname("清朝");
            //設置關係
            s1.setGrade(g);
            g.getStudents().add(s1);
            //保存
            session.save(s1);//只保存學生,能夠同時保存這個學生對象所關聯的班級對象
            transaction.commit();
        }


放棄外鍵維護問題:

對於一對多級聯操做(假設「一」一方爲A,「多」一方爲B),可能會產生多餘SQL語句,緣由是緩存區中A,B的數據都發生了變化了,使得不與快照區的數據映像相同,Hibernate默認就會對更新了的對象進行修改處理。

  • 對象是新建的時候,不涉及到快照區,不會發生上述問題;
  • 但在修改外鍵的時候就會發生,好比先查詢出來一個A,兩個B(因爲處於持久態,這時候三個對象的數據會存儲到緩存區和快照區),A本來僅僅與B1關聯,如今增長了與B2的關聯,那麼這時候緩存區中的A對象發生了數據更改,B2對象也發生了更改。在提交的時候,校驗緩存區和快照區的數據發現緩存區的A和B2與快照區的數據不一樣了,這時候hibernate會發起對A的數據更新(外鍵新增)以及對B2的數據更新(外鍵新增)。因此發起了兩次外鍵新增,這就產生了多餘的SQL語句。【而多對多中,Hibernate考慮到若是兩個都有的話,會在中間表插入重複的,因此多對多不會發生這個問題(測試過!)】

  • 解決方案:inverse是Hibernate定義的一種維護外鍵的屬性,當這個屬性設置爲true時,那麼設置的一方會放棄外鍵維護權(表明外鍵更新了它也不會去管理),對於一對多,一般都是在set上配置inverse=」true」,讓多的一方去維護外鍵 。



日誌


若是你已經導入前面說講的那幾個日誌包了的話,那麼你還須要一個log4j.properties就能夠配置好你的日誌功能了。Hibernate須要日誌來打印一些數據。

日誌包:

  • slf4j-api-1.6.1.jar【這是Hibernate的日誌接口,若是要擴展日誌功能,須要這個接口】【這個能夠在lib\spatial中找到】
  • slf4j-log4j12-1.7.2.jar【咱們這裏要使用log4j來實現日誌功能,但slf4j不能直接到log4j,它須要轉換器,這個就是轉換器】【這個須要你本身下載】
  • log4j-1.2.6.jar【這個也須要你本身下載】

至於log4j.properties怎麼寫,project/etc中有一個log4j.properties,能夠參照那個來寫。

#
# Hibernate, Relational Persistence for Idiomatic Java
#
# License: GNU Lesser General Public License (LGPL), version 2.1 or later.
# See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
#

### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### direct messages to file hibernate.log ###
#log4j.appender.file=org.apache.log4j.FileAppender
#log4j.appender.file.File=hibernate.log
#log4j.appender.file.layout=org.apache.log4j.PatternLayout
#log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### set log levels - for more verbose logging change 'info' to 'debug' ###

log4j.rootLogger=warn, stdout

#log4j.logger.org.hibernate=info
log4j.logger.org.hibernate=debug

### log HQL query parser activity
#log4j.logger.org.hibernate.hql.ast.AST=debug

### log just the SQL
#log4j.logger.org.hibernate.SQL=debug

### log JDBC bind parameters ###
log4j.logger.org.hibernate.type=info
#log4j.logger.org.hibernate.type=debug

### log schema export/update ###
log4j.logger.org.hibernate.tool.hbm2ddl=debug

### log HQL parse trees
#log4j.logger.org.hibernate.hql=debug

### log cache activity ###
#log4j.logger.org.hibernate.cache=debug

### log transaction activity
#log4j.logger.org.hibernate.transaction=debug

### log JDBC resource acquisition
#log4j.logger.org.hibernate.jdbc=debug

### enable the following line if you want to track down connection ###
### leakages when using DriverManagerConnectionProvider ###
#log4j.logger.org.hibernate.connection.DriverManagerConnectionProvider=trace



寫在最後:Hibernate畢竟是一個框架,市面上的書寫它都能寫它幾百頁,在一篇博文內不可能把內容寫得詳盡。因此這篇博文很大程度上主要是你讓你瞭解Hibernate,剩下的就要你本身去了解了。獲取百度纔是你最好的老師? 這篇博文斷斷續續寫了三天,不少時候都是在想以什麼一種方式來說述才能更好地讓你們理解。最終成了這個樣子,但願能幫到你們。

相關文章
相關標籤/搜索