mpyw/laravel-retry-on-duplicate-key

此包已废弃,不再维护。未建议替代包。

当违反唯一约束时,自动重试非原子性插入更新操作。

v1.3.3 2023-03-15 02:21 UTC

This package is auto-updated.

Last update: 2023-11-21 17:19:31 UTC


README

注意

废弃:由于Laravel v10.29.0 的变化,此库的功能已集成到Laravel核心中,因此在此大多数情况下不再需要此库。因此,维护将停止。 从现在开始,重试处理将在 Model::createOrFirst()Model::firstOrCreate()Model::updateOrCreate() 中自动执行。唯一仍有价值的是 Model::firstOrNew() + save() 模式,但由于等效处理可以由自己编写,请不要再使用此库。

当违反唯一约束时,自动重试 非原子性 插入更新操作。

例如:firstOrCreate() updateOrCreate() firstOrNew() + save()

原始问题: 更新或创建时的重复条目 · 问题 #19372 · laravel/framework

要求

版本 必需
PHP ^8.0
Laravel ^9.0 || ^10.0
mpyw/laravel-unique-violation-detector ^1.0
PHPStan >=1.1

安装

composer require mpyw/laravel-retry-on-duplicate-key

基本用法

重要

默认实现由 ConnectionServiceProvider 提供,但 包发现不可用。请注意,您必须自己将其注册到 config/app.php

<?php

return [

    /* ... */

    'providers' => [
        /* ... */

        Mpyw\LaravelRetryOnDuplicateKey\ConnectionServiceProvider::class,

        /* ... */
    ],

];
<?php

use Illuminate\Support\Facades\DB;

$user = DB::retryOnDuplicateKey(function () {
    // Email has a unique constraint
    return User::firstOrCreate(['email' => 'example.com'], ['name' => 'Example']);
});
其他
选择
(无结果)
选择
(无结果)
插入
(OK)
插入
(错误!重复条目)
准备下一次重试,参考主连接
选择
(1 结果)

高级用法

您可以通过将 RetriesOnDuplicateKey 特性扩展到连接类来自定义。

<?php

namespace App\Providers;

use App\Database\MySqlConnection;
use Illuminate\Database\Connection;
use Illuminate\Support\ServiceProvider;

class DatabaseServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        Connection::resolverFor('mysql', function (...$parameters) {
            return new MySqlConnection(...$parameters);
        });
    }
}
<?php

namespace App\Database;

use Illuminate\Database\Connection as BaseMySqlConnection;
use Mpyw\LaravelRetryOnDuplicateKey\RetriesOnDuplicateKey;

class MySqlConnection extends BaseMySqlConnection
{
    use RetriesOnDuplicateKey;
}

与其他原生upsert实现的区别

这些实现侧重于原子地执行 INSERT-or-UPDATE 查询。因此,在用法上肯定存在明显差异。

  • firstOrCreate() updateOrCreate() 有效地保存 自增编号空间
    • 相比之下,upsert() 总是增加数字,即使没有发生 INSERT,这会导致出现缺失的数字。如果查询频繁执行,这可能会成为一个严重问题。如果您仍然想使用 upsert(),您可能需要考虑使用 UUID 或 ULID 而不是自增。
  • 如果 firstOrCreate() 的调用主要是通过只有一个 SELECT 而很少通过一个随后的 INSERT 完成,那么它具有明显的优势。
    • 相比之下,你必须在所有情况下都使用 upsert() 执行两个查询。
  • 至于 updateOrCreate(),这取决于 RDBMS,可能会有额外的考虑。
    • 对于非 MySQL 的 RDBMS,除非其调用确实更改了行的字段值,否则 updateOrCreate() 会更好。upsert() 可能会破坏当连接既有 Reader(副本)又有 Writer(主)时的 sticky 优化,因为它们假设所有由 WHERE 条件缩窄的行都已受到影响。
    • 在 MySQL 中,upsert() 对此没有限制。它认为只有实际更改字段值的行才会受到影响。
  • 请注意,upsert() 永远不会触发 Eloquent 事件,如 createdupdated,因为它的实现是在 Eloquent Builder 上,而不是在 Model 上。
  • 只有 upsert() 支持批量插入。如果有大量记录且不需要任何 Eloquent 事件,这会很有好处。