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
相关产品推荐
相关产品推荐

