1、简介 {#1简介}
本文将带你了解如何为数据库序列(Sequences)配置 Hibernate 6 的隐式 命名策略。Hibernate 6 引入了几种新的命名策略,这些策略会影响序列的命名和使用方式。
2、标准命名策略 {#2标准命名策略}
默认情况下,Hibernate 6 使用标准命名策略。它根据实体名称和列名称生成序列名称。假如,我们有一个带有 id
列的实体 Person
,那么序列名称就是 person_seq
。
要修改命名策略,需要在 application.properties
中为不同的命名策略添加必要的配置。
# 使用标准命名策略
spring.jpa.properties.hibernate.id.db_structure_naming_strategy=standard
下面介绍如何为每种命名策略设置配置。
来看一个基本的 Person
实体类:
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String name;
// Getter / Setter 省略
}
在本例中,由于我们使用的是标准策略,因此在建表的同时,Hibernate 会自动生成一个名为 person_seq
的序列:
Hibernate:
create table person (
id bigint not null,
name varchar(255),
primary key (id)
)
Hibernate:
create sequence person_seq start with 1 increment by 50
标准策略的一个关键点是其默认增量(increment )值。Hibernate 会分配一个较大的值,如 50,以优化批处理操作,减少序列检索所需的数据库调用次数。
当我们插入一条 Person
记录时,Hibernate 会使用 person_seq
序列来生成主键:
Hibernate:
select next value for person_seq
Hibernate:
insert
into person (name,id)
values (?,?)
此外,我们还可以使用 @Table
注解来覆盖表映射。这样,就可以指定一个自定义表名,然后使用该表名生成相应的序列名。
例如,如果我们有一个带有 id
列的实体 Person
,我们可以指定一个自定义表名 my_person_table
,以生名为 my_person_table _seq
的序列:
@Entity
@Table(name = "my_person_table")
public class Person {
// ...
}
在本例中,表名为 my_person_table
,生成的序列名为 my_person_table_seq
:
Hibernate:
create table my_person_table (
id bigint not null,
name varchar(255),
primary key (id)
)
Hibernate:
create sequence my_person_table_seq start with 1 increment by 50
当我们尝试插入一条 Person
记录时,Hibernate 会利用 my_person_table_seq
生成主键值:
Hibernate:
select
next value for my_person_table_seq
3、传统命名策略 {#3传统命名策略}
该策略与标准策略类似,但它使用传统的命名约定或生成器名称(如果指定)来生成序列名称。例如,我们有一个带有列 id
的实体 Person
,那么序列名称就是 hibernate_sequence
。而且,hibernate_sequence
序列会在所有实体中使用。
要启用传统策略,要在 application.properties
文件中将 hibernate.id.db_structure_naming_strategy
属性设置为 legacy
:
spring.jpa.properties.hibernate.id.db_structure_naming_strategy=legacy
来看看使用传统策略的现有 Person
实体类。在这种情况下,Hibernate 会创建表和一个名为 hibernate_sequence
的单个序列:
Hibernate:
create table person (
id bigint not null,
name varchar(255),
primary key (id)
)
Hibernate:
create sequence hibernate_sequence start with 1 increment by 1
与标准命名策略不同,使用传统命名策略时,序列的默认增量值通常为 1 。 这意味着序列产生的每个新值都将以 1 为增量。
在插入 Person
记录时,Hibernate 依赖 hibernate_sequence
来生成主键:
Hibernate:
select next value for hibernate_sequence
Hibernate:
insert
into person (name,id)
values (?,?)
即使使用传统策略,我们也可以使用 @Table
注解自定义表名。在这种情况下,生成的序列名称不会反映表的名称。
不过,我们可以使用 @SequenceGenerator
注解来指定自定义序列名称。这对于管理特定实体的序列特别有用:
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "person_custom_seq")
@SequenceGenerator(name = "person_custom_seq", sequenceName = "person_custom_seq", allocationSize = 10)
private Long id;
如下,使用 @SequenceGenerator
注解指定了一个分配大小为 10 的自定义序列名称 person_custom_seq
。@GeneratedValue
注解被设置为使用该序列生成器:
Hibernate:
create table my_person_table (
id bigint not null,
name varchar(255),
primary key (id)
)
Hibernate:
create sequence person_custom_seq start with 1 increment by 10
当插入一条 Person
记录时,Hibernate 会执行以下 SQL 语句,从 person_custom_seq
序列中检索下一个可用值:
Hibernate:
select next value for person_custom_seq
传统策略主要是为了保持与旧版本 Hibernate 的兼容性,避免破坏现有系统。
4、唯一命名策略 {#4唯一命名策略}
Hibernate 6 引入了唯一命名策略,通过为同一 Schema 中的所有实体使用统一的序列名称来简化序列命名。与传统策略类似,在使用唯一命名策略时,Hibernate 会生成一个名为 hibernate_sequence
的唯一序列,并在模式中的所有实体间共享,这对于确保序列管理方法的一致性特别有用。
来看看这在 Person
和 Book
这两个不同的实体中是如何使用的。
要使用唯一命名策略,需要在 application.properties
文件中进行配置:
spring.jpa.properties.hibernate.id.db_structure_naming_strategy=single
如下,使用唯一命名策略创建两个实体:Person
和 Book
:
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String title;
// Getter / Setter 省略
}
使用唯一命名策略时,Hibernate 会为所有实体生成唯一的一个序列。下面是实体的 SQL 语句:
Hibernate:
create table book (
id bigint not null,
title varchar(255),
primary key (id)
)
Hibernate:
create table person (
id bigint not null,
name varchar(255),
primary key (id)
)
Hibernate:
create sequence hibernate_sequence start with 1 increment by 1
你可以看到,在这里,我们只创建了一个序列 hibernate_sequence
。因此,在向 Person
和 Book
表插入记录时,Hibernate 会使用 hibernate_sequence
序列来生成主键值:
Person person = new Person();
person.setName("John Doe");
personRepository.save(person);
Book book = new Book();
book.setTitle("Baeldung");
bookRepository.save(book);
List<Person> personList = personRepository.findAll();
List<Book> bookList = bookRepository.findAll();
assertEquals((long)1,(long) personList.get(0).getId());
assertEquals((long)2, (long) bookList.get(0).getId());
如上,我们创建并保存两个实体:Person
和 Book
。这两个实体应使用相同的序列生成器生成主键。当我们先保存 Person
,再保存 Book
时,Person
的 ID 应该是 1
,Book
的 ID 应该是 2
。
5、自定义命名策略 {#5自定义命名策略}
此外,我们还可以通过自定义命名策略来定义自己的序列命名约定。要为序列使用自定义命名策略,我们首先需要创建 ImplicitDatabaseObjectNamingStrategy
的自定义实现。该接口用于为包括序列在内的各种数据库对象提供自定义命名策略。
如下,创建 ImplicitDatabaseObjectNamingStrategy
的自定义实现,自定义序列名称:
public class CustomSequenceNamingStrategy implements ImplicitDatabaseObjectNamingStrategy {
@Override
public QualifiedName determineSequenceName(Identifier catalogName, Identifier schemaName, Map<?, ?> map, ServiceRegistry serviceRegistry) {
JdbcEnvironment jdbcEnvironment = serviceRegistry.getService(JdbcEnvironment.class);
String seqName = ((String) map.get("jpa_entity_name")).concat("_custom_seq");
return new QualifiedSequenceName(
catalogName,
schemaName,
jdbcEnvironment.getIdentifierHelper().toIdentifier(seqName));
}
// 其他方法
}
在 determineSequenceName()
方法中,我们自定义了序列名称的生成。首先,使用 JdbcEnvironment
服务访问各种与数据库相关的设置,这有助于我们高效地管理数据库对象名称。然后,在实体名称后添加 _custom_seq
后缀,创建自定义序列名称。通过这种方法,我们可以控制整个数据库 Schema 的序列名称格式。
接下来,需要配置 Hibernate 以使用自定义命名策略:
spring.jpa.properties.hibernate.id.db_structure_naming_strategy=com.baeldung.sequencenaming.CustomSequenceNamingStrategy
通过自定义的 ImplicitDatabaseObjectNamingStrategy
,Hibernate 将生成包含我们定义的自定义序列名称的 SQL 语句:
Hibernate:
create table person (
id bigint not null,
name varchar(255),
primary key (id)
)
Hibernate:
create sequence person_custom_seq start with 1 increment by 50
除了 ImplicitDatabaseObjectNamingStrategy
之外,我们还可以使用 PhysicalNamingStrategy
,它定义了所有数据库对象的综合命名约定。使用 PhysicalNamingStrategy
,我们可以实现复杂的命名规则,这些规则不仅适用于序列,也适用于其他数据库对象。
下面是一个如何使用 PhysicalNamingStrategy
实现自定义序列命名的示例:
public class CustomPhysicalNamingStrategy extends DelegatingPhysicalNamingStrategy {
@Override
public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment context) {
return new Identifier(name.getText() + "_custom_seq", name.isQuoted());
}
// 针对表和列的其他方法
}
6、总结 {#6总结}
本文介绍了如何在 Hibernate 6 中使用自定义命名策略。标准策略根据实体名称生成序列名称,而传统策略和唯一策略则通过使用统一的序列名称来简化序列管理。此外,自定义命名策略还允许我们定制序列命名约定。
Ref:https://www.baeldung.com/hibernate-sequence-naming-strategies