Laravel:使用带有DB::transaction()的try...catch

zte4gxcn  于 2023-01-06  发布在  其他
关注(0)|答案(7)|浏览(359)

我们都使用DB::transaction()进行多个插入查询。在这样做的时候,try...catch应该放在它里面还是 Package 它呢?如果出现错误,事务会自动失败,那么是否有必要包括try...catch呢?
Package 事务的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() Package 了一个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;
jjhzyzn0

jjhzyzn01#

如果您需要通过代码手动“退出"事务(无论是通过异常还是简单地检查错误状态),您不应该使用DB::transaction(),而是将代码 Package 在DB::beginTransactionDB::commit/DB::rollback()中:

DB::beginTransaction();

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

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

见业务单据

nfs0ujit

nfs0ujit2#

如果使用PHP7,请在catch中使用Throwable来捕获用户异常和致命错误。
例如:

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;
}
eni9jsuy

eni9jsuy3#

你可以把事务 Package 在try..catch上,甚至反转它们,这是我在laravel 5中使用的示例代码,如果你深入研究DB:transaction(),就像你编写手动事务一样。

拉腊维尔交易

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(),它不会立即重定向,因为它返回的是处理事务的变量。

打包事务处理

try {
    $result = DB::transaction(function () use ($request, $message) {
        // execute query 1
        // execute query 2
        // ..
    });          
    // redirect the page
    return redirect(route('account.article'));
} catch (\Exception $e) {
    return redirect()->back()->withErrors(['error' => $e->getMessage()]);
}

那么另一种方法是抛出布尔变量并在事务函数外处理重定向,或者如果需要检索事务失败的原因,则可以从catch(Exception $e){...}内的$e->getMessage()获取

jchrr9hc

jchrr9hc4#

我决定给予这个问题的答案,因为我认为可以使用比复杂的try-catch块更简单的语法来解决这个问题。Laravel文档在这个问题上非常简短。
除了使用try-catch,您还可以使用DB::transaction(){...} Package 器,如下所示:

// 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和任何其他字段在为$user对象创建之后可用(至少在此事务的持续时间内)。事务将通过您拥有的任何创建逻辑运行。但是,当抛出SomeCustomException时,整个记录将被丢弃。id的自动递增列在失败的事务上也会递增。
    在Laravel 5.8上测试
hjzp0vay

hjzp0vay5#

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

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

waxmsbnn6#

在laravel 8中,您可以在try-catch中使用DB::transaction。例如:

try{
    DB::transaction(function() {
        // do anything
    });
}
catch(){
    // do anything
}

如果每一个查询在尝试时失败,则运行捕获块。

mzmfm0qo

mzmfm0qo7#

第一:在Laravel中使用PostgreSQL数据库会使事情变得更加棘手。
如果您在事务错误后不回滚,则以后的每个查询都将抛出此错误 * 在失败的sql事务中:错误:当前事务被中止,命令被忽略,直到事务块 * 结束。因此,如果你不能在回滚之前将原始错误消息保存在表中。

try {
    DB::beginTransaction(); //start transaction
    $user1 = User::find(1);
    $user1->update(['money' => 'not_a_number']); //bad update
}
catch(Exception $exception) {
    $user2 = User::find(2); // ko, "In failed sql transaction" error
    $user2->update(['field' => 'value']);
}

try {
    DB::beginTransaction(); //start transaction
    $user1 = User::find(1);
    $user1->update(['money' => 'not_a_number']); //bad update
}
catch(Exception $exception) {
    DB::rollBack();
    $user2 = User::find(2); // ok, go on
    $user2->update(['field' => 'value']);
}

第二:重视口才模型属性体系。
Eloquent模型在更新错误后会保留更改的属性,所以如果我们想在catch块中更新该模型,我们需要丢弃坏属性。这不是dbtransaction事务,所以rollback命令是无用的。

try {
    DB::beginTransaction(); //start transaction
    $user1 = User::find(1);
    $user1->update(['money' => 'not_a_number']); //bad update
}
catch(Exception|Error $exception) {
    DB::rollBack();
    $user1->update(['success' => 'false']); // ko, bad update again
}

try {
    DB::beginTransaction(); //start transaction
    $user1 = User::find(1);
    $user1->update(['money' => 'not_a_number']); //bad update
}
catch(Exception|Error $exception) {
    DB::rollBack();
    $user1->discardChanges(); // remove attribute changes from model
    $user1->update(['success' => 'false']); // ok, go on
}

相关问题