hibernate主键增长increment与native的区别

本文探讨了Hibernate中increment和native两种主键生成策略。increment通过内存中增加来生成主键,适合单一进程但不适合集群环境。native则根据数据库类型自动选择identity、sequence等方式,需要设置自增字段或序列,适用于多数据库环境。删除数据后,native策略不会填充主键空缺,而increment会基于现有最大值生成。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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了。


这是自己第一次写的原创博客,虽然写的很渣,但是还是记录自己在学习中的一些经历,,,原谅我不由自主的自勉,哈哈~

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值