Spring Boot是否通过@GeneratedValue改变ID自增方式?版本差异咨询
你遇到的这个情况我之前帮不少开发者排查过,确实是Spring Boot 2.0.0对Hibernate自动配置做的一处关键变更导致的。在1.5.10版本里,Hibernate默认会给每个JPA实体单独维护自增主键序列,所以Car和Airplane的主键都能从1开始,互相不干扰;但升级到2.0.0之后,默认逻辑变成了所有实体共享一个全局序列,就出现了你描述的Car ID是1,Airplane ID会接着用后续数字的情况。
问题根源
Spring Boot 2.0.0把Hibernate的hibernate.id.new_generator_mappings参数默认值从false改成了true。这个参数控制Hibernate是否启用JPA 2.1规范的新生成器映射:
- 当值为
false(1.5.x版本默认):Hibernate使用旧版生成器逻辑,每个实体都会单独创建自己的序列/表生成器,主键自增完全独立。 - 当值为
true(2.0.0版本默认):Hibernate默认使用SequenceStyleGenerator,如果实体没有显式指定生成器,就会共用一个名为hibernate_sequence的全局序列(适用于PostgreSQL等支持序列的数据库);对于MySQL这类不支持序列的数据库,则会用一个全局的表生成器,自然就导致了主键序列共享。
三种解决方案
1. 快速回退到旧版生成器逻辑
如果你不想折腾,只想快速恢复1.5.x的行为,直接在配置文件里把参数改回去就行:
# application.properties spring.jpa.properties.hibernate.id.new_generator_mappings=false
这种方式简单直接,但缺点是会错过JPA 2.1的一些新特性。
2. 为每个实体显式指定独立生成器(推荐)
如果想保留JPA 2.1的新特性,同时保证实体主键独立,最稳妥的方式是给每个实体单独定义生成器:
针对支持序列的数据库(如PostgreSQL):
Car实体:
@Entity @SequenceGenerator( name = "car_seq", sequenceName = "car_sequence", initialValue = 1, allocationSize = 1 ) public class Car { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "car_seq") private Long id; // 其他字段、构造方法和Getter/Setter }
Airplane实体:
@Entity @SequenceGenerator( name = "airplane_seq", sequenceName = "airplane_sequence", initialValue = 1, allocationSize = 1 ) public class Airplane { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "airplane_seq") private Long id; // 其他字段、构造方法和Getter/Setter }
针对MySQL这类不支持序列的数据库:
改用TableGenerator,给每个实体分配独立的生成器表:
@Entity @TableGenerator( name = "car_table_gen", table = "car_id_generator", pkColumnName = "seq_name", valueColumnName = "seq_value", pkColumnValue = "car_seq", initialValue = 1, allocationSize = 1 ) public class Car { @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "car_table_gen") private Long id; // ... }
Airplane实体同理,只需要把生成器名称、表名等改成对应的即可。
3. 自定义全局生成器策略(进阶)
如果你有大量实体,不想逐个配置,可以自定义Hibernate的生成器逻辑,让它默认给每个实体创建独立序列。这需要扩展SequenceStyleGenerator或者编写自定义IdentifierGenerator,实现起来稍复杂,适合有Hibernate底层经验的开发者。
验证方式
配置完成后启动应用,依次创建Car和Airplane实例,检查它们的主键是否都从1开始,确认序列不再共享即可。
内容的提问来源于stack exchange,提问作者Florian Laforest

