Laravel:使用try…catch与DB :: transaction()


81

我们都使用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;

或只是一笔没有交易的交易...赶上

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

    // Do your SQL here

});

return is_null($exception) ? true : false;

Answers:


179

如果您需要通过代码手动“退出”交易(通过异常还是只是检查错误状态),则不应使用DB::transaction(),而是将代码包装在DB::beginTransactionand DB::commit/中DB::rollback()

DB::beginTransaction();

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

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

请参阅交易文档


再次查看之后,这就是我想要的答案。:)
增强魅力

@alexrussell-数据库不会生成不同的内容\Exception吗?我可以用这个泛型\Exception吗?太好了!
Artur Mamedov

DB::beginTransaction()和之间有什么区别DB:transaction()
Hamed Kamrava '16

2
一个简单的问题:如果您在异常发生后不进行回滚,或者您没有捕获异常,会发生什么?脚本结束后是否自动回滚?
neoteknic

2
@HengSopheak这个问题是关于Laravel 4数据库的,所以我的答案很可能不再适用于5.3。您可能需要问一个带有Laravel 5.3标签的新问题,以获得正确的社区支持。
alexrussell '16

23

如果使用PHP7,请使用Throwable incatch捕获用户异常和致命错误。

例如:

DB::beginTransaction();

try {
    DB::insert(...);    
    DB::commit();
} catch (\Throwable $e) {
    DB::rollback();
    throw $e;
}

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

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中吗?
Michael Pawlowsky '18

4
如果事务尚未开始,则无需回滚任何内容。此外,尝试在catch块中回滚未启动的事务是不好的。因此,好地方DB::beginTransaction()是在try封锁之前。
mnv

11

你可以在在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将消息返回到表单或重定向到另一页。REMEMBER 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块的事务,它也运行良好
hamidreza samsami,2017年

@hamidrezasamsami是,数据库自动回滚,但有时你需要知道的是查询全部成功或不..
昂嘎阿里甲亮

6
“包装交易”示例是错误的。即使其中一个查询失败,因为所有异常都在事务回调中捕获,这将始终提交。您想将try / catch放在DB :: transaction之外。
redmallard

2

我决定对此问题给出一个答案,因为我认为可以使用比卷积try-catch块更简单的语法来解决它。Laravel文档对此主题非常简短。

除了使用try-catch之外,您还可以DB::transaction(){...}像这样使用包装器:

// 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()在其回调中使用返回值。
  • throw如果要回滚事务,请确保有一个异常(或者有一个嵌套的函数可以自动为您抛出异常,例如Eloquent中的SQL异常)。
  • idupdated_atcreated_at和任何其它字段是为可用之后CREATION$user对象(此事务处理的持续时间)。事务将通过您拥有的任何创建逻辑来运行。但是,AnyException抛出时,整个记录将被丢弃。这意味着,例如id,失败事务的自动递增列的确会增加。

在Laravel 5.8上测试


疯狂的没有人提到这种明确的方法
亚当
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.