You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Doctrine实体保存时如何注册自定义更新查询替代默认逻辑

解决方案

别上来就覆写核心EntityPersister,维护成本极高,两种适配方案按维护成本从低到高选:

方案1:微秒时间类型+事件监听(推荐)

完全兼容Doctrine内置乐观锁逻辑,不需要改动核心持久化流程:

  • 先自定义支持微秒精度的DBAL时间类型,代码如下:
<?php
namespace App\DBAL\Types;

use Doctrine\DBAL\Types\DateTimeType;
use Doctrine\DBAL\Platforms\AbstractPlatform;

class DateTimeMicrosecondType extends DateTimeType
{
    public function convertToDatabaseValue($value, AbstractPlatform $platform)
    {
        return $value === null ? null : $value->format('Y-m-d H:i:s.u');
    }

    public function convertToPHPValue($value, AbstractPlatform $platform)
    {
        if ($value === null || $value instanceof \DateTimeInterface) {
            return $value;
        }
        return \DateTime::createFromFormat('Y-m-d H:i:s.u', $value);
    }

    public function getName(): string
    {
        return 'datetime_micro';
    }
}
  • 在Doctrine配置中注册该自定义类型,将对应实体的modified_at字段类型改为datetime_micro,同时标记为乐观锁版本字段:
// 实体映射配置示例
#[ORM\Column(type: 'datetime_micro')]
#[ORM\Version]
private ?\DateTimeInterface $modifiedAt = null;
  • 注册preUpdate事件监听,更新时自动写入带微秒的当前时间:
<?php
namespace App\EventListener;

use App\Entity\Thing;
use Doctrine\ORM\Events;
use Doctrine\Persistence\Event\LifecycleEventArgs;

class ThingUpdateListener
{
    public function preUpdate(LifecycleEventArgs $args): void
    {
        $entity = $args->getObject();
        if (!$entity instanceof Thing) {
            return;
        }
        $entity->setModifiedAt(new \DateTimeImmutable('now'));
    }

    public static function getSubscribedEvents(): array
    {
        return [Events::preUpdate => 'preUpdate'];
    }
}

最后把数据库中modified_at字段的类型修改为DATETIME(6),避免数据库层面截断微秒,该方案下Doctrine原生的乐观锁校验会自动生效,不会出现同秒并发漏判的问题。

方案2:自定义Persister完全替换更新逻辑

如果必须使用你写的原生SQL、完全替换默认persister->update()流程,按以下步骤实现:

  • 编写自定义Persister类,继承Doctrine默认的BasicEntityPersister,覆写update()方法,在方法内执行你的自定义更新SQL,自行处理参数绑定、影响行数校验、更新后实体字段同步逻辑——注意更新影响行数为0时必须抛出OptimisticLockException,否则会丢失并发保护能力。
  • 在实体映射配置中直接指定自定义Persister类,Doctrine会自动替换默认的持久化实现:
#[ORM\Entity(repositoryClass: ThingRepository::class)]
#[ORM\PersisterClass(CustomThingPersister::class)]
class Thing
{
    // 实体字段定义
}

注意:该方案需要自行处理所有字段更新、版本校验、实体状态同步逻辑,版本升级后需要跟进Doctrine内部接口变化,维护成本较高,非必要不选。

内容的提问来源于stack exchange,提问作者Joaquim d'Souza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 20:03:21