Laravel7执行数据填充时关闭外键检查仍无法截断表的问题求解
问题根源
Laravel 7 对数据库连接的会话隔离逻辑做了调整:
- Laravel 6 版本中,
Artisan::call()调用命令时会复用当前主线程的数据库连接会话,你在外层设置的SET FOREIGN_KEY_CHECKS=0可以在填充逻辑中生效。 - Laravel 7 及之后版本,
Artisan::call()调用db:seed命令时会独立创建全新的数据库连接会话,外层设置的外键检查关闭状态只作用于主线程会话,填充逻辑运行的新会话中外键检查仍然是开启状态,因此执行truncate()时触发外键约束报错。
解决方法
方案1:将外键开关逻辑移入Seeder内部(最常用)
直接在填充类的run方法中管理外键检查状态,保证和截断、插入逻辑在同一会话生效,修改后的StatusTableSeeder.php代码如下:
public function run() { \DB::statement("SET FOREIGN_KEY_CHECKS=0;"); \DB::table('statuses')->truncate(); \DB::table('statuses')->insert([ [ 'id' => 1, 'name' => 'Current', 'type' => 'current', ], [ 'id' => 2, 'name' => 'Resolved', 'type' => 'resolved', ], ]); \DB::statement("SET FOREIGN_KEY_CHECKS=1;"); }
修改后外层调用代码里的两个DB::statement设置可以直接删除。
方案2:替代truncate操作
如果不想修改外键检查状态,可以用delete()+重置自增ID的方式达到和truncate一致的效果,不会触发DDL级别的外键约束检查:
public function run() { \DB::table('statuses')->delete(); \DB::statement("ALTER TABLE statuses AUTO_INCREMENT = 1;"); // 后续插入逻辑不变 \DB::table('statuses')->insert([ // 插入数据 ]); }
方案3:封装通用基类Seeder
如果有多个填充类都需要截断表,可以封装公共父类避免重复代码:
// 新建基类 Seeder class BaseSeeder extends \Illuminate\Database\Seeder { protected function truncateTable($tableName) { \DB::statement("SET FOREIGN_KEY_CHECKS=0;"); \DB::table($tableName)->truncate(); \DB::statement("SET FOREIGN_KEY_CHECKS=1;"); } } // 子类继承使用 class StatusTableSeeder extends BaseSeeder { public function run() { $this->truncateTable('statuses'); // 插入逻辑 } }
内容的提问来源于stack exchange,提问作者Mdr Kuchhadiya
相关产品推荐
相关产品推荐

