Hibernate 6.0及以上版本如何委托PostgreSQL生成UUID?
背景
我通过Liquibase变更集定义了如下数据表,明确指定由PostgreSQL负责UUID的生成:
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd"> <changeSet author="..." id="uuid-ossp-extension"> <sql>CREATE EXTENSION IF NOT EXISTS "uuid-ossp"</sql> </changeSet> <changeSet author="example" id="widgets-table"> <createTable tableName="widgets"> <column name="widget_id" type="uuid" defaultValueComputed="uuid_generate_v1mc()"> <constraints nullable="false" primaryKey="true" primaryKeyName="widget_pk"/> </column> <column name="name" type="varchar(10)"> <constraints nullable="false"/> </column> </createTable> </changeSet> </databaseChangeLog>
使用Spring JdbcTemplate时,可通过如下方式实现保存数据并返回生成的UUID:
@Repository @RequiredArgsConstructor public class WidgetRepository { public static final String INSERT_QUERY = "INSERT INTO widgets(name) VALUES (:name)"; private final NamedParameterJdbcTemplate template; public UUID saveAndReturn(Widget widget) { var params = new MapSqlParameterSource(Map.of("name", widget.getName())); var generatedKeyHolder = new GeneratedKeyHolder(); var generateKeyColumns = new String[]{"widget_id"}; template.update(INSERT_QUERY, params, generatedKeyHolder, generateKeyColumns); return generatedKeyHolder.getKeyAs(UUID.class); } ... }
Hibernate 6.0及以上版本提供了新的UuidGenerator注解,可简化由Hibernate自行生成UUID的逻辑:
@Entity @Table(name = "widgets") public class Widget { @Id @UuidGenerator(style = TIME) @Column(name = "widget_id") private UUID id; @Column private String name; ... }
但该UuidGenerator机制不支持委托PostgreSQL生成UUID,此前相关方案依赖的UUIDGenerationStrategy接口现已废弃,请问Hibernate 6.0及以上版本中的推荐实现方式是什么?
解决方案
在Hibernate 6.x中,要实现委托PostgreSQL数据库生成UUID,推荐以下两种方式:
方式一:使用@GeneratedValue配合GenerationType.IDENTITY
由于数据表已通过defaultValueComputed指定PostgreSQL自动生成UUID(uuid_generate_v1mc()),只需在实体类ID字段配置@GeneratedValue(strategy = GenerationType.IDENTITY),让Hibernate识别这是数据库生成的主键:
@Entity @Table(name = "widgets") public class Widget { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "widget_id", columnDefinition = "uuid") private UUID id; @Column(name = "name", length = 10, nullable = false) private String name; // 构造器、getter/setter等 }
这种方式下,Hibernate插入数据时会忽略ID字段的赋值,由数据库通过默认值生成UUID,插入后自动将生成的ID值返回给实体对象,是最简洁的方案。
方式二:自定义标识符生成器(适配灵活场景)
如果需要更精细的控制(比如切换不同UUID生成函数),可实现Hibernate 6.x的IdentifierGenerator接口自定义生成器:
- 自定义生成器类:
public class PostgreSqlUuidGenerator implements IdentifierGenerator { @Override public Object generate(SharedSessionContractImplementor session, Object entity) throws HibernateException { // 调用PostgreSQL指定的UUID生成函数 return session.createNativeQuery("SELECT uuid_generate_v1mc()", UUID.class) .uniqueResult(); } }
- 在实体类中指定使用该生成器:
@Entity @Table(name = "widgets") public class Widget { @Id @GeneratedValue(generator = "postgresql-uuid") @GenericGenerator(name = "postgresql-uuid", type = PostgreSqlUuidGenerator.class) @Column(name = "widget_id", columnDefinition = "uuid") private UUID id; @Column(name = "name", length = 10, nullable = false) private String name; // 构造器、getter/setter等 }
这种方式可自定义生成逻辑,比如替换为uuid_generate_v4()函数,适合需要动态调整生成策略的场景。
注意事项:
- 确保PostgreSQL已安装
uuid-ossp扩展(你的Liquibase变更集已处理此步骤)- 使用
GenerationType.IDENTITY时,Hibernate会自动适配PostgreSQL主键生成逻辑,无需额外配置- 自定义生成器时,需保证生成函数与数据库默认值逻辑一致,避免主键冲突
内容的提问来源于stack exchange,提问作者toolkit

