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

Doctrine持久化时未设置外键值触发非空约束异常

Doctrine持久化时user_id为空导致非空约束违反问题

错误信息:

An exception occurred while executing 'INSERT INTO user_access_company (end_at, created_at, updated_at, user_id, company_id) VALUES (?, ?, ?, ?, ?)' with params [null, "2022-12-12 23:06:57+0000", "2022-12-12 23:06:57+0000", null, 82]:  SQLSTATE[23502]: Not null violation: 7 ERROR:  null value in column "user_id" violates not-null constraint

实体类代码

class Participant implements ParticipantInterface
{
    protected $id;

    protected $source;
    protected $identifier;
    protected $firstName;
    protected $lastName;
    protected $emailAddress;
    protected $createdAt;
    protected $updatedAt;
}

class UserAccessCompany
{
    private Participant $user;
    private Company $company;
    private ?\DateTimeImmutable $endAt;
    private \DateTimeImmutable $createdAt;
    private \DateTimeImmutable $updatedAt;
}

XML映射文件

Participant映射

<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
                  http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd"
                  xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping"
                  >

    <entity name="Participant" table="message_participant">
        <id name="id" type="bigint">
            <generator strategy="AUTO" />
        </id>

        <field name="source" type="string" />
        <field name="identifier" type="integer" />
        <field name="firstName" type="string" />
        <field name="lastName" type="string" nullable="true"/>
        <field name="emailAddress" type="string" nullable="true">

        <field name="createdAt" type="datetime"><gedmo:timestampable on="create"/></field>
        <field name="updatedAt" type="datetime"><gedmo:timestampable on="update"/></field>
    </entity>
</doctrine-mapping>

UserAccessCompany映射

<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping"
                  xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
  <entity name="UserAccessCompany" table="user_access_company">
    <id name="user" association-key="true"/>
    <id name="company" association-key="true"/>
    <field name="endAt" type="datetimetz_immutable" column="end_at" nullable="true"/>
    <field name="createdAt" type="datetimetz_immutable" column="created_at"><gedmo:timestampable on="create"/></field>
    <field name="updatedAt" type="datetimetz_immutable" column="updated_at"><gedmo:timestampable on="create"/></field>
    <many-to-one field="user" target-entity="Participant">
      <join-columns>
        <join-column name="user_id" referenced-column-name="identifier"/>
      </join-columns>
    </many-to-one>
    <many-to-one field="company" target-entity="Company">
      <join-columns>
        <join-column name="company_id"/>
      </join-columns>
    </many-to-one>
  </entity>
</doctrine-mapping>

插入数据代码

$userAccessCompany = new UserAccessCompany($user, $company, $endAt);
$this->getEntityManager()->persist($userAccessCompany);
$this->getEntityManager()->flush();

补充说明

  • $user和$company均为已持久化到数据库的实体,打印$user->identifier能看到有效值,但生成的SQL语句中user_id为null。
  • Company实体的配置方式类似(未指定inversedBy/mappedBy),生成迁移时Doctrine已创建userAccessCompany.user_id关联participant.identifier的外键。
  • 所有实体均已定义getter和setter,尝试过注解映射、给UserAccessCompany添加独立id并移除联合主键,但问题依旧。
  • 当前使用Symfony 4.4和doctrine/orm 2.7.3版本。

问题原因及解决方案

核心原因

Doctrine默认依赖实体的主键字段处理关联关系,但你在UserAccessCompany的user关联中指定了referenced-column-name="identifier",而Participant的主键是id字段,并非identifier。Doctrine无法正确识别非主键字段作为关联字段的持久化规则,导致插入时无法获取identifier的值填充到user_id。

解决步骤

  1. 调整Participant的主键配置(如果identifier是业务主键)
    如果identifier是Participant的业务唯一主键,直接将其设为实体主键:

    <entity name="Participant" table="message_participant">
        <!-- 替换原id配置,将identifier设为主键 -->
        <id name="identifier" type="integer">
            <generator strategy="NONE" /> <!-- 根据实际生成策略调整,自增用AUTO -->
        </id>
        <!-- 原id字段改为普通字段(若需保留) -->
        <field name="id" type="bigint" nullable="true"/>
        
        <!-- 其他字段保持不变 -->
        <field name="source" type="string" />
        <field name="firstName" type="string" />
        <field name="lastName" type="string" nullable="true"/>
        <field name="emailAddress" type="string" nullable="true"/>
        <field name="createdAt" type="datetime"><gedmo:timestampable on="create"/></field>
        <field name="updatedAt" type="datetime"><gedmo:timestampable on="update"/></field>
    </entity>
    
  2. 给identifier添加唯一约束(若无需改主键)
    若identifier只是业务唯一键而非主键,给它添加唯一约束,确保Doctrine能正确追踪该字段:

    <!-- 在Participant映射中修改identifier字段 -->
    <field name="identifier" type="integer" unique="true" />
    
  3. 检查构造函数赋值逻辑
    确保UserAccessCompany的构造函数正确初始化$user属性:

    class UserAccessCompany
    {
        public function __construct(Participant $user, Company $company, ?\DateTimeImmutable $endAt = null)
        {
            $this->user = $user;
            $this->company = $company;
            $this->endAt = $endAt;
            // 手动初始化时间字段(若gedmo未自动处理)
            $this->createdAt = new \DateTimeImmutable();
            $this->updatedAt = new \DateTimeImmutable();
        }
    }
    
  4. 强制刷新实体状态
    在持久化前刷新$user实体,确保Doctrine持有最新的字段值:

    $this->getEntityManager()->refresh($user);
    $userAccessCompany = new UserAccessCompany($user, $company, $endAt);
    $this->getEntityManager()->persist($userAccessCompany);
    $this->getEntityManager()->flush();
    

内容的提问来源于stack exchange,提问作者ᴄʀᴏᴢᴇᴛ

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 20:10:48