Laravel DB::transaction 不回滚异常
Posted
技术标签:
【中文标题】Laravel DB::transaction 不回滚异常【英文标题】:Laravel DB::transaction not rolling back on exception 【发布时间】:2014-09-16 17:05:26 【问题描述】:我在使用 Laravel 4.2 和 DB::transaction 时遇到了这个小问题。我遇到了事务没有回滚的问题,所以我尝试了最简单的 sn-p 代码并将其放入 routes.php 以进行测试:
routes.php:
DB::transaction(function()
$user = App::make("User");
$user->save();
throw new Exception("Should not create users");
);
...
...
...
Some other code here
我只是尝试在事务关闭中创建用户,并在创建用户后抛出异常以强制回滚事务。我的问题是,即使抛出异常,事务也不会回滚。每次刷新应用程序时,我都会在数据库中获得新用户。相同的代码在本地机器上按预期工作,但在我计划用于生产的服务器上,它根本不会回滚事务。你有什么想法为什么?
编辑:
服务器 mysql:5.1.73-cll - MySQL 社区服务器 (GPLv2)
服务器 PHP:PHP 5.4.30 (cli)(构建时间:2014 年 7 月 19 日 15:22:18)
本地 PHP:5.5.9
本地 MySql:5.6.16
服务器在 CentO 上,而本地机器是 Windows 7。
【问题讨论】:
您在本地和生产环境中是否有相同版本的 PHP 和 MySQL(或您正在使用的任何其他数据库)? 为反映两种环境而进行的编辑。 【参考方案1】:如果事务仍然没有为您回滚,并且您已经在使用 InnoDB
,这可能是由于您在其中运行的查询。
例如,MySQL 有一个错误/功能,其中TRUNCATE <table>;
具有隐式提交,因此在您尝试回滚之前保存了表。解决方法是使用DELETE FROM <table>;
,后跟ALTER TABLE <table> AUTO_INCREMENT = 1;
(如果您希望保留重置 auto_increment id 的行为)。
我建议在php artisan tinker
中尝试一个基本查询,例如:
DB::transaction(function ()
//DB::statement('SET FOREIGN_KEY_CHECKS = 0;');
$autoIncrement = DB::select("SHOW TABLE STATUS LIKE 'users'")[0]->Auto_increment;
$id = DB::table('users')->insertGetId([
'name' => 'Jane Doe',
'email' => 'example@example.com',
'password' => 'password'
]);
DB::table('users')->where('id', $id)->delete();
// ALTER TABLE has implicit commit just like TRUNCATE, so prevents rollback:
//DB::statement("ALTER TABLE `users` AUTO_INCREMENT = " . ((int) $autoIncrement));
//DB::statement("ALTER TABLE `users` AUTO_INCREMENT = ?", [1]); // doesn't work
//DB::statement("ALTER TABLE `users` AUTO_INCREMENT = :incrementStart", ['incrementStart' => 1]); // still doesn't work
//DB::statement('SET FOREIGN_KEY_CHECKS = 1;');
dd("User $id deleted, rolling back...");
);
请注意语句中的不一致之处,其中一处是 'users'
,另一处是 `users`
。有时不加引号的关键字也可以。
请注意无法在原始语句中转义参数的危险,为了安全起见,我必须将$autoIncrement
类型转换为整数。如果有人知道解决方法,请告诉我们!
我已经包含了更多的行来玩,因为查询也可能由于外键约束而失败。
注意勘误表、尚未实现的用例以及 MySQL、PostgreSQL 和其他引擎之间的不一致。这里是龙。
更多信息:
transaction doesn't work on truncate
https://laracasts.com/discuss/channels/eloquent/db-transaction-doesnt-appear-to-rollback-properly
Difference between Laravel's raw SQL functions
Can I use transactions with ALTER TABLE?
【讨论】:
【参考方案2】:如果您在项目中使用多个数据库,则需要指定要在哪个数据库中提交和回滚。
DB::connection('name_of_database')->beginTransaction();
try
//Do something
DB::connection('name_of_database')->commit();
catch (Exception $e)
DB::connection('name_of_database')->rollback();
【讨论】:
【参考方案3】:另一种可能是
中的设置/config/database.php
将引擎的值从 null 更改为 InnoDB
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => 'InnoDB',
],
【讨论】:
【参考方案4】:所以我正在回答我自己的问题。在 MySql 5.5 之前,InnoDb 不是默认存储引擎。在我的情况下,MYISAM 是默认存储引擎,不支持事务。我必须做的是在我的 CPanel 服务器安装的 MySQL 中启用 InnoDB。我必须确保我的 Laravel 迁移中的每个表都是使用 InnoDB 引擎创建的。我通过添加:
$table->engine = "InnoDB";
到每个迁移文件。一旦使用 InnoDB 引擎设置了所有表,事务就会按预期工作。
【讨论】:
以上是关于Laravel DB::transaction 不回滚异常的主要内容,如果未能解决你的问题,请参考以下文章