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 不回滚异常的主要内容,如果未能解决你的问题,请参考以下文章

Laravel DB::transaction 不回滚异常

DB::transaction() 可以将 Eloquent 查询作为事务执行吗?

Laravel lockforupdate(悲观锁)

Laravel 事务未读取参数

事务在 laravel 中不起作用

Laravel Eloquent ORM 事务