1. 程式人生 > >hibernate主鍵增長increment與native的區別

hibernate主鍵增長increment與native的區別

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應用程序訪問資料庫,否則就可能產生主鍵衝突,所以不適合多程序併發更新資料庫,適合單一程序訪問資料庫,不能用於群集環境。

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

package com.lxs.test;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.Transaction;

import com.lxs.domain.Department;
import com.lxs.domain.Emp;
import com.lxs.utils.HibernaetUtil;

public class Test {
	private Session session=null;
	private Transaction transaction=null;
	public static void main(String args[]) {
		new Test();
	}
	public Test(){	
		try {
			session=HibernaetUtil.getSession();
			transaction=session.beginTransaction();
			Emp emp=new Emp();
			emp.setName("部門1");
			Emp emp2=new Emp();
			emp2.setName("部門2");
			Emp emp3=new Emp();
			emp3.setName("部門3");
			session.save(emp);
			session.save(emp2);
			session.save(emp3);
			transaction.commit();		
		} catch (Exception e) {
			e.printStackTrace();
			// TODO: handle exception
		}finally{
			HibernaetUtil.colseSession();
		}
	}
}
對映檔案:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC 
      "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
      "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.lxs.domain">
	<class name="com.lxs.domain.Emp" table="emp">
	<id name="id" column="id" type="java.lang.Integer">
		<generator class="increment"/>
	</id>
	<property name="name" column="name" not-null="true" type="java.lang.String" length="20"></property>
	</class>

</hibernate-mapping>

執行結果:


這裡可以看到插入之前先是select max(id) from emp 最大值,然後將這個最大值快取起來,再插入的時候就直接從快取從拿出來再加一,但是隻是在一個session下有效,如果再執行,會發現還是會繼續查詢表主鍵的最大值的。

native

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

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

<generator class="native" />

</id>

例如MySQL使用identityOracle使用sequence

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

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

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

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

修改對映檔案,其他不變
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC 
      "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
      "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.lxs.domain">
	<class name="com.lxs.domain.Emp" table="emp">
	<id name="id" column="id" type="java.lang.Integer">
		<generator class="increment"/>
	</id>
	<property name="name" column="name" not-null="true" type="java.lang.String" length="20"></property>
	</class>

</hibernate-mapping>

執行結果:


這裡可以看到每次的插入語句都是沒有id的,也就是native把生成id交給資料庫取生成了,由資料庫自己去查詢主鍵的最大值,然後加進去的。

看到這裡心裡也明白了,為什麼我們使用native方式插入資料,如果資料庫之前已有資料被刪除了,mysql的主鍵增長並不會補充原有的空缺,而是還在之前的排序再加一,也就是這裡如果我刪除了50的資料,重新插入mysql資料庫是在51行繼續插入,而不是50,而是用native的方式就是交給資料庫去負責的,所以自然就出現這種情況了。

但是如果我們是使用increment的方式,這時插入會出現插入的位置是在刪除的50行上繼續插入


因為increment的方式插入是會去select max(id) from tb_name,那麼主鍵的最大值當然會是50了。

這是自己第一次寫的原創部落格,雖然寫的很渣,但是還是記錄自己在學習中的一些經歷,,,原諒我不由自主的自勉,哈哈~