Laravel雄辩的ORM交易


96

雄辩的ORM很好,尽管我想知道是否有一种简单的方法可以以与PDO相同的方式使用innoDB设置MySQL事务,或者是否必须扩展ORM才能实现这一点?

Answers:


165

你可以这样做:

DB::transaction(function() {
      //
});

关闭内部的所有事务都在事务内执行。如果发生异常,它将自动回滚。


1
在闭包内部,我可以在类中调用查询吗?会行吗?
拉斐尔·索弗拉斯2015年

不幸的是,如果我创建不同模型的实例并以自己的相关方法存储记录,那么这对我来说是行不通的。
Volatil3

如果我在事务中捕获到异常(用于生成错误消息等),是否需要重新发送异常以使回滚发生?
alexw '16

3
好的答案,但有两点使我失望:1.您需要添加“ use DB”;为此,例如在模型文件的顶部2。与JS不同,除非您显式地传递它们,否则您将无法访问父作用域中的局部变量,因此需要添加“ use”结构。 :: transaction(function()use($ user){...引用$ user ...的东西);
Polsonby

Discussed in more detail here链接已死。
tomloprod '16

100

如果您不喜欢匿名函数:

try {
    DB::connection()->pdo->beginTransaction();
    // database queries here
    DB::connection()->pdo->commit();
} catch (\PDOException $e) {
    // Woopsy
    DB::connection()->pdo->rollBack();
}

更新:对于laravel 4,该pdo对象不再公开,因此:

try {
    DB::beginTransaction();
    // database queries here
    DB::commit();
} catch (\PDOException $e) {
    // Woopsy
    DB::rollBack();
}

15
您也可以使用快捷方法DB::beginTransaction()DB::commit()DB::rollback()。那会更清洁一点。
Flori 2014年

2
请更新以使用@Flori建议。比较干净。同样,将新答案向上移动将使您的答案更容易混淆。在使用第二种方法之前,我使用了第一种方法。
frostymarvelous 2014年

对于旧版本的Laravel,您可能需要:DB::connection()->getPdo()->beginTransaction();
取而代之的是

我个人认为DB::transactionwith回调更加干净,但是缺点是,如果您需要为不同的异常指定不同的处理程序,则必须返回尝试/捕获技术
OzzyTheGiant

33

如果您想使用Eloquent,也可以使用

这只是我项目中的示例代码

        /* 
         * Saving Question
         */
        $question = new Question;
        $questionCategory = new QuestionCategory;

        /*
         * Insert new record for question
         */
        $question->title = $title;
        $question->user_id = Auth::user()->user_id;
        $question->description = $description;
        $question->time_post = date('Y-m-d H:i:s');

        if(Input::has('expiredtime'))
            $question->expired_time = Input::get('expiredtime');

        $questionCategory->category_id = $category;
        $questionCategory->time_added = date('Y-m-d H:i:s');

        DB::transaction(function() use ($question, $questionCategory) {

            $question->save();

            /*
             * insert new record for question category
             */
            $questionCategory->question_id = $question->id;
            $questionCategory->save();
        });

question->id事务回调中的表达式返回零。
Christos Papoulas

@ChristosPapoulas您的意思是,我们无法获取交易中的自动增量ID?
hellojinjie

26

如果您想避免闭包,并乐于使用立面,则可以使事情保持整洁:

try {
    \DB::beginTransaction();

    $user = \Auth::user();
    $user->fill($request->all());
    $user->push();

    \DB::commit();

} catch (Throwable $e) {
    \DB::rollback();
}

如果有任何语句失败,则提交将永远不会执行,并且事务也不会处理。


如果任何语句失败,则后续语句将不会运行。您仍然需要显式回滚事务。
杰森

1
@Jason我已经更新了答案。对于大多数(所有?)数据库引擎,我是否有想法,当连接终止时,任何未提交的事务查询都不会提交。但是,我同意您的发言,并且最好是明确
克里斯(Chris)

18

我确定您不是要寻找封闭解决方案,请尝试使用此解决方案以获取更紧凑的解决方案

 try{
    DB::beginTransaction();

    /*
     * Your DB code
     * */

    DB::commit();
}catch(\Exception $e){
    DB::rollback();
}

10

由于某些原因,在任何地方都很难找到该信息,所以我决定将其发布在这里,因为我的问题虽然与Eloquent交易有关,但正好改变了这一点。

阅读了这个 stackoverflow的答案之后,我意识到我的数据库表使用的是MyISAM而不是InnoDB。

为了使事务在Laravel上(或其他任何地方)运行,需要将表设置为使用InnoDB

为什么?

引用MySQL 事务和Atomic Operations文档(此处):

MySQL服务器(版本3.23-max和所有版本4.0及更高版本)支持使用InnoDB和BDB事务存储引擎的事务。InnoDB提供了完全的ACID合规性。请参阅第14章,存储引擎。有关InnoDB与标准SQL在事务错误处理方面的区别的信息,请参见第14.2.11节“ InnoDB错误处理”。

MySQL Server中的其他非事务性存储引擎(例如MyISAM)遵循称为“原子操作”的数据完整性的另一范式。用事务术语来说,MyISAM表始终始终在自动提交= 1模式下运行。原子操作通常提供可比的完整性和更高的性能。

因为MySQL Server支持这两种范例,所以您可以决定通过原子操作的速度还是使用事务功能来最好地服务于应用程序。可以按表进行选择。


对于DML,这是正确的,而对于DDL,并非总是如此。
Yevgeniy Afanasyev

4

如果发生任何异常,则事务将自动回滚。

Laravel基本交易格式

    try{
    DB::beginTransaction();

    /* 
    * SQL operation one 
    * SQL operation two
    ..................     
    ..................     
    * SQL operation n */


    DB::commit();
   /* Transaction successful. */
}catch(\Exception $e){       

    DB::rollback();
    /* Transaction failed. */
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.