Java GeneratedValue序列ID异常:allocationSize=1作用解析及有效性确认
问题解析与解答
1. 未设置allocationSize时ID数值偏大的原因
这其实是JPA序列生成器的默认预分配机制在搞事情:
- 当你没显式指定
allocationSize时,JPA(比如Hibernate这类常用实现)默认的allocationSize值是50。 - 意思就是,每次应用启动或者预分配的ID用完时,它会一次性从数据库的
FEATURE_INSTALLED_SEQ序列里捞50个ID存在内存里,之后新插入实体时直接从内存里取ID,不用每次都去数据库请求。 - 这就导致数据库里的序列值会直接跳50(比如从1跳到51,再跳到101),而你实际插入的ID是内存里逐个分配的(1、2、3…50)。如果中途重启应用,那些预分配但没用到的ID就浪费了,下次会再取50个,所以你看到的数据库序列数值会比实际用的ID大很多,本质是预分配带来的“数值跳跃”。
2. allocationSize=1代码的有效性确认
你设置allocationSize = 1后的代码完全有效,刚好能解决上面的问题:
- 这时JPA会每次插入实体都实时去数据库序列取最新值,不再预分配批量ID,数据库序列的增长步长和你实际插入的ID会完全对应,不会再出现数值大幅跳跃的情况。
- 要注意的是:得确保数据库里
FEATURE_INSTALLED_SEQ序列的INCREMENT BY参数也是1(这一般是默认值),如果序列的步长不是1,那allocationSize必须和序列步长保持一致,不然会出现ID冲突或者序列值浪费的问题。
3. 关于FEATURE_INSTALLED_ID为NULL时的触发器的注意事项
这里要重点留意触发器和JPA序列生成的冲突问题:
- 当你设置
allocationSize=1后,JPA在插入实体前就会从序列拿到ID并赋值给featureInstalledId字段,所以插入到数据库时这个字段不会是NULL,触发器不会被触发。 - 如果你的触发器是用来在
FEATURE_INSTALLED_ID为NULL时自动生成ID(比如也用同一个序列),那现在JPA已经提前占了序列值,触发器再去取的话就会导致ID重复,引发主键冲突。 - 建议你统一ID生成方式:要么全用JPA的序列生成,要么禁用JPA的自动生成逻辑,只用触发器生成ID,避免两者同时工作出问题。
内容的提问来源于stack exchange,提问作者Adder




