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

Doctrine 3实体CURRENT_TIMESTAMP默认值不生效,如何正确设置?

问题分析与解决方案

错误原因

Doctrine ORM在持久化实体时,会把所有映射的字段都包含到INSERT语句中。虽然你给数据库列设置了DEFAULT CURRENT_TIMESTAMP,但实体的sent_at是未赋值的readonly属性,Doctrine会在INSERT时传递NULL值给该字段。数据库会优先使用显式传递的NULL,而非列默认值,这就触发了NOT NULL约束错误。而直接执行SQL时你没指定sent_at字段,数据库才会自动应用默认值。

无需手动设置sent_at的正确配置方法

方法1:用生命周期回调在应用层自动赋值

通过Doctrine的PrePersist生命周期事件,在实体持久化前自动设置sent_at的值。因为是readonly属性,需要用反射绕过初始化限制:

use Doctrine\ORM\Mapping\{Entity, Table, Column, HasLifecycleCallbacks, PrePersist};
use Doctrine\DBAL\Types\Types;
use ReflectionProperty;

#[Entity]
#[Table(name: 'messages')]
#[HasLifecycleCallbacks]
class Message
{
    #[Column(
        name: 'sent_at',
        type: Types::DATETIME_IMMUTABLE,
        updatable: false,
        options: ['default' => 'CURRENT_TIMESTAMP']
    )]
    private readonly DateTimeImmutable $sent_at;

    public function __construct(private readonly string $message)
    {
    }

    #[PrePersist]
    public function setSentAtOnPersist(): void
    {
        $reflection = new ReflectionProperty(self::class, 'sent_at');
        $reflection->setValue($this, new DateTimeImmutable());
    }

    // 可选:添加getter方法
    public function getSentAt(): DateTimeImmutable
    {
        return $this->sent_at;
    }
}

优点:持久化后立即能获取sent_at的值,无需刷新实体;缺点:时间戳由应用服务器生成,可能和数据库服务器时间存在偏差。

方法2:让Doctrine忽略该字段的插入,依赖数据库默认值

配置Doctrine不在INSERT语句中包含sent_at字段,让数据库自动应用默认值。需要添加insertable: false和generated: Column::GENERATED_DEFAULT属性:

use Doctrine\ORM\Mapping\{Entity, Table, Column};
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\EntityManagerInterface;

#[Entity]
#[Table(name: 'messages')]
class Message
{
    #[Column(
        name: 'sent_at',
        type: Types::DATETIME_IMMUTABLE,
        updatable: false,
        insertable: false,
        generated: Column::GENERATED_DEFAULT,
        options: ['default' => 'CURRENT_TIMESTAMP']
    )]
    private readonly DateTimeImmutable $sent_at;

    public function __construct(private readonly string $message)
    {
    }

    // 持久化后需要刷新实体才能获取数据库生成的sent_at
    public function refreshSentAt(EntityManagerInterface $em): void
    {
        $em->refresh($this);
    }

    public function getSentAt(): DateTimeImmutable
    {
        return $this->sent_at;
    }
}

使用时,持久化后如果需要获取sent_at值,要调用refreshSentAt方法:

$message = new Message('test message');
$this->entity_manager->persist($message);
$this->entity_manager->flush();
$message->refreshSentAt($this->entity_manager);
// 现在可以获取到数据库生成的sent_at值
echo $message->getSentAt()->format('Y-m-d H:i:s');

优点:时间戳由数据库服务器生成,时间更准确;缺点:需要手动刷新实体才能拿到sent_at的值。

内容的提问来源于stack exchange,提问作者Unlucky

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 21:57:27