You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Grails 2.5双服务器轮询下GORM主键重复问题求助

解决Grails多服务器环境下主键重复问题

1. 确保子类assigned生成策略完全生效

父类用identity(依赖数据库自增),子类配置assigned后没效果,大概率是未在保存前显式给ID赋值或赋值时机不对:

  • 创建ActualEntity1实例时,必须在调用save()前明确给id字段赋值,示例代码:
    def entity = new ActualEntity1(id: getNextValidId(), ...)
    entity.save(flush: true)
    
  • 获取ID时必须用数据库原子操作,避免多服务器并发取到重复值,比如MySQL中用以下语句安全获取下一个ID:
    SELECT MAX(id) + 1 FROM actual_entity_1;
    
    更稳妥的方式是创建独立序列表,通过SELECT ... FOR UPDATE或INSERT ... ON DUPLICATE KEY UPDATE实现原子化ID生成,彻底杜绝并发冲突。

2. 禁用Hibernate的ID生成缓存

如果子类实际仍继承父类的identity生成策略(配置未成功覆盖),可通过以下方式强制禁用ID缓存:

  • 在子类映射中添加缓存禁用参数:
    class ActualEntity1 extends BaseEntity {
        static mapping = {
            id generator: 'assigned', params: [cache: 'false']
        }
    }
    
  • 修改DataSource.groovy中的Hibernate配置,启用新生成器映射:
    hibernate {
        id.new_generator_mappings = true
    }
    

Grails 2.5默认该配置为false,启用后会使用更可靠的生成器实现,减少缓存引发的问题。

3. 改用native生成器替代assigned

若无需手动设置ID,可将子类的ID生成器改为native,让Hibernate适配MySQL自增机制:

class ActualEntity1 extends BaseEntity {
    static mapping = {
        id generator: 'native'
    }
}

native生成器完全依赖数据库的AUTO_INCREMENT生成ID,多服务器轮询场景下不会出现主键重复。

4. 排查数据库表结构与映射配置

由于父类设置了tablePerConcreteClass true,每个子类拥有独立数据表,需确认:

  • 若子类使用assigned策略,actual_entity_1表的主键需去掉AUTO_INCREMENT属性,否则数据库会自动生成ID,与手动设置的ID冲突。
  • 执行grails schema-export命令导出DDL,验证GORM生成的表结构是否符合预期,重点检查主键配置。

5. 临时强制刷新会话(仅用于排查)

若Hibernate会话缓存了旧的ID状态,可在保存前强制刷新会话:

def entity = new ActualEntity1(...)
entity.id = getNextValidId()
sessionFactory.currentSession.flush()
sessionFactory.currentSession.clear()
entity.save(flush: true)

此方法仅用于排查问题,不建议作为常规解决方案。


内容的提问来源于stack exchange,提问作者Prakhar Mishra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 04:56:29