`

hibernate id策略

    博客分类:
  • J2EE
阅读更多
Hibernate的id生成有N种策略, 可以通过hbm文件或者annotationhibernate 源码配置.

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


Hibernate主键生成方式 Key Generator
Hibernate主键生成方式 Key Gehibernate 主键nerator主键产生器可选项说明:1) assigned主键由外部程序负责生成,无需Hibernate参与。


2) hilo通过hi/lo 算hibernate 联合主键法实现的主键生成机制,需要额外的数据库表保存主键生成历史状态。


3) seqhilo与hilo 类似,通过hi/lohibernate 复合主键 算法实现的主键生成机制,只是主键历史状态保存在Sequence中,适用于支持Sequence的数据库,如Oracle.


4) increment主键按数值顺序递增。此方式的实hibernate 没有主键现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,之后每次需要生成主键的时候将此值加1作为主键。


这种方式可能产生的问题是:如果当前有多个实例访问同一个数据库,那么由于各个实例各自维护主键状态,不同实例可能生成同样的主键,从而hibernate 无主键造成主键重复异常。因此,如果同一数据库有多个实例访问,此方式必须避免使用。


5) identity采用hibernate 指定主键数据库提供的主键生成机制。如DB2、SQL Server、MySQL中的主键生成机制。


6) sequence采用数据库提供的sequence 机制生成主键。如Oralce 中hibernate主键策略的Sequence.


7) native由Hibernate根据底层数据库自行判断采用identity、hilo、sequence其中一hibernate 主键生成种作为主键生成方式。


8) uuid.hex由Hibernate基于128 位唯一值产生算法生成16 进制数值(编码后以长度32 的字符hibernate 自动主键串表示)作为主键。


9) uuid.string与uuid.hex 类似,只prop key hibernate是生成的主键未进行编码(长度16)。在某些数据库中可能出现问题(如PostgreSQL)。


10) foreign使用外部表的字段作为主键。


一般而言,利用uuid.hex方式生成主键将提供最好的性能和数据库平台适应性。


另外由于常用的数据库,如Oracle、DB2、SQLServer、MySql 等,都提供了易用的主键生成机制(Auto-Increase 字段或者Sequence)。我们可以在数据库提供的主键生成机制上,采用generator-class=native的主键生成方式。不过值得注意的是,一些数据库提供的主键生成机制在效率上未必最佳,大量并发insert数据时可能会引起表之间的互锁。


数据库提供的主键生成机制,往往是通过在一个内部表中保存当前主键状态(如对于自增型主键而言,此内部表中就维护着当前的最大值和递增量),之后每次插入数据会读取这个最大值,然后加上递增量作为新记录的主键,之后再把这个新的最大值更新回内部表中,这样,一次Insert操作可能导致数据库内部多次表读写操作,同时伴随的还有数据的加锁解锁操作,这对性能产生了较大影响。


因此,对于并发Insert要求较高的系统,推荐采用uuid.hex 作为主键生成机制。

Related posts:

Hibernate对各数据库的连接方言
Hibernate简介
Java程序连接各种数据库的方法
常用的数据库连接方法
Java生成UUID通用唯一识别码



对应这些策略, 可以在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().createhibernate3 源码IdentifierGenerator
(
       settings.getDialect(),
            settings.getDefaultCatalogName(),
            settings.getDefaultSchemaName(),
            (RootClass) model
      );
     identifierGenerators.put( model.getEntityName(), generator );
    }
   }

model.getIdentifier().createhibernate3 源码IdentifierGenerator() 最终会引用到

return IdentifierGeneratorFactory.create(
     identifierGeneratorStrategy,
     getType(),
     params,
     dialect
    );

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

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

最主要的接口有4个, 图中用蓝色标出.
* identifiergenerator: id生成器接口, 只有一个ghibernate idenerate方法, 返回生成的id.
* hibernate save idpostinsertidentifiergenerator: 针对mysql, mssql这类能为主键设置自动增长的id生成器
* persistentidentifihibernate id自增ergenerator: 针对需要访问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”>
                <paid generatorram 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教程02_ID生成策略

    http://blog.csdn.net/e421083458/article/details/8794127 该源码为Hibernate教程配套源码

    Hibernate笔记 马士兵

    第13课 ID主键生成策略 20 一、 Xml方式 20 元素(主键生成策略) 20 二、 annotateon方式 21 1、AUTO默认 21 2、IDENTITY 22 3、SEQUENCE 22 4、为Oracle指定定义的Sequence 22 5、TABLE - 使用表保存id值 23 三、 ...

    Hibernate的主键生成策略

    Hibernate的所有自带的主键生成策略以及XML配置文件的用法

    Hibernate+中文文档

    3.8. Hibernate SQL方言 (hibernate.dialect) 3.9. Hibernate日志类别 3.10. JTA TransactionManagers 9.1. 继承映射特性(Features of inheritance mappings) 16.1. 别名注射(alias injection names) 19.1. ...

    马士兵hibernate学习笔记(原版)

    5 ID生成策略(重点 AUTO) 6 Hibernate核心开发接口介绍(重点) ... 风格 1 先脉络,后细节 2 先操作,后原理 3 重Annotation,轻xml配置文件 ... 性能优化 1 注意session.clear()的运用,尤其在不断...

    hibernate 3中的缓存小结

     Hibernate的二级缓存策略,是针对于ID查询的缓存策略,对于条件查询则毫无作用。为此,Hibernate提供了针对条件查询的Query Cache。 2.3.2. 什么样的数据适合存放到第二级缓存中? 1 很少被修改的数据 2 不是很...

    Hibernate注释大全收藏

    以上所有实体使用 JOINED 策略 Ferry和Boat class使用同名的主键关联(eg: Boat.id = Ferry.id), AmericaCupClass 和 Boat 关联的条件为 Boat.id = AmericaCupClass.BOAT_ID. 从父类继承的属性 @MappedSuperclass ...

    hibernate3.2中文文档(chm格式)

    HIBERNATE - 符合Java习惯的关系数据库持久化 Hibernate参考文档 3.2 -------------------------------------------------------------------------------- 目录 前言 1. 翻译说明 2. 版权声明 1. Hibernate...

    HibernateAPI中文版.chm

    HIBERNATE - 符合Java习惯的关系数据库持久化 Hibernate参考文档 3.2 -------------------------------------------------------------------------------- 目录 前言 1. 翻译说明 2. 版权声明 1. Hibernate...

    JAVA 的ID生成策略

    主要描述hibernate在Annotation情况下的主键生成策略

    hibernate 教程

    开始Hibernate之旅 1.2. 第一个可持久化类 1.3. 映射cat 1.4. 与猫同乐 1.5. 结语 2. 体系结构 2.1. 总览 2.2. JMX集成 2.3. JCA支持 3. SessionFactory配置 3.1. 可编程配置方式...

    hibernate学习笔记

    Hibernate 学习笔记 Hibernate 学习笔记 1 第一个hibernate项目(hibernate_first) 2 测试实体对象的生命周期(hibernate_session) 3 ...hibernate抓取策略 53 Hibernate最佳实践(Best Practices) 55

    Hibernate中的merge使用详情解说.docx

    merge的作用是:新new一个对象,如果该对象设置了ID,则这个对象就当作游离态处理: 当ID在数据库中不能找到时,用...用update的话,由于没有ID,所以会报异常,merge此时则会保存数据,根据ID生产策略生成一条数据;

    Hibernate注解

    * @GenericGenerator —— 注解声明了一个hibernate的主键生成策略。支持十三种策略。该注解有如下属性 * name 指定生成器名称 * strategy 指定具体生成器的类名(指定生成策略)。 * parameters 得到strategy指定的...

    hibernate配置

    hibernate是一个封装在SQL和JDBC之上的企业级应用框架 配置文件的基本结构如下: &lt;?xml version="1.0" encoding='UTF-8'?&gt;... 主键生成策略"/&gt; &lt;/id&gt; …… &lt;/hibernate-mapping&gt;

    Hibernate中文详细学习文档

    5.1.12. 自然ID(natural-id) 5.1.13. 组件(component), 动态组件(dynamic-component) 5.1.14. properties 5.1.15. 子类(subclass) 5.1.16. 连接的子类(joined-subclass) 5.1.17. 联合子类(union-subclass) ...

    演示怎样在Hibernate中使用复合主键

    环境:Windows XP professional、JDK 1.6、Hibernate 3.1、SQL Server 2000、MyEclipse 5.5。 如果使用Hibernate开发legacy的数据库应用,对于数据库表中有使用字符串作为主键或者使用复合主键情况,那么对于这些...

    Hibernate 中文 html 帮助文档

    5.1.12. 自然ID(natural-id) 5.1.13. 组件(component), 动态组件(dynamic-component) 5.1.14. properties 5.1.15. 子类(subclass) 5.1.16. 连接的子类(joined-subclass) 5.1.17. 联合子类(union-subclass) 5.1.18. ...

    最全Hibernate 参考文档

    1.1. 开始Hibernate之旅 1.2. 第一个持久化类 1.3. 映射cat 1.4. 与Cat同乐 1.5. 结语 2. 架构(Architecture) 2.1. 概况(Overview) 2.2. 实例状态 2.3. JMX整合 2.4. 对JCA的支持 3. 配置 3.1. 可编程的配置方式 ...

    hibernate 体系结构与配置 参考文档(html)

    1. Hibernate入门 1.1. 前言 1.2. 第一部分 - 第一个Hibernate应用程序 1.2.1. 第一个class 1.2.2. 映射文件 1.2.3. Hibernate配置 1.2.4. 用Ant构建 1.2.5. 启动和辅助类 1.2.6. 加载并存储对象 1.3. 第...

Global site tag (gtag.js) - Google Analytics