Laravel:使用try ... catch DB :: transaction()

enc*_*nce 61 php transactions laravel

我们都使用DB::transaction()多个插入查询.这样做,是应该try...catch放在里面还是包裹它?甚至有必要包括一个事件try...catch在出现问题时会自动失败的情况吗?

try...catch包装交易的示例:

// try...catch
try {
    // Transaction
    $exception = DB::transaction(function() {

        // Do your SQL here

    });

    if(is_null($exception)) {
        return true;
    } else {
        throw new Exception;
    }

}
catch(Exception $e) {
    return false;
}
Run Code Online (Sandbox Code Playgroud)

相反,DB::transaction()包装尝试...抓住:

// Transaction
$exception = DB::transaction(function() {
    // try...catch
    try {

        // Do your SQL here

    }
    catch(Exception $e) {
        return $e;
    }

});

return is_null($exception) ? true : false;
Run Code Online (Sandbox Code Playgroud)

或者只是一个没有尝试的交易......捕获

// Transaction only
$exception = DB::transaction(function() {

    // Do your SQL here

});

return is_null($exception) ? true : false;
Run Code Online (Sandbox Code Playgroud)

ale*_*ell 142

如果您需要通过代码手动"退出"事务(通过异常或只是检查错误状态),您不应该使用DB::transaction(),而是将代码包装在/ DB::beginTransaction和:DB::commitDB::rollback()

DB::beginTransaction();

try {
    DB::insert(...);
    DB::insert(...);
    DB::insert(...);

    DB::commit();
    // all good
} catch (\Exception $e) {
    DB::rollback();
    // something went wrong
}
Run Code Online (Sandbox Code Playgroud)

查看交易文档.

  • 一个简单的问题:如果您在异常发生后不进行回滚,或者您没有捕获异常,会发生什么?脚本结束后是否自动回滚? (2认同)
  • @HengSopheak 这个问题是关于 Laravel 4 数据库的,所以我的答案很可能不再适用于 5.3。您可能值得用 Laravel 5.3 标签提出一个新问题,以获得正确的社区支持。 (2认同)

mnv*_*mnv 18

如果你使用PHP7,使用的Throwablecatch捕捉用户异常和致命错误.

例如:

DB::beginTransaction();

try {
    DB::insert(...);    
    DB::commit();
} catch (\Throwable $e) {
    DB::rollback();
    throw $e;
}
Run Code Online (Sandbox Code Playgroud)

如果您的代码必须与PHP5兼容,请使用ExceptionThrowable:

DB::beginTransaction();

try {
    DB::insert(...);    
    DB::commit();
} catch (\Exception $e) {
    DB::rollback();
    throw $e;
} catch (\Throwable $e) {
    DB::rollback();
    throw $e;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果尚未启动事务,则不需要回滚任何内容.另外,在`catch`块中尝试回滚未启动事务是不好的.因此`DB :: beginTransaction()`的好地方是在`try`块之前. (2认同)

Fla*_*ame 10

我决定回答这个问题,因为我认为可以使用比复杂的 try-catch 块更简单的语法来解决这个问题。Laravel 文档关于这个主题非常简短。

DB::transaction(){...}您可以像这样使用包装器,而不是使用 try-catch :

// MyController.php
public function store(Request $request) {
    return DB::transaction(function() use ($request) {
        $user = User::create([
            'username' => $request->post('username')
        ]);

        // Add some sort of "log" record for the sake of transaction:
        $log = Log::create([
            'message' => 'User Foobar created'
        ]);

        // Lets add some custom validation that will prohibit the transaction:
        if($user->id > 1) {
            throw AnyException('Please rollback this transaction');
        }

        return response()->json(['message' => 'User saved!']);
    });
};
Run Code Online (Sandbox Code Playgroud)

您应该看到,在此设置中,用户和日志记录不能单独存在。

关于上述实现的一些注意事项:

  • 确保return事务的任何内容,以便您可以使用response()在其回调中返回的内容作为控制器的响应。
  • throw如果您希望事务回滚,请确保异常(或者有一个嵌套函数自动为您抛出异常,就像 Eloquent 中的任何 SQL 异常一样)。
  • 、和任何其他id字段在创建对象后可用(至少在此事务的持续时间内)。该事务将通过您拥有的任何创建逻辑运行。但是,抛出时整个记录将被丢弃。尽管事务失败,但自动增量列确实会增加。updated_atcreated_at$userSomeCustomExceptionid

在 Laravel 5.8 上测试


Ang*_*aya 8

您可以将事务包装在try..catch上,甚至可以反转它们,这里是我在laravel 5中使用的示例代码,如果您深入了解内部DB:transaction(),Illuminate\Database\Connection就像您编写手动事务一样,

Laravel交易

public function transaction(Closure $callback)
    {
        $this->beginTransaction();

        try {
            $result = $callback($this);

            $this->commit();
        }

        catch (Exception $e) {
            $this->rollBack();

            throw $e;
        } catch (Throwable $e) {
            $this->rollBack();

            throw $e;
        }

        return $result;
    }
Run Code Online (Sandbox Code Playgroud)

所以你可以像这样写你的代码,并处理你的异常,例如通过flash将信息扔回你的表单或重定向到另一个页面.REMEMBER返回内部闭包是在transaction()中返回的,所以如果你返回redirect()->back()它将不会立即重定向,因为它返回处理事务的变量.

包裹交易

$result = DB::transaction(function () use ($request, $message) {
   try{

      // execute query 1
      // execute query 2
      // ..

      return redirect(route('account.article'));

   } catch (\Exception $e) {
       return redirect()->back()
          ->withErrors(['error' => $e->getMessage());
    }
 });

// redirect the page
return $result;
Run Code Online (Sandbox Code Playgroud)

然后替代方法是抛出布尔变量并处理事务函数外的重定向,或者如果您需要检索为什么事务失败,您可以从$e->getMessage()内部获取它catch(Exception $e){...}

  • "Wrap Transaction"示例是错误的.这将始终提交,即使其中一个查询失败,因为所有异常都在事务回调中捕获.您想将try/catch放在DB :: transaction之外. (4认同)

oma*_*ari 5

我使用的是 Laravel 8,您应该将事务包装在 try-catch 中,如下所示:

try {
    DB::transaction(function () {
        // Perform your queries here using the models or DB facade
    });
}
catch (\Throwable $e) {
    // Do something with your exception
}
Run Code Online (Sandbox Code Playgroud)