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

如何保存使用自定义类ID的多对多关系

自定义ID类在多对多关联保存时无法正确序列化问题

问题背景

使用自定义UserId和OrganizationId类生成带前缀的字符串ID(如USER-1234、Org-1234),为User和Organization实体建立多对多关联。手动插入数据后可正常查询关联关系,但执行保存操作时,连接表插入语句使用DEFAULT值,触发"Field 'organizationId' doesn't have a default value"错误。

相关代码

实体类定义

@Entity('user')
export class User {
  @PrimaryColumn({
    type: 'varchar',
    transformer: idTransformer,
  })
  @Property()
  id: UserId;

  // 其他字段...
  @ManyToMany(() => Organization, (org) => org.users)
  @Default([])
  @JoinTable({ name: 'user_organization' })
  organizations: Array<Organization>;
}

@Entity('organization')
export class Organization {
  @PrimaryColumn({
    type: 'varchar',
    transformer: idTransformer,
  })
  @Property()
  id: OrganizationId;
  
  // 其他字段...
  @ManyToMany(() => User, (user) => user.organizations)
  @JoinTable({ name: 'user_organization' })
  @Default([])
  users: Array<User>;
}

ID转换器实现

export const idTransformer = {
  to: (value: Id) => {
    return value.getValue();
  },
  from: <T extends Id>(value: string): T => {
    if (value) {
      return IdUtils.fromHex(value);
    }

    return null;
  },
};

触发错误的保存操作

const org = await orgRepository.getOrg();
const user = await getUser();
org.users.add(user);

await orgRepository.save(org);

错误信息

{
  "name": "QueryFailedError",
  "message": "Field 'organizationId' doesn't have a default value",
  "status": 500,
  "errors": [],
  "stack": "QueryFailedError: Field 'organizationId' doesn't have a default value\n..."
}

MySQL执行日志

INSERT INTO `user_organization`(`organizationId`, `userId`) VALUES (DEFAULT, DEFAULT)

解决方案

1. 为自定义ID类添加toString方法

ORM在处理关联关系时,可能直接调用对象的toString()方法而非字段转换器。给Id基类添加该方法,确保能输出正确的ID字符串:

class Id {
  private value: string;

  constructor(value: string) {
    this.value = value;
  }

  getValue(): string {
    return this.value;
  }

  toString(): string {
    return this.getValue();
  }
}

2. 显式配置连接表字段的转换器

部分ORM(如TypeORM)不会自动将实体主键的转换器应用到连接表,需在@JoinTable中手动指定:

// 修改User类中的多对多关联配置
@ManyToMany(() => Organization, (org) => org.users)
@JoinTable({
  name: 'user_organization',
  joinColumns: [
    {
      name: 'userId',
      referencedColumnName: 'id',
      transformer: idTransformer,
    },
  ],
  inverseJoinColumns: [
    {
      name: 'organizationId',
      referencedColumnName: 'id',
      transformer: idTransformer,
    },
  ],
})
organizations: Array<Organization>;

同时移除Organization类中的@JoinTable注解,多对多关联只需一方配置即可。

3. 检查ORM版本与配置

确认使用的ORM版本是否存在自定义ID转换器的兼容问题,升级到最新稳定版。同时验证实体元数据是否正确加载,转换器是否被ORM识别。

4. 手动插入连接表数据

若以上方法无效,可绕过ORM自动关联逻辑,直接执行SQL插入:

await connection.query(
  'INSERT INTO user_organization (userId, organizationId) VALUES (?, ?)',
  [user.id.getValue(), org.id.getValue()]
);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 16:05:38