Laravel:使用 try...catch 和 DB::transaction()
Posted
技术标签:
【中文标题】Laravel:使用 try...catch 和 DB::transaction()【英文标题】:Laravel: Using try...catch with DB::transaction() 【发布时间】:2014-05-19 08:48:32 【问题描述】:我们都使用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;
相反,DB::transaction()
包装了一个 try...catch:
// Transaction
$exception = DB::transaction(function()
// try...catch
try
// Do your SQL here
catch(Exception $e)
return $e;
);
return is_null($exception) ? true : false;
或者只是一个没有 try...catch 的事务
// Transaction only
$exception = DB::transaction(function()
// Do your SQL here
);
return is_null($exception) ? true : false;
【问题讨论】:
【参考方案1】:我决定回答这个问题,因为我认为它可以使用比复杂的 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!']);
);
;
您应该看到,在此设置中,用户和日志记录不能相互独立。
关于上述实现的一些注意事项:
确保return
处理任何事务,以便您可以使用在其回调中返回的response()
作为控制器的响应。
如果您希望事务回滚(或者有一个自动为您抛出异常的嵌套函数,就像 Eloquent 中的任何 SQL 异常一样),请确保 throw
异常。
id
、updated_at
、created_at
和任何其他字段在创建 $user
对象后可用(至少在此事务期间)。事务将通过您拥有的任何创建逻辑运行。但是,当SomeCustomException
被抛出时,整个记录都会被丢弃。 id
的自动增量列会在事务失败时增加。
在 Laravel 5.8 上测试
【讨论】:
【参考方案2】:我正在使用 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
【讨论】:
【参考方案3】:我知道这可能以前得到了很好的回答,但我想给予我的支持:D。
我有时会这样做。
try
DB::transaction(function () use (/* place here extra variables if you need */)
throw new Exception("Hubo un error en la transacción");
);
// If no errors, you can continue with your common workflow.
catch (Exception $e)
// You can check here because the transaction will auto rollback and then will throw an exception.
return $e->getMessage();
【讨论】:
【参考方案4】:在 laravel 8 中,你可以在 try-catch 中使用 DB::transaction。 例如:
try
DB::transaction(function()
// do anything
);
catch()
// do anything
如果每个查询在尝试时都失败,则运行 catch 块。
【讨论】:
【参考方案5】:您可以将事务包装在 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;
因此您可以像这样编写代码,并处理您的异常,例如通过 Flash 将消息扔回您的表单或重定向到另一个页面。记住闭包内的 return 在 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;
那么替代方法是抛出布尔变量并在事务函数之外处理重定向,或者如果您需要检索事务失败的原因,您可以从$e->getMessage()
内部catch(Exception $e)...
获取它
【讨论】:
我使用了没有 try-catch 块的事务,效果也很好 @hamidrezasamsami 是的,数据库自动回滚,但有时您需要知道查询是否全部成功.. “Wrap Transaction”示例是错误的。这将始终提交,即使其中一个查询失败,因为所有异常都在事务回调中捕获。您想将 try/catch 放在 DB::transaction 之外。【参考方案6】:如果您使用 PHP7,请在 catch
中使用 Throwable 来捕获用户异常和致命错误。
例如:
DB::beginTransaction();
try
DB::insert(...);
DB::commit();
catch (\Throwable $e)
DB::rollback();
throw $e;
如果您的代码必须与 PHP5 兼容,请使用 Exception
和 Throwable
:
DB::beginTransaction();
try
DB::insert(...);
DB::commit();
catch (\Exception $e)
DB::rollback();
throw $e;
catch (\Throwable $e)
DB::rollback();
throw $e;
【讨论】:
DB::beginTransaction() 也可能抛出 \Exception 的事实如何?是否应该包含在 try/catch 中? 如果事务还没有开始,我们不需要回滚任何东西。此外,在catch
块中尝试回滚未启动的事务是没有好处的。因此DB::beginTransaction()
的好地方是在try
块之前。【参考方案7】:
如果您需要通过代码手动“退出”事务(无论是通过异常还是只是检查错误状态),您不应使用 DB::transaction()
,而应将代码包装在 DB::beginTransaction
和 @987654324 中@/DB::rollback()
:
DB::beginTransaction();
try
DB::insert(...);
DB::insert(...);
DB::insert(...);
DB::commit();
// all good
catch (\Exception $e)
DB::rollback();
// something went wrong
请参阅transaction docs。
【讨论】:
DB::beginTransaction()
和DB:transaction()
有什么区别?
DB::transaction
接受一个匿名函数以使 DB 语句在事务中运行,DB::beginTransaction()
要求将 DB 语句写在调用的“旁边”(根据上面的示例),然后一个最终的DB::commit()
或DB::rollback()
来完成交易。
简单问题:如果您在异常后不回滚,或者如果您不捕获异常,会发生什么?脚本结束后自动回滚?
不幸的是我不知道,但是是的,我会想象事务保持打开状态,愉快地吞下更多的数据库插入/更新,然后最终当应用程序结束时,整个事情都回滚了。拼凑一个快速测试脚本来尝试它会很容易。您可能会发现“事务未退出”样式异常以及自动回滚。
@HengSopheak 这个问题是关于 Laravel 4 数据库的,所以很可能我的答案对于 5.3 不再正确。可能值得你用 Laravel 5.3 标签提出一个新问题以获得正确的社区支持。以上是关于Laravel:使用 try...catch 和 DB::transaction()的主要内容,如果未能解决你的问题,请参考以下文章
测试 Laravel Controller 的 try catch