`

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生成策略

    在Java的持久化框架Hibernate中,ID生成策略是至关重要的一个环节。它是用来唯一标识数据库中每一行记录的关键部分。本教程将详细讲解Hibernate中的ID生成策略,以及如何在实际项目中灵活应用。 首先,ID生成策略是...

    Hibernate主键策略-sequence

    在Java的持久化框架Hibernate中,主键策略是管理数据库记录唯一标识的重要组成部分。主键策略定义了如何生成和管理实体对象的主键值。在本篇内容中,我们将深入探讨"Hibernate主键策略-sequence",并结合标签中的...

    常用Hibernate主键生成策略

    ### 常用Hibernate主键生成策略详解 #### 一、引言 在数据库设计与操作过程中,主键是确保数据唯一性的关键要素之一。在实际应用中,开发者经常需要处理不同类型的数据库,并且需要应对各种不同的主键生成需求。...

    Hibernate中主键生成策略

    在Java的持久化框架Hibernate中,主键生成策略是一个至关重要的概念,它决定了数据库表中主键值如何自动生成。主键通常是表中唯一标识记录的一列,对于数据的完整性和一致性至关重要。以下是对Hibernate中主键生成...

    hibernate抓取策略和懒加载案例

    在Java的持久化框架Hibernate中,数据访问优化是至关重要的,而抓取策略(Fetch Strategy)和懒加载(Lazy Loading)则是实现这一目标的关键技术。本文将深入探讨这两个概念,并通过具体的案例进行分析。 首先,让...

    hibernate 缓存策略

    **hibernate缓存策略详解** Hibernate作为Java领域中广泛使用的ORM框架,其在处理大量数据时,为了提高性能和减少数据库的访问压力,引入了缓存机制。本文将深入探讨Hibernate的缓存策略,包括一级缓存、二级缓存...

    Hibernate的缓存策略

    ### Hibernate的缓存策略 #### 一、缓存的基本概念 缓存技术是现代软件架构设计中的重要组成部分,尤其在数据库交互频繁的应用场景下尤为重要。简单来说,缓存是一种存储技术,位于应用程序与物理数据之间,目的是...

    Hibernate各种主键生成策略与配置详解

    ### Hibernate 主键生成策略与配置详解 #### 一、概述 在使用Hibernate进行持久化操作时,合理选择和配置主键生成策略对于确保数据的一致性和优化性能至关重要。本文将详细介绍几种常见的主键生成策略,并结合示例...

    hibernate主键生成策略详解

    ### hibernate主键生成策略详解 #### 一、assigned **assigned** 主键生成策略意味着主键的值是由外部程序负责生成的,并且在执行 `save()` 方法之前必须明确指定一个值。在这种策略下,Hibernate 不参与主键的...

    hibernate映射主键生成策略native

    ### Hibernate映射主键生成策略native #### 一、引言 在ORM(对象关系映射)技术中,Hibernate作为一款流行的Java持久层框架,在处理数据持久化方面提供了丰富的功能和灵活性。其中,主键生成策略是Hibernate配置...

    Hibernate数据类型映射及ID

    标题:“Hibernate数据类型映射及ID” ...总之,Hibernate通过其强大的数据类型映射机制和灵活的ID生成策略,为开发者提供了高效、便捷的对象关系映射体验,使得Java应用能够无缝地与各种关系型数据库进行交互。

    hibernate缓存策略

    ### Hibernate缓存策略详解 #### 一、理解Hibernate缓存 ##### 1.1 缓存概念 在软件开发领域,缓存技术是一项重要的优化手段,它可以显著提高应用程序的性能和响应速度。Hibernate作为一种持久层框架,其核心功能...

    day36 06-Hibernate抓取策略:set集合上的抓取策略

    今天我们将深入探讨"day36 06-Hibernate抓取策略",特别是针对set集合的抓取策略。在阅读这篇博客文章(链接:https://364232252.iteye.com/blog/2368811)后,我们将了解到如何优化数据加载,以避免N+1查询问题,...

    hibernate的lazy策略forClass

    在Java的持久化框架Hibernate中,懒加载(Lazy Loading)是一种重要的对象关系映射策略,用于提高应用程序的性能。懒加载的基本思想是延迟加载,即当真正需要数据时,才去数据库加载。在这个场景中,我们关注的是...

    Hibernate的主键生成策略

    ### Hibernate的主键生成策略详解 #### 一、概述 在使用Hibernate进行持久化操作时,主键生成策略的选择对于数据库性能及应用架构至关重要。本文将详细介绍Hibernate中五种常用的主键生成策略:assigned、...

    Hibernate主键生成策略.docx

    ### Hibernate 主键生成策略详解 #### 一、概述 Hibernate 是一种流行的 Java 持久化框架,它简化了数据库操作,并提供了多种主键生成策略。主键是表中的一个或多个字段组合,用于唯一标识表中的每一条记录。...

    hibernate 主键生成策略

    ### Hibernate 主键生成策略详解 Hibernate 是一款流行的 Java 持久层框架,它提供了对象关系映射(ORM)的功能,使得 Java 开发者能够更高效地与数据库进行交互。在 Hibernate 中,主键生成策略是一项核心功能,...

Global site tag (gtag.js) - Google Analytics