php Laravel DB::transaction not rolling back on exception

i86rm4rw  于 2023-04-04  发布在  PHP
关注(0)|答案(4)|浏览(132)

我在使用Laravel 4.2和DB::transaction时遇到了一个小问题。我遇到了一个事务不能回滚的问题,所以我尝试了最简单的代码片段,并将其放入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 Community Server(GPLv2)
Server PHP:PHP 5.4.30(cli)(built:2014年7月19日15:22:18)
本地PHP:5.5.9
本地MySql:5.6.16
服务器位于CentOs上,而本地计算机是Windows 7。

6ljaweal

6ljaweal1#

所以我在回答我自己的问题。InnoDb不是默认的存储引擎,直到MySql 5.5。在我的情况下,MYISAM是默认的存储引擎,不支持事务。我必须做的是在我的MySQL的CPanel服务器安装中启用InnoDB。然后我必须确保我的Laravel迁移中的每个表都是使用InnoDB引擎创建的。我通过添加:

$table->engine = "InnoDB";

一旦所有的表都用InnoDB引擎设置好了,事务就可以按预期工作了。

m4pnthwp

m4pnthwp2#

另一种方法是使用
/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',
        ],
rm5edbpk

rm5edbpk3#

1.确保你已经在databases.php连接数组配置中设置了'engine' => 'InnoDB'
1.如果在项目中使用多个数据库连接,则需要指定要在哪个连接中提交和回滚。

DB::connection('name_of_connection')->beginTransaction();
    try {
        //Do something
        DB::connection('name_of_connection')->commit();
    } catch (Exception $e) {
        DB::connection('name_of_connection')->rollback();
    }
vyswwuz2

vyswwuz24#

如果事务仍然没有为您回滚,并且您已经在使用InnoDB,则可能是由于您在内部运行的查询。
例如,MySQL有一个bug/特性,其中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?

相关问题