Hibernate的ID生成原理

来源:http://www.cnblogs.com/firstdream/archive/2012/04/22/2464681.html

 

支持的策略包括:uuid, hilo, assigned, identity, select, sequence, seqhilo, increment, foreign, guid, uuid.hex, sequence-identity.

对应这些策略, 可以在org.hibernate.id包下找到, 其中有一个IdentifierGeneratorFactory类用于根据实体类的配置(hbm文件的<id>元素或@Id,@GeneratedValue注解)来创建相应的策略.

public final class IdentifierGeneratorFactory {
... //注册所有支持的ID生成策略
private static final HashMap GENERATORS = new HashMap();
static {
   GENERATORS.put( "uuid", UUIDHexGenerator.class );
   GENERATORS.put( "hilo", TableHiLoGenerator.class );
   GENERATORS.put( "assigned", Assigned.class );
   GENERATORS.put( "identity", IdentityGenerator.class );
   GENERATORS.put( "select", SelectGenerator.class );
   GENERATORS.put( "sequence", SequenceGenerator.class );
   GENERATORS.put( "seqhilo", SequenceHiLoGenerator.class );
   GENERATORS.put( "increment", IncrementGenerator.class );
   GENERATORS.put( "foreign", ForeignGenerator.class );
   GENERATORS.put( "guid", GUIDGenerator.class );
   GENERATORS.put( "uuid.hex", UUIDHexGenerator.class ); // uuid.hex is deprecated
   GENERATORS.put( "sequence-identity", SequenceIdentityGenerator.class );
}
...
public static IdentifierGenerator create(String strategy, Type type, Properties params, Dialect dialect)
    throws MappingException {
   try {
    Class clazz = getIdentifierGeneratorClass( strategy, dialect );
...
}

public static Class getIdentifierGeneratorClass(String strategy, Dialect dialect) {
   Class clazz = ( Class ) GENERATORS.get( strategy );
...
}
}
显然create()方法是用于创建ID生成器的, 而且用到了参数strategy和dialect. Hibernate在初始化SessionFactory的时候就会准备这些ID生成器. 见以下代码

SessionFactoryImpl(){
...
   Iterator classes = cfg.getClassMappings();
   while ( classes.hasNext() ) {
    PersistentClass model = (PersistentClass) classes.next();
    if ( !model.isInherited() ) {
     IdentifierGenerator generator = model.getIdentifier().createIdentifierGenerator(
       settings.getDialect(),
            settings.getDefaultCatalogName(),
            settings.getDefaultSchemaName(),
            (RootClass) model
      );
     identifierGenerators.put( model.getEntityName(), generator );
    }
   }

model.getIdentifier().createIdentifierGenerator() 最终会引用到
return IdentifierGeneratorFactory.create(
     identifierGeneratorStrategy,
     getType(),
     params,
     dialect
    );

看来SessionFactory做了太多的工作. 为了能对这么多中ID生成策略有最直观的了解, 下面给出一个各种ID策略的类关系图.

看Hibernate源码之 ID Generator - wolfgangkiefer - wolfgangkiefer的博客
 

该图分为两部分, 蓝色先左边的ID生成器是不需要借助数据库, 采用本地算法生成的, 而右边的ID生成器则需要借助数据库提供的ID生成能力.

最主要的接口有4个, 图中用蓝色标出.
* IdentifierGenerator: ID生成器接口, 只有一个generate方法, 返回生成的ID.
* PostInsertIdentifierGenerator: 针对MySQL, MSSQL这类能为主键设置自动增长的ID生成器
* PersistentIdentifierGenerator: 针对需要访问DB来生成ID的生成器, 比如GUID.
* Configurable: 用于读取配置信息

具体每个类如何处理:
1) uuid: 是采用128位的算法生成惟一值,支持大部分的数据库
   public Serializable generate(SessionImplementor session, Object obj) {
   return new StringBuffer(36)
    .append( format( getIP() ) ).append(sep)
    .append( format( getJVM() ) ).append(sep)
    .append( format( getHiTime() ) ).append(sep)
    .append( format( getLoTime() ) ).append(sep)
    .append( format( getCount() ) ) //注: 每次加1, JVM内唯一, 通过synchronized来保证实现
    .toString();
}
protected String format(int intval) {
   String formatted = Integer.toHexString(intval);
   StringBuffer buf = new StringBuffer("00000000");
   buf.replace( 8-formatted.length(), 8, formatted );
   return buf.toString();
}
protected String format(short shortval) {
   String formatted = Integer.toHexString(shortval);
   StringBuffer buf = new StringBuffer("0000");
   buf.replace( 4-formatted.length(), 4, formatted );
   return buf.toString();

2)GUID: 通过使用数据库本身的uuid算法来实现
public class GUIDGenerator implements IdentifierGenerator {
   public Serializable generate(SessionImplementor session, Object obj) 
   throws HibernateException {
       final String sql = session.getFactory().getDialect().getSelectGUIDString();
   ...
}
假如getDialect()返回的是MySQLDialect, 则返回的是
public String getSelectGUIDString() {
   return "select uuid()";
}
但是如果不支持uuid的数据库, 则抛出异常
public String getSelectGUIDString() {
   throw new UnsupportedOperationException( "dialect does not support GUIDs" );
}

3)increment:
public class IncrementGenerator implements IdentifierGenerator, Configurable {
... 
public synchronized Serializable generate(SessionImplementor session, Object object) 
throws HibernateException {

   if (sql!=null) {
    getNext( session ); //注:使用了一个sql: "select max(" + column + ") from " + buf.toString();
   }
   return IdentifierGeneratorFactory.createNumber(next++, returnClass);
}
}
留意这里对generate方法使用了同步, 可想如果所有ID都通过hibernate创建, 则是安全的...

4) foreign key 简而言之, 就是要取到关联的对象的ID
foreign-key的配置稍微繁琐一点, 附上一个例子: 对于帖子(Post)和评论(Comment), Comment表有一个外键fk_post, comment.post_id关联到Post.id. 那么在Comment.hbm.xml中就可以这样配置

Comment.hbm.xml
------------------------------------------------------------
<hibernate-mapping package="work">
    <class name="Comment" lazy="false">
        <id name="id">
            <generator class="foreign">
                <param name="property">post</param>
            </generator>
        </id>
...
        <many-to-one name="post" column="post_id"></many-to-one>
    </class>
</hibernate-mapping>


hibernate源代码:
------------------------------------------------------------
public Serializable generate(SessionImplementor sessionImplementor, Object object) 
throws HibernateException {
    //注:这里object是Comment对象
  
   Session session = (Session) sessionImplementor;

   //注:这里associatedObject是Post对象
   Object associatedObject = sessionImplementor.getFactory()
          .getClassMetadata( entityName )
          .getPropertyValue( object, propertyName, session.getEntityMode() );
  
   if ( associatedObject == null ) {
    throw new IdentifierGenerationException(
      "attempted to assign id from null one-to-one property: " + 
      propertyName
     );
   }
  
   EntityType type = (EntityType) sessionImplementor.getFactory()
        .getClassMetadata( entityName )
        .getPropertyType( propertyName );

   Serializable id;
   try {
    id = ForeignKeys.getEntityIdentifierIfNotUnsaved(
      type.getAssociatedEntityName(), 
     associatedObject, 
      sessionImplementor
     ); 
   }
   catch (TransientObjectException toe) {
    id = session.save( type.getAssociatedEntityName(), associatedObject );
   //注: 尝试保存该对象来生成ID, 这个操作可能触发一系列其他的东西, 如事件, 缓存写入等等
   }

   if ( session.contains(object) ) {
    //abort the save (the object is already saved by a circular cascade)
    return IdentifierGeneratorFactory.SHORT_CIRCUIT_INDICATOR;
    //throw new IdentifierGenerationException("save associated object first, or disable cascade for inverse association");
   }
   return id;
}

5) Identity: 利用数据库的自增长方式来生成ID
相比前面的策略, 这是很有意思的ID生成策略, 因为hibernate并不能在insert前预先获得ID, 而是在insert后, 依赖于JDBC API的PreparedStatement.getGeneratedKeys()方法来取得ID, 该方法返回的是一个ResultSet, 只有一列, 名称为GENERATED_KEY. 所以Hibernate也是采用一种后置处理的方式: 即在调用到IdentifierGenerator.getnerate()方法的时候(其实这个时候的实现是IdentityGenerator类) , 直接返回一个Serilizable对象--IdentifierGeneratorFactory.POST_INSERT_INDICATOR. 接着, 在我们使用session.save(object)方法的时候, 会判断save操作的类型是否为IdentifierGeneratorFactory.POST_INSERT_INDICATOR,再进行相应处理. 

save部分代码量太大, 免了. 看一些关键的.

先是"奇怪"的generate()方法
public abstract class AbstractPostInsertGenerator implements PostInsertIdentifierGenerator {
public Serializable generate(SessionImplementor s, Object obj) {
   return IdentifierGeneratorFactory.POST_INSERT_INDICATOR;
}
}
public class IdentityGenerator extends AbstractPostInsertGenerator {
.. //没有覆盖generate()
}

然后是session.save()对应的事件监听器 AbstractSaveEventListener 的saveWithGeneratedId()
protected Serializable saveWithGeneratedId(...){
...
if ( generatedId == null ) {
    throw new IdentifierGenerationException( "null id generated for:" + entity.getClass() );
   }
   else if ( generatedId == IdentifierGeneratorFactory.SHORT_CIRCUIT_INDICATOR ) {
    return source.getIdentifier( entity );
   }
   else if ( generatedId == IdentifierGeneratorFactory.POST_INSERT_INDICATOR ) {
    return performSave( entity, null, persister, true, anything, source, requiresImmediateIdAccess );
   }
...
}
该方法一直执行到protected Serializable performSaveOrReplicate(...)方法的
if ( useIdentityColumn ) {
    EntityIdentityInsertAction insert = new EntityIdentityInsertAction(
      values, entity, persister, source, shouldDelayIdentityInserts
    );
    if ( !shouldDelayIdentityInserts ) {
     log.debug( "executing identity-insert immediately" );
     source.getActionQueue().execute( insert ); //这里有文章,hibernate已先把先前的操作先转换成sql执行

经过N多处理后, 最后回到刚才提到的JDBC API上. 在IdentityGenerator.GetGeneratedKeysDelegate子类中
   public Serializable executeAndExtract(PreparedStatement insert) throws SQLException {
    insert.executeUpdate();
    ResultSet rs = null;
    try {
     rs = insert.getGeneratedKeys();
     return IdentifierGeneratorFactory.getGeneratedIdentity(rs,persister.getIdentifierType());
    ...  
}
~_~

不得不佩服Hibernate团队的重构能力, 许多功能都被按照"行为向上集中"的规则处理, 即相同的行为, 放在更高层次的父类去. 调式过程中能看到很多类都是继承于AbstractXXX. 并且每个类的责任很明确. 像EntityPersister, Batcher, ActionQueue, Executable和它的实现类, 等等...

用hibernate形式uuid的生成算法 用hibernate形式的uuid的生成类 立即下载

相关推荐

CRPR的三种关键影响:从SI、OCV到Min Pulse Width的时序优化实践

本文深入探讨了CRPR(时钟重汇聚悲观度消除)在芯片时序优化中的三种关键影响:信号完整性(SI)、片上变异(OCV)和最小脉宽检查。通过实际案例分析和技术细节,展示了CRPR如何有效修正时序分析中的过度保守估计,提升设计精度。文章特别强调了SI-aware CRPR的调试技巧和OCV补偿的最佳实践,为芯片设计工程师提供了宝贵的时序优化指导。

weixin_30920853的博客 379

org.hibernate.id.IdentifierGenerationException

当出现org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save():异常时,一般是因为元素配置不正确,元素缺少其子元素的配置引起。

qq_32564349的博客 354

基于ANSYS的电磁兼容仿真教程

最近,在做某系统的EMC仿真,使用ANSYS套件,对仿真学习做简要记录。本书简介与目录如下:本书配套有:视频教程+工程源文件+彩色仿真图片,下载链接如下:(审核中)第1章 基础知识1.1 电磁兼容基础​​​MS2、EMC三要素:3、 国际无线电干扰特别委员会标准:CISPR标准4、电磁兼容测试认证:传导发射(CE)、传导敏感度(CS)、辐射发射(RE)和辐射敏感度(RS)测试。

weixin_42508010的博客 2052

【吐血整理】Hibernate常用的主键生成策略的原理、优缺点、应用场合

通过简介以及详细讲解主键生成策略的不同属性的功能

机智猫 1万+

baostock实战:如何用Python批量下载多只股票的历史数据(含常见错误排查)

本文详细介绍了如何使用Python和baostock库批量下载多只股票的历史数据。内容涵盖从环境搭建、代码标准化到构建稳健的批量下载框架的全过程,并重点提供了高级错误处理、网络重试、数据验证及内存管理等实战解决方案,帮助用户高效构建股票数据处理流水线。

nice1的博客 1081

电力市场小白必看:手把手教你用Matlab实现市场出清电价计算(附完整代码)

本文为电力市场初学者提供了使用Matlab计算市场出清电价的完整指南。通过Yalmip和Matpower工具包,详细讲解了从环境配置到模型构建、求解及结果分析的整个过程,帮助读者快速掌握节点电价计算的核心技术。

weixin_30379531的博客 73

hibernate4 异常

 最近学习hibernate4遇到的问题记录一下,数据库使用 的是mysql5.6.23 1.Exception in thread "main" org.hibernate.MappingException: Could not instantiate id generator [entity-name=com.share.domain.User] at org.hibernate.id...

c13581357368的专栏 181

ABAP代码检查(Code Review)

1、SLIN 扩展程序检查 也可通过(SE38工具栏->检查-扩展程序检查) 进入 2、ATC 检查 通过<SE38菜单栏-检查-ABAP测试主控室> 或事物代码SCI, 支持自定义检查变式 SCI可以配置检查变式满足特殊需求,配置对象集用于批量检查 3、通过ABAP测试主控室实现统一管理,定期检查作业和监控功能(事务代码ATC) 参考 SLIN...

u012232542的博客 6068

JPA 02 进阶

JPA 02 进阶 主键生成策略(掌握); ​ 主键:能够区分表里面每一行数据 ​ 主键特点:非空且唯一 ​ 主键的分类: ​ 自然主键:具有实际意义的列来作为主键 ​ 代理主键:没有实际意义的列来作为主键 — 单个项目使用 JPA直接不定义 @GeneratedValue 就可以了,在保存之前设置主键的值 如果定义看 @GeneratedValue ,再去设置主键的值就会报错 不能修...

Kira_7的博客 440

openclaw通俗入门系列4——智能体工作区

工作区 = 智能体的家,存放它的性格、记忆、技能。默认不是牢笼(可以用沙箱隔离)。建议放到私有 Git 备份,但绝对不要提交密钥!

m0_60605453的博客 462

Python爬虫反爬:突破Cloudflare 5秒盾(免费方案,小红书/知乎可用)

这套方案的核心优势是“真实模拟+免费无门槛”无需付费代理、无需复杂JS解密,仅用playwright模拟真实浏览器,即可突破Cloudflare 5秒盾;适配小红书、知乎、豆瓣等主流平台,修改配置即可复用;兼顾易用性和稳定性,新手也能快速上手,企业级场景可通过IP轮换、异步爬取进一步优化。“模拟人类行为到底”——Cloudflare的反爬本质是区分“机器”和“人类”,只要爬虫的浏览器指纹、行为特征与人类一致,就能稳定绕过限制。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 2881

【hibernate框架】uuid与native生成ID的样例-XML配置

native算法生成随机id: native 根据底层数据库的能力选择 identity、sequence 或者 hilo 中的一个。 native算法指定的字段类型: 用mysql,你需要设置主键bigint, autoincreament (大数,自增) 用oracle,你需要设置主键bigint, sequence  这样才能在hibernate中指定native

程序猿之洞 5583

解读 2025《高质量数据集 质量评测规范》

《高质量数据集质量评测规范》为2025年数据质量评估提供标准化框架,适用于政府部门、AI企业、数据服务商等行业主体。规范以"说明文档-数据质量-模型应用"三维体系为核心:明确定义高质量数据集分类,细化各项指标要求(包括文档完整性、数据规范性、应用多样性等),并提供具体评分细则(百分制,90分达标)和计算方法。附录补充多模态数据处理细则,旨在建立可操作的质量评估标准,保障优质数据供给流通。

cdfunlove的博客 1887

jpa 获取多个生成的id_java – 如何在使用JPA和Hibernate时选择id生成...

All generators implement the interface org.hibernate.id.IdentifierGenerator. This is a very simple interface. Some applications can choose to provide their own specialized implementations, however, Hi...

weixin_39928003的博客 86

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

看Hibernate源码 003 - ID Generator

Hibernate的id生成有N种策略, 可以通过hbm文件或者annotation配置. 支持的策略包括:uuid, hilo, assigned, identity, select, sequence, seqhilo, increment, foreign, guid, uuid.hex, sequence-identity. 对应这些策略, 可以在org.hibernate.id包下找...

wjboy49的专栏 229

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

020_ID生成策略_XML_配置

博客分类: 星期六, 一月 02, 2016  14:04:55     id生成策略 1.对应项目:hibernate_0400_ID 2.注意:     a)我们观察hibernate生成表的结构并不是为了将来就用它生成,(可能还有自己的扩展,比如index等) 而是为了明白我们应该建立什么样的表和实体类映射。

dotedy的博客 427

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

hibernate id 设置(解决多进程 id 冲突)

 type (可选): 标识Hibernate类型的名字。 column (可选 - 默认为属性名): 主键字段的名字。 unsaved-value (可选 - 默认为null): 一个特定的标识属性值,用来标志该实例是刚刚创建的,尚未保存。这可以把这种实例和从以前的session中装载过(可能又做过修改--译者注)但未再次持久化的实例区分开来。 access (可选 - 默认为property)

xuexijava的专栏 1102

看Hibernate源码之 ID Generator

Hibernate的id生成有N种策略, 可以通过hbm文件或者annotation配置. 支持的策略包括:uuid, hilo, assigned, identity, select, sequence, seqhilo, increment, foreign, guid, uuid.hex, sequence-identity. 对应这些策略, 可以在org.hibernate.id包

专注于编程技术 908

分布式Unique ID的生成原理

分布式Unique ID的生成原理

杨鑫newlife的专栏 990

hibernate id 映射配置

hibernate id 映射配置

binziwell的专栏 508

Spring Data JPA使用sequence生成主键

一、实体类配置 @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "operator_seq") private Long id; 二、说明 表示使用名为"operator_seq"的序列,默认分配的值为50(可以在@SequenceGenerator(name = “operator_seq”, allocationSize = 50)中进行更改)。 实际使用中发现,批量保存多条记录时(repository.saveA

李剑 1888

hibernate 主键生成方式

<br />Key Generator<br /> 主键产生器<br /> 可选项说明:<br /> 1) assigned<br /> 主键由外部程序负责生成,无需Hibernate参与。<br /><br /> 2) hilo<br /> 通过hi/lo 算法实现的主键生成机制,需要额外的数据库表保存主<br /> 键生成历史状态。<br /><br /> 3) seqhilo<br /> 与hilo 类似,通过hi/lo 算法实现的主键生成机制,只是主键历史<br /> 状态保存在

hld_hepeng的专栏 522

Hibernate知识点总结

1.HelloWorld a.xml b.annotation2.Hibernate原理模拟,什么是O/R Mapping以及为什么要有这个?3.常见的O/RKUANGJIAN 4.基础配置5.核心接口介绍6.对象的三种状态7.ID生成策略8.关系映射9.hibernate查询(HQL)10.在struts基础上继续完善项目11.性能优化12.补充话题----------------...

aq9618154的博客 168

Hibernate 专题研究系列(一) save/update/saveOrUpdate等方法学习

  知识准备 1 所有Entity实体在其hbm.xml配置文件中都必须设置Id元素或composite-id元素,不然hbm.xml文件会报错。 因此,任何情况下Entity都拥有IdentifierGenerator对象,即关键列生成策略。 &lt;id name="id" type="string"&gt;     &lt;column name...

poethelasi1的博客 262
上一篇: 文件下载问题
下一篇: Java系统程序员修炼之道
gufengjava
博客等级 码龄16年 1粉丝 59原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值