`
wjboy49
  • 浏览: 284616 次
  • 性别: Icon_minigender_1
  • 来自: 湖南岳阳
社区版块
存档分类
最新评论

看Hibernate源码 003 - ID Generator

    博客分类:
  • java
阅读更多

Hibernate的id生成有N种策略, 可以通过hbm文件或者annotation配置.

支持的策略包括: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策略的类关系图.



 

该图分为两部分, 蓝色先左边的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和它的实现类, 等等...

  • 大小: 24.1 KB
分享到:
评论

相关推荐

    Hibernate主键策略-sequence

    Hibernate通过`org.hibernate.id.SequenceGenerator`类来实现序列主键生成器。在`nextValue()`方法中,它会与数据库进行交互,获取序列的下一个值。这个过程涉及到JDBC操作,包括SQL的执行和结果的处理。 3. **...

    精通hibernate源码ch2

    - @Id:定义主键字段,通常与@IdGenerator配合使用定义主键生成策略。 4. Hibernate的Session操作: - save()和saveOrUpdate():将对象持久化到数据库,如果对象尚未存在,会创建新记录。 - update():更新已...

    Struts2+spring2+hibernate3实例源码-java源码

    ### Struts2 + Spring2 + Hibernate3 整合实例源码分析 #### 一、概述 随着企业级应用的发展,为了提高开发效率和系统维护性,越来越多的项目开始采用MVC设计模式。其中,Struts2作为MVC框架中的佼佼者,在前端...

    Hibernate Mapping Many-to-One 实例 内附源代码及附件下载

    在Java世界中,Hibernate是一个非常流行的对象关系映射(ORM)框架,它简化了数据库操作,使得开发者可以使用面向对象的方式来处理数据。本实例将详细讲解如何在Hibernate中实现Many-to-One关系映射,这是一种常见的...

    hibernate学习笔记第四天的源码

    **hibernate学习笔记第四天源码解析** 在hibernate学习的过程中,第四天通常会深入探讨实体类、映射文件、配置文件以及查询语言等方面的内容。...建议结合源码仔细阅读并实践,以便更好地掌握hibernate的使用。

    hibernate主键实现一对一单向关联关系源码

    本主题将深入探讨如何使用Hibernate通过主键来实现一对一的单向关联关系,并提供相关的源码分析。 首先,我们需要理解一对一关联关系的基本概念。在数据库中,一对一关系意味着两个表中的每一条记录都对应另一表中...

    hibernate3.1.2英文文档(pdf)

    根据给定的文件信息,以下是对“hibernate3.1.2英文文档(pdf)”的知识点提炼和详细解析: ### 核心知识点:Hibernate 3.1.2 的架构、配置与基本对象关系映射 #### 引言: Hibernate 是一个开放源代码的对象关系...

    Hibernate ORM - 一对一主键关联关系

    Hibernate ORM 是一个强大的Java对象关系映射(ORM)框架,它允许开发者将数据库操作与Java对象模型紧密结合,简化了数据库应用程序的开发。在一对一(One-to-One)主键关联关系中,两个实体类之间存在唯一对应的...

    Hibernate-API打包

    《深入理解Hibernate-API及其在项目中的应用》 Hibernate是一个强大的Java持久化框架,它简化了数据库操作,使得开发者可以更加专注于业务逻辑,而非底层的SQL语法。本篇文章将围绕“Hibernate-API打包”这一主题,...

    hibernate学习总结

    在Eclipse中新建一个Java项目,并创建一个源码文件夹`src`,将`hibernate-3.2.4.sp1`解压后的`hibernate3.jar`及`lib`下的所有JAR包,以及Oracle的驱动包添加到项目的构建路径中。 ##### 配置Hibernate **配置`...

    解析一个简单的hibernate

    Hibernate是一个开放源码的对象关系映射(ORM)框架,它为Java应用程序提供了一种解决方案,用以处理面向对象编程语言与关系型数据库之间的不匹配问题。Hibernate的主要特点之一是其低侵入性设计,这体现在它不要求...

    深入理解Hibernate缓存

    ### 深入理解Hibernate缓存 #### 一、Hibernate缓存概述 Hibernate作为一款流行的Java持久层框架,为开发者提供了高效且灵活的数据访问能力。其中,缓存机制是Hibernate性能优化的重要组成部分之一。理解Hibernate...

    hibernate学习笔记01

    ### Hibernate学习笔记01 #### 一、Hibernate简介与安装 **Hibernate** 是一款开源的对象关系映射(Object Relational Mapping, ORM)框架,它能够有效地简化Java应用程序与数据库之间的交互,提供了一种更为优雅...

    hibernate 连接数据库基础源码3

    **hibernate 连接数据库基础源码3** 在Java编程中,Hibernate是一个非常流行的对象关系映射(ORM)框架,它简化了数据库操作,使开发者可以使用面向对象的方式来处理数据库事务。本教程将深入探讨Hibernate连接...

    Hibernate配置

    了解Hibernate的工作原理时,深入源码分析是非常有价值的。例如,研究`SessionFactory`如何管理会话,`Session`如何执行SQL语句,以及`Entity`状态转换机制等,能帮助我们更好地优化性能和解决问题。 **总结** ...

    hibernate笔记

    - **Hibernate**: 是一个开放源码的对象关系映射框架(Object Relational Mapping, ORM),它对JDBC进行了非常轻量级的对象封装,使得Java开发人员可以使用面向对象的方式进行数据库的操作。 #### 二、实现步骤详解...

    hiberate入门实例-源码

    **hibernate入门实例-源码** 在Java开发中,Hibernate是一个非常流行的对象关系映射(ORM)框架,它简化了数据库操作,使得开发者能够用面向对象的方式处理数据。本实例将带你逐步了解Hibernate的基本使用,包括...

    hibernate总结

    Hibernate是一个开放源码的ORM(Object Relational Mapping,对象关系映射)框架,它为Java应用程序提供了一种将对象模型表示的对象映射到基于SQL的关系型数据库结构中的机制。通过这种映射,开发人员可以专注于业务...

    hibernate一对一关联映射

    -- id generator配置 --&gt; &lt;/id&gt; &lt;one-to-one name="account" class="Account" cascade="all"/&gt; &lt;id name="id" column="ACCOUNT_ID"&gt; &lt;!-- id generator配置 --&gt; &lt;/id&gt; &lt;one-to-one name="user" class=...

Global site tag (gtag.js) - Google Analytics