【问题标题】:Laravel get the latest non incrementing value before save without race conditionLaravel 在没有竞争条件的情况下保存之前获取最新的非递增值
【发布时间】:2016-02-26 19:39:50
【问题描述】:

我试图在我的模型中增加一个完全独立于模型的自动递增 ID 的非自动递增整数列。

为了简化我要说的内容,考虑一张放书的桌子。在此表中,我们有四个字段:

Book_id (auto incrementing ID)
book_title  (a string) 
book_contents (Also a string for the purposes of this example)
book_edition (an integer representing the edition of the book. Think College textbooks)

假设我们有一本书叫“哈利波特”,另一本书叫“米老鼠”。我们的数据库目前看起来像这样

id:            1
book_title:    "Harry Potter" 
book_contents: "Book Contents"
book_edition:  1 
--------------------------
id:            2
book_title:    "Mickey Mouse"
book_contents: "This is a book about Mickey"
book_edition:  1 

现在让我们说,出于某种原因,“哈利波特”这本书必须进行重大修改,无论是“导演剪辑版”还是其他类似的书,或者其他原因。该条目将如下所示

id:            3
book_title:    "Harry Potter" 
book_contents: "Updated book contents"
book_edition:  2

我的问题是在 Laravel 5+ 中获取原书版本的最佳方法是什么?

我是否需要实际执行 Model::where( {{ query to get latest }} ) 或者我可以用另一种方式处理这个问题吗?我看到有一个增量函数,但它没有很好地记录

作为第二个问题,如果我确实像上面提到的那样做了一个 Model::where,如果我将它包装在一个 Laravel DB::transaction 中,我是否可以免受可能的竞争条件,即两个人在同时? 示例

function saveBook()
{
    DB::transaction(function ($arr) {

          $latest = Book::where('book_title', '=', 'Harry Potter')->orderBy("book_edition", "desc")->first();

          $this->book_edition = $latest->book_edition + 1;
          $this->save();
      });
}

这里有可能遇到竞态条件吗? 提前致谢!

【问题讨论】:

    标签: php mysql laravel eloquent


    【解决方案1】:

    book_titlebook_edition 创建一个复合唯一索引应该可以从一开始就解决问题:

    ALTER TABLE `books` ADD UNIQUE `unique_edition`(`book_title`, `book_edition`);
    

    在这种情况下添加事务不会提供任何好处,因为您只执行一次写入操作,虽然这取决于读取的结果,但已经受到唯一索引约束的保护,不会受到任何竞争条件的影响。

    要处理独特的异常,您可以这样做:

    function saveBook()
    {
        ...
    
        $latestEdition = Book::where('book_title', 'Harry Potter')->max('book_edition');
        $this->book_edition = $latestEdition + 1;
    
        try {
            $this->save();
        } catch (Illuminate\Database\QueryException $e) {
            // Your checking for 1062 because that's
            // the MySQL error message for duplicate entry
            if ($e->errorInfo[1] == 1062) {
                // set your error message here
            }
        }
    
        ...
    }
    

    您也可以看看使用Pessimistic Locking,但只能作为替代方案,因为如果您已经在使用唯一约束,锁定将是多余的。但在这种情况下,唯一约束更有意义。

    【讨论】:

    • 感谢您的回复。在这种情况下,“独特”是否不起作用,因为可以有多个“第 2 版”,一个用于哈利波特书,一个用于米老鼠?
    • 啊,是的。那种感觉是有道理的。我很清楚,在这种情况下,您是否不想在“book_title”和“book_edition”列而不是 ID 上使用唯一索引约束?也可以用这种方式制作自动递增索引吗?
    • 是的,你说得对,我今天好像有点慢:)。我已经更新了我的答案。
    • 如果抛出了唯一约束异常(如果有人在您的请求能够更新条目之前添加了新版本),您是否会返回错误或重试该操作并再次尝试增加版本。为了更容易理解:如果当前版本是 2,而您正尝试将其更新为 3,但同时有人已将其更新为 3,您是否希望自动重试并同时将其更新为 4请求或只是发送响应通知用户无法更新,他/她应该再试一次?
    • 我已经用一个解决方案更新了我的答案,以处理可能发生独特异常的罕见情况。对获取最新版本的查询进行了小幅改进。
    猜你喜欢
    • 2013-12-23
    • 1970-01-01
    • 2021-02-14
    • 2013-07-16
    • 2023-02-09
    • 2016-01-14
    • 2012-03-31
    • 2020-11-08
    • 1970-01-01
    相关资源
    最近更新 更多