1. 程式人生 > >Hibernate(2)——IDEA+maven+hibernate主鍵生成策略

Hibernate(2)——IDEA+maven+hibernate主鍵生成策略

  • Maven版本hibernate程式

    Pom.xml

<dependencies>

      <dependency>

          <groupId>org.hibernate</groupId>

          <artifactId>hibernate

-core</artifactId>

          <version>4.3.11.Final</version>

      </dependency>

      <dependency>

          <groupId>mysql</

groupId>

          <artifactId>mysql-connector-java</artifactId>

          <version>5.1.44</version>

      </dependency>

      <

dependency>

          <groupId>junit</groupId>

          <artifactId>junit</artifactId>

          <version>4.12</version>

      </dependency>

   </dependencies>

其他的程式碼都複製hibernate01中的程式碼即可。只是junit測試,test檔案需做修改——Hibernate(1)——簡單、快速上手

junit測試的test檔案

package com.kxt.test;

import com.kxt.model.User;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.junit.Before;
import org.junit.Test;

/**
 * @author sikai
 * @create 2018 - 09 - 26 10:26
 */
public class TestHibernate {
    SessionFactory sessionFactory = null;
    Session session = null;


    @Before
    public void init(){
//        1.載入配置檔案
        Configuration cfg = new Configuration();
        cfg.configure("hibernate.cfg.xml");

//        2.建立sessionFactory  cfg.buildSessionFactory()已被廢棄
        ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(cfg.getProperties()).build();
        sessionFactory = cfg.buildSessionFactory(serviceRegistry);

//        3.開啟全新session
        session = sessionFactory.openSession();
    }

    @Test
    public void testUserSave(){
        try {
            //事務
            Transaction ts = session.beginTransaction();

            User user = new User();
            user.setUname("zhangzhantao");

            //新增資料
            session.save(user);

            //提交事務
            ts.commit();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public void close(){
        //關閉連結
        if (session!=null){
            session.close();
        }
        if (sessionFactory!=null){
            sessionFactory.close();
        }
    }



}

Maven專案執行報錯:

Java下面的對映檔案找不到,測試不通過。

因為對於Maven工程,編譯的工作是由Maven程式來完成的,而Maven預設只會把src/main/resources資料夾下的檔案拷貝到target/classes資料夾下,所以上圖的.hbm.xml都不會被複制到/target/calsses資料夾下,所以Hibernate框架在執行的時候,就會報找不到*.hbm.xml的錯誤。

解決方案:

在pom.xml中顯式地告訴Maven把什麼資原始檔複製到target/classes資料夾下。

<build>

    <resources>
        <resource>
            <directory>
src/main/java</directory>
            <includes>
                <include>
**/*.xml</include>
            </includes>
            <filtering>
true</filtering>
        </resource>
        <resource>
            <directory>
src/main/resources</directory>
            <includes>
                <include>
**/*.xml</include>
                <include>
**/*.properties</include>
            </includes>
        </resource>
    </resources>
</build>

然後我們再編譯執行,就測試通過了:

  • Hibernate主鍵生成策略

  • assigned-掌握

主鍵由外部程式負責生成,在 save() 之前必須指定一個。Hibernate不負責維護主鍵生成。與Hibernate和底層資料庫都無關,可以跨資料庫。在儲存物件前,必須要使用主鍵的setter方法給主鍵賦值,至於這個值怎麼生成,完全由自己決定,這種方法應該儘量避免

<id name="id" column="id">

<generator class="assigned" />

</id>

特點:可以跨資料庫,人為控制主鍵生成,應儘量避免。

increment-重點

由Hibernate從資料庫中取出主鍵的最大值(每個session只取1次),以該值為基礎,每次增量為1,在記憶體中生成主鍵,不依賴於底層的資料庫,因此可以跨資料庫。

<id name="id" column="id">

<generator class="increment" />

</id>

Hibernate呼叫org.hibernate.id.IncrementGenerator類裡面的generate()方法,使用select max(idColumnName) from tableName語句獲取主鍵最大值。該方法被宣告成了synchronized,所以在一個獨立的Java虛擬機器內部是沒有問題的,然而,在多個JVM同時併發訪問資料庫select max時就可能取出相同的值,再insert就會發生Dumplicate entry的錯誤。所以只能有一個Hibernate應用程序訪問資料庫,否則就可能產生主鍵衝突,所以不適合多程序併發更新資料庫,適合單一程序訪問資料庫,不能用於群集環境。

官方文件:只有在沒有其他程序往同一張表中插入資料時才能使用,在叢集下不要使用。

特點:跨資料庫,不適合多程序併發更新資料庫,適合單一程序訪問資料庫,不能用於群集環境。

hilo-掌握

hilo(高低位方式high low)是hibernate中最常用的一種生成方式,需要一張額外的表儲存hi的值。儲存hi值的表至少有一條記錄(只與第一條記錄有關),否則會出現錯誤。可以跨資料庫。

<id name="id" column="id">

<generator class="hilo">

<param name="table">hibernate_hilo</param>

<param name="column">next_hi</param>

<param name="max_lo">100</param>

</generator>

</id>

<param name="table">hibernate_hilo</param> 指定儲存hi值的表名

<param name="column">next_hi</param> 指定儲存hi值的列名

<param name="max_lo">100</param> 指定低位的最大值

也可以省略table和column配置,其預設的表為hibernate_unique_key,列為next_hi

<id name="id" column="id">

<generator class="hilo">

<param name="max_lo">100</param>

</generator>

</id>

hilo生成器生成主鍵的過程(以hibernate_hilo表,next_hi列為例):

1. 獲得hi值:讀取並記錄資料庫的hibernate_hilo表中next_hi欄位的值,資料庫中此欄位值加1儲存。

2. 獲得lo值:從0到max_lo迴圈取值,差值為1,當值為max_lo值時,重新獲取hi值,然後lo值繼續從0到max_lo迴圈。

3. 根據公式 hi * (max_lo + 1) 計算生成主鍵值。

注意:當hi值是0的時候,那麼第一個值不是0*(max_lo+1)+0=0,而是lo跳過0從1開始,直接是1、2、3……

那max_lo配置多大合適呢?

這要根據具體情況而定,如果系統一般不重啟,而且需要用此表建立大量的主鍵,可以吧max_lo配置大一點,這樣可以減少讀取資料表的次數,提高效率;反之,如果伺服器經常重啟,可以吧max_lo配置小一點,可以避免每次重啟主鍵之間的間隔太大,造成主鍵值主鍵不連貫。

特點:跨資料庫,hilo演算法生成的標誌只能在一個數據庫中保證唯一。

seqhilo-掌握

與hilo類似,通過hi/lo演算法實現的主鍵生成機制,只是將hilo中的資料表換成了序列sequence,需要資料庫中先建立sequence,適用於支援sequence的資料庫,如Oracle。

<id name="id" column="id">

<generator class="seqhilo">

<param name="sequence">hibernate_seq</param>

<param name="max_lo">100</param>

</generator>

</id>

 

特點:與hilo類似,只能在支援序列的資料庫中使用。

sequence-重點

採用資料庫提供的sequence機制生成主鍵,需要資料庫支援sequence。如oralce、DB、SAP DB、PostgerSQL、McKoi中的sequence。MySQL這種不支援sequence的資料庫則不行(可以使用identity)。

<generator class="sequence">

<param name="sequence">hibernate_id</param>

</generator>

<param name="sequence">hibernate_id</param> 指定sequence的名稱

Hibernate生成主鍵時,查詢sequence並賦給主鍵值,主鍵值由資料庫生成,Hibernate不負責維護,使用時必須先建立一個sequence,如果不指定sequence名稱,則使用Hibernate預設的sequence,名稱為hibernate_sequence,前提要在資料庫中建立該sequence。

特點:只能在支援序列的資料庫中使用,如Oracle。

identity-重點

identity由底層資料庫生成識別符號。identity是由資料庫自己生成的,但這個主鍵必須設定為自增長,使用identity的前提條件是底層資料庫支援自動增長欄位型別,如DB2、SQL Server、MySQL、Sybase和HypersonicSQL等,Oracle這類沒有自增欄位的則不支援。

<id name="id" column="id">

<generator class="identity" />

</id>

例:如果使用MySQL資料庫,則主鍵欄位必須設定成auto_increment。

id int(11) primary key auto_increment

特點:只能用在支援自動增長的欄位資料庫中使用,如MySQL。

​​​​​​​native-重點

native由hibernate根據使用的資料庫自行判斷採用identity、hilo、sequence其中一種作為主鍵生成方式,靈活性很強。如果能支援identity則使用identity,如果支援sequence則使用sequence。

<id name="id" column="id">

<generator class="native" />

</id>

例如MySQL使用identity,Oracle使用sequence

注意:如果Hibernate自動選擇sequence或者hilo,則所有的表的主鍵都會從Hibernate預設的sequence或hilo表中取。並且,有的資料庫對於預設情況主鍵生成測試的支援,效率並不是很高。

使用sequence或hilo時,可以加入引數,指定sequence名稱或hi值表名稱等,如

<param name="sequence">hibernate_id</param>

特點:根據資料庫自動選擇,專案中如果用到多個數據庫時,可以使用這種方式,使用時需要設定表的自增欄位或建立序列,建立表等。

​​​​​​​uuid-掌握

UUID:Universally Unique Identifier,是指在一臺機器上生成的數字,它保證對在同一時空中的所有機器都是唯一的。按照開放軟體基金會(OSF)制定的標準計算,用到了乙太網卡地址、納秒級時間、晶片ID碼和許多可能的數字,標準的UUID格式為:

xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx (8-4-4-4-12)

其中每個 x 是 0-9 或 a-f 範圍內的一個十六進位制的數字。

<id name="id" column="id">

<generator class="uuid" />

</id>

Hibernate在儲存物件時,生成一個UUID字串作為主鍵,保證了唯一性,但其並無任何業務邏輯意義,只能作為主鍵,唯一缺點長度較大,32位(Hibernate將UUID中間的“-”刪除了)的字串,佔用儲存空間大,但是有兩個很重要的優點,Hibernate在維護主鍵時,不用去資料庫查詢,從而提高效率,而且它是跨資料庫的,以後切換資料庫極其方便。

特點:uuid長度大,佔用空間大,跨資料庫,不用訪問資料庫就生成主鍵值,所以效率高且能保證唯一性,移植非常方便,推薦使用。

​​​​​​​guid-掌握

GUID:Globally Unique Identifier全球唯一識別符號,也稱作 UUID,是一個128位長的數字,用16進製表示。演算法的核心思想是結合機器的網絡卡、當地時間、一個隨即數來生成GUID。從理論上講,如果一臺機器每秒產生10000000個GUID,則可以保證(概率意義上)3240年不重複。

<id name="id" column="id">

<generator class="guid" />

</id>

Hibernate在維護主鍵時,先查詢資料庫,獲得一個uuid字串,該字串就是主鍵值,該值唯一,缺點長度較大,支援資料庫有限,優點同uuid,跨資料庫,但是仍然需要訪問資料庫。

注意:長度因資料庫不同而不同

MySQL中使用select uuid()語句獲得的為36位(包含標準格式的“-”)

Oracle中,使用select rawtohex(sys_guid()) from dual語句獲得的為32位(不包含“-”) 

特點:需要資料庫支援查詢uuid,生成時需要查詢資料庫,效率沒有uuid高,推薦使用uuid。

​​​​​​​foreign

使用另外一個相關聯的物件的主鍵作為該物件主鍵。主要用於一對一關係中。

<id name="id" column="id">

<generator class="foreign">

<param name="property">user</param>

</generator>

</id>

<one-to-one name="user" class="domain.User" constrained="true" />

該例使用domain.User的主鍵作為本類對映的主鍵。

特點:很少使用,大多用在一對一關係中。

​​​​​​​select

使用觸發器生成主鍵,主要用於早期的資料庫主鍵生成機制,能用到的地方非常少。