在Hibernate的hbm映射配置文件中,主键生成策略通过<generator>标签的class属性来指定,不同的class值对应不同的主键生成逻辑,适配各类数据库和业务需求。

hbm中generator的核心可选项
generator的class属性支持多种取值,以下是最常用的可选项及其说明:
1. assigned
由应用程序在调用save方法之前手动为对象分配主键值,Hibernate不会干预主键生成过程。适用于主键由业务逻辑生成,或者需要从外部系统获取主键的场景。
配置示例:
<id name="id" type="java.lang.Long">
<column name="id" />
<generator class="assigned" />
</id>2. increment
Hibernate先查询当前表主键的最大值,然后在此基础上加1作为新记录的主键。这种方式不依赖数据库的自增特性,但是多个Hibernate实例同时操作时可能出现主键冲突,只适合单应用实例的场景。
配置示例:
<id name="id" type="java.lang.Long">
<column name="id" />
<generator class="increment" />
</id>3. identity
依赖数据库自带的自增主键机制,比如MySQL的AUTO_INCREMENT、SQL Server的IDENTITY列。插入数据时数据库会自动生成自增主键,Hibernate会在插入后获取生成的主键值。仅支持有自增机制的数据库。
配置示例:
<id name="id" type="java.lang.Long">
<column name="id" />
<generator class="identity" />
</id>4. sequence
依赖数据库的序列(Sequence)机制,比如Oracle、PostgreSQL的序列。需要配合<param>标签指定序列的名称,插入数据时Hibernate会从序列中获取下一个值作为主键。仅支持有序列机制的数据库。
配置示例:
<id name="id" type="java.lang.Long">
<column name="id" />
<generator class="sequence">
<param name="sequence">user_seq</param>
</generator>
</id>5. native
Hibernate会根据底层数据库的能力,自动在identity、sequence、hilo之间选择最适合的主键生成策略。如果是MySQL等支持自增的数据库会选择identity,如果是Oracle等支持序列的数据库会选择sequence,适配性较强,是常用的配置选项。
配置示例:
<id name="id" type="java.lang.Long">
<column name="id" />
<generator class="native" />
</id>6. uuid
Hibernate使用128位的UUID算法生成主键值,生成的主键是32位十六进制字符串,不会重复,不依赖数据库,适合分布式系统场景。但是主键是字符串类型,索引效率比数值类型低。
配置示例:
<id name="id" type="java.lang.String">
<column name="id" length="32" />
<generator class="uuid" />
</id>7. hilo
使用高低位算法生成主键,需要额外的一张表来保存高位值。Hibernate会从高位表中获取高位值,结合低位计数器生成主键,不依赖数据库的自增或序列机制,跨数据库兼容性好。
配置示例:
<id name="id" type="java.lang.Long">
<column name="id" />
<generator class="hilo">
<param name="table">hilo_table</param>
<param name="column">next_hi</param>
<param name="max_lo">100</param>
</generator>
</id>8. seqhilo
类似hilo策略,但是高位值不是从表中获取,而是从数据库的序列中获取,结合了序列和hilo算法的特点,适用于支持序列的数据库。
配置示例:
<id name="id" type="java.lang.Long">
<column name="id" />
<generator class="seqhilo">
<param name="sequence">user_seq</param>
<param name="max_lo">100</param>
</generator>
</id>9. foreign
使用关联对象的标识符作为当前对象的主键,通常用于一对一关联的场景,比如用户和用户详情表,用户详情的主键复用用户表的主键。
配置示例:
<id name="id" type="java.lang.Long">
<column name="id" />
<generator class="foreign">
<param name="property">user</param>
</generator>
</id>可选项适用场景对比
不同generator选项的适用场景差异较大,以下是核心维度的对比:
| generator选项 | 依赖数据库特性 | 主键类型 | 适用场景 |
|---|---|---|---|
| assigned | 无 | 自定义 | 业务自定义主键、外部系统导入主键 |
| increment | 无 | 数值型 | 单应用实例、低并发场景 |
| identity | 数据库自增 | 数值型 | MySQL、SQL Server等支持自增的数据库 |
| sequence | 数据库序列 | 数值型 | Oracle、PostgreSQL等支持序列的数据库 |
| native | 自动适配 | 数值型 | 跨数据库兼容、无特殊主键需求 |
| uuid | 无 | 字符串型 | 分布式系统、需要全局唯一主键 |
| hilo | 无(需高位表) | 数值型 | 不支持自增和序列的数据库、跨数据库 |
| foreign | 无 | 同关联对象主键 | 一对一关联场景 |
配置注意事项
- 选择generator时需要先确认使用的数据库支持哪种主键生成机制,比如MySQL不支持sequence,配置sequence会导致运行错误。
- 如果使用分布式部署,不建议使用increment、hilo等依赖本地状态生成的策略,容易出现主键冲突。
- 主键类型需要和generator生成的值类型匹配,比如uuid生成字符串,主键类型就要配置成java.lang.String,否则会出现类型转换异常。
- foreign策略需要配合一对一关联的映射配置使用,否则无法正确获取关联对象的主键值。
示例:完整hbm配置演示
以下是一个用户实体类的完整hbm配置,使用native作为generator选项:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.example.pojo.User" table="t_user">
<id name="id" type="java.lang.Long">
<column name="id" />
<generator class="native" />
</id>
<property name="username" type="java.lang.String">
<column name="username" length="50" not-null="true" />
</property>
<property name="age" type="java.lang.Integer">
<column name="age" />
</property>
</class>
</hibernate-mapping>