如何让基于JPA的旧Java应用与Quarkus新应用共用MySQL的主键生成序列?
嘿,这个问题我之前帮朋友处理过类似的,核心原因是两个应用对JPA的@GeneratedValue(strategy = GenerationType.AUTO)的实现逻辑不一样——旧应用用了全局共享的主键序列(所有表共用一个计数器),而Quarkus默认适配了MySQL的表级自增主键,两边的生成逻辑不同步,才会出现重复主键的冲突。我给你梳理下解决步骤:
先搞清楚旧应用的主键生成机制
登录MySQL数据库,先检查有没有类似hibernate_sequence的表(或者其他自定义命名的序列表)。这类表一般只有一两行数据,存着当前的序列值(比如字段叫next_val)。如果存在这个表,那旧应用的JPA提供者(比如Hibernate)就是靠这个全局表来给所有实体生成主键的——这也能解释你说的“产品1、客户2、产品3”这种跨表连续主键的情况。让Quarkus应用完全适配旧应用的序列策略
既然不能改旧应用,那只能让新的Quarkus应用对齐旧的生成规则。你需要把Quarkus实体类的主键注解从默认的@GeneratedValue(strategy = GenerationType.AUTO)改成显式的全局表生成策略,参数要和旧应用完全匹配:
比如旧应用用的是Hibernate默认的hibernate_sequence表,那你的实体主键应该这么写:@Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "global_seq") @TableGenerator( name = "global_seq", table = "hibernate_sequence", // 和旧应用的序列表名完全一致 valueColumnName = "next_val", // 存储当前序列值的字段名,按旧表结构来 allocationSize = 1 // 每次递增的步长,必须和旧应用一致,别用默认的50! ) private Long id;如果旧应用用的是自定义的序列表(比如叫
app_global_sequence,字段是seq_name和current_val),那就要补充对应的参数:@TableGenerator( name = "global_seq", table = "app_global_sequence", pkColumnName = "seq_name", pkColumnValue = "default_global", // 旧应用对应的序列标识值 valueColumnName = "current_val", allocationSize = 1 )重点提醒:
allocationSize一定要和旧应用保持一致!如果旧应用每次只递增1,你这里用了默认的50,新应用生成的主键会直接跳50,肯定会和旧应用的主键冲突。验证配置是否生效
启动Quarkus应用,插入一条测试记录,然后去看全局序列表的数值是不是正确递增了。接着用旧应用插一条记录,确认它拿到的是序列表的下一个值,不会出现重复主键的错误。别再手动赋值主键了
你之前手动给主键赋值的做法会绕过JPA的序列生成机制,导致全局序列表的数值没有更新——旧应用下次生成主键时还是会用旧的序列值,自然就冲突了。一定要让JPA自动生成主键,别手动干预。
如果检查下来旧应用并没有用全局序列表,而是每个表自己的自增主键(但你描述的跨表连续主键情况不太符合这个逻辑),那可能是旧应用的JPA配置有特殊处理,这时候你需要把Quarkus的主键策略也改成GenerationType.IDENTITY,确保每个表用自己的AUTO_INCREMENT字段,两边就不会互相干扰了。
备注:内容来源于stack exchange,提问作者Boni Machado

