来源: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策略的类关系图.
该图分为两部分, 蓝色先左边的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和它的实现类, 等等...
分享到:
相关推荐
自动生成的Entity类通常包含属性(对应表的字段)和getter/setter方法,有时还会包含一些特定的Hibernate注解,如@Id(主键)和@GeneratedValue(主键生成策略)。 2. **映射文件(Mapping Files)**:在传统的...
### Hibernate 主键生成策略详解 Hibernate 是一款流行的 Java 持久层框架,它提供了对象关系映射(ORM)的功能,使得 Java 开发者能够更高效地与数据库进行交互。在 Hibernate 中,主键生成策略是一项核心功能,...
生成工具的工作原理大致如下: 1. 连接数据库:工具会首先连接到指定的数据库,获取数据库的元数据信息,如表名、列名、约束等。 2. 分析表结构:根据数据库中的表结构,工具会解析出每个表的字段类型、主键信息...
- **实现机制**:该策略由`org.hibernate.id.IdentifierGenerator`接口实现,通过递增的方式生成主键值。 - **应用场景**:当数据表中的记录数量相对较少,并且不会同时有多个进程尝试插入数据时,可以使用此策略。 ...
<id>xdoclet-hibernate</id> <goal>generate ``` 然后运行构建,XDoclet将根据源代码中的注解生成相应的.hbm.xml文件。这些文件会被Hibernate自动加载,用于对象与数据库之间的映射。 除了映射文件...
JAVA ID生成策略是一种在Hibernate框架中使用的策略,用于生成实体类的主键值。这种策略在Annotation情况下,主要有三种方式生成主键值。 第一种方式是使用数据库的自动增长字段生成。这种方式使用@GeneratedValue...
标题“使用表的id生成...综上所述,"使用表的id生成"这个主题涵盖了JPA中主键生成的基本原理、策略选择、源码解析以及可能的开发工具应用。通过学习和实践,开发者能够灵活地根据项目需求来设置和管理数据库表的主键。
总结来说,Hibernate4 的实现原理主要涉及配置加载、对象状态管理、事务控制和 SQL 生成执行等方面。通过这些机制,开发者可以专注于业务逻辑,而无需关注底层数据库操作,极大地提高了开发效率。在实际使用中,还需...
上述代码定义了一个User实体类,Hibernate会根据这个类生成一个名为`users`的表,包含一个自增的id字段和一个非空的name字段。 8. 使用注意事项 - 在生产环境中,谨慎使用`create`模式,以免造成数据丢失。 - ...
`@Id`注解标记实体类中的主键字段,而`@GeneratedValue`用于指定主键生成策略,如自增、序列等。 ```java @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long ...
generator 是 Hibernate 中的一种生成器,用于生成唯一的标识符。generator 可以通过 Java 类或 XML 文件来定义。 composite-id composite-id 是 Hibernate 中的一种复合标识符,用于标识对象模型的唯一性。...
《Hibernate原理与应用》 Hibernate 是一款流行的Java ORM(对象关系映射)框架,它解决了在面向对象编程中模型与关系数据库之间的“阻抗不匹配”问题。在Java应用程序中,我们通常使用对象来表示业务逻辑,而...
本文将深入探讨如何使用Hibernate的形式生成UUID,通过分析一个具体的代码示例——`UUIDHexGenerator`类,来理解其内部机制和工作原理。 #### UUIDHexGenerator类解析 `UUIDHexGenerator`类位于`com.general`包下...
- **实体类的注解配置**:使用@Entity注解标记持久化类,@Id注解标记主键字段,@GeneratedValue自动生成主键。 - **Session工厂**:通过SessionFactoryBuilder构建SessionFactory对象,它是整个Hibernate的核心。 ...
4. **删除(Delete)**: 删除操作可以由`deleteById()`方法执行,它会构建并执行一个SQL的DELETE语句,根据提供的ID删除对应记录。如果需要批量删除,可能还需要额外的方法。 本示例强调了不使用Hibernate等ORM工具...
### Hibernate的原理与配置 #### 一、概述 Hibernate 是一款流行的开源 ORM(Object Relational Mapping,对象关系映射)框架,它简化了 Java 应用程序与数据库之间的交互过程。通过提供一套强大的 API,Hibernate...
在"Hibernate 原理与应用 ppt"中,主要讲解了Hibernate的基本概念、安装配置、CURD操作、关联映射、继承映射、懒加载、HQL和Criteria查询、缓存管理、事务处理以及最佳实践和与JPA的集成。 **安装配置** 在开始使用...
**Hibernate原理与配置快速入门** Hibernate 是一个强大的Java持久化框架,它简化了数据库操作,使得开发者无需直接编写SQL语句即可对数据库进行操作。在本文中,我们将深入探讨Hibernate的核心原理、配置过程以及...
《深入剖析Hibernate 3.2源代码...通过深入学习Hibernate 3.2的源代码,开发者不仅可以了解其实现原理,还能在遇到问题时更快定位,进行定制化开发。同时,这也有助于理解ORM框架的工作方式,提升软件设计和开发能力。
在生成代码后,你需要配置 Hibernate 的主配置文件(通常为 `hibernate.cfg.xml`),指定数据源、实体类、映射文件等信息。此外,还需要创建 SessionFactory,它是 Hibernate 工作的核心,用于管理数据库会话。 五...