Doctrine迁移:避免重复创建meal_moment ID0及ID替换问题
问题分析与解决方案
核心问题
- ID=0被自动替换为1:
meal_moment表的id字段是自增主键(AUTO_INCREMENT),MySQL默认SQL_MODE下,向自增字段插入0会被视为请求生成下一个自增ID,而非直接保留0值。 - 部分环境迁移崩溃:原代码未检查ID=0的记录是否已存在,直接插入会触发主键重复冲突报错,导致迁移中断。
修正后的迁移代码
up 方法
public function up(Schema $schema): void { // 临时设置SQL_MODE,允许向自增字段插入0值 $this->addSql("SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';"); // 仅当ID=0的meal_moment不存在时才执行插入 $this->addSql(" INSERT INTO `meal_moment` (`id`, `name`, `code`, `ordre`, `customer_id`) SELECT 0, 'None', 'None', 0, 0 WHERE NOT EXISTS (SELECT 1 FROM `meal_moment` WHERE id = 0); "); // 恢复默认SQL_MODE(可根据实际环境调整是否保留) $this->addSql("SET sql_mode = '';"); // 更新order表中mealmoment_id为NULL的记录 $this->addSql("UPDATE `order` SET mealmoment_id = 0 WHERE mealmoment_id IS NULL;"); }
down 方法(优化回滚逻辑)
public function down(Schema $schema): void { // 回滚本次迁移设置的mealmoment_id=0记录(若需区分原有合法0值,可结合业务标记调整) $this->addSql("UPDATE `order` SET mealmoment_id = NULL WHERE mealmoment_id = 0;"); // 仅当ID=0的meal_moment存在时执行删除 $this->addSql("DELETE FROM `meal_moment` WHERE id = 0;"); }
关键说明
NO_AUTO_VALUE_ON_ZERO:该SQL_MODE会让MySQL将自增字段的0值视为普通数值,而非触发自增逻辑,确保ID=0能被正确插入。INSERT ... SELECT + NOT EXISTS:通过判断记录是否存在避免重复插入,彻底解决主键冲突导致的迁移崩溃问题。- 分步骤执行SQL:拆分不同逻辑的执行步骤,提升迁移的稳定性和可调试性。
额外建议
- 检查
meal_moment表主键定义:如果业务不需要id自增,可直接移除AUTO_INCREMENT属性,从根源避免此类问题。 - 验证回滚逻辑:在测试环境完整跑一遍迁移和回滚流程,确保不会误修改原有合法业务数据。
内容的提问来源于stack exchange,提问作者kekaaafm
相关产品推荐
相关产品推荐

