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

TypeORM嵌套实体报错:Cannot read properties of undefined reading 'getEntityValue'

问题解决:NestJS + TypeORM 中 User 与 Avatar 关联报错 Cannot read properties of undefined (reading 'getEntityValue')

问题根源

你错误地使用了 @TreeChildren 装饰器来关联 User 和 Avatar 实体。@TreeChildren 是 TypeORM 专门用于树形层级结构(如分类树、部门层级)的装饰器,需要配合 @TreeParent 和 @Tree 一起使用,完全不适合用来实现 User 嵌套 Avatar 的需求。这种错误用法导致创建无 Avatar 的 User 时,TypeORM 尝试处理树形结构逻辑时抛出未定义错误。

解决方案

根据 Avatar 是 User 的附属属性(无独立存在意义)的场景,推荐两种正确的关联方式:

方案1:使用嵌入式实体(Embedded Entity)

适合 Avatar 仅作为 User 的一部分存储的场景,无需单独的数据库表。

  1. 修改 Avatar 实体为嵌入式:
// avatar.entity.ts
import { Embeddable, Column } from 'typeorm';

@Embeddable()
export class Avatar {
    @Column({ type: "text", nullable: true })
    public_key: string;

    @Column({ type: "text", nullable: true })
    url: string;
}
  1. 更新 User 实体的关联方式:
// user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, BeforeInsert, Embedded } from 'typeorm';
import { Avatar } from './avatar.entity';
import * as uniqid from 'uniqid';

@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number;

    @Column({ type: "text", nullable: false })
    userName: string;

    @Column({ type: "text", nullable: false })
    email: string;

    @Column({ type: "text", nullable: false })
    firstName: string;

    @Column({ type: "text", nullable: false })
    lastName: string;

    @Column({ type: "text", nullable: false, select: false })
    password: string;

    @Column({ type: "text", nullable: true })
    socket_id: string;

    @Column({ type: "text", nullable: true, select: false })
    otp: string;

    // 使用嵌入式实体,允许为空
    @Embedded(() => Avatar, { nullable: true })
    avatar: Avatar;

    @Column({ type: "boolean", default: false, nullable: false })
    is_verify: boolean;

    @CreateDateColumn()
    created_at: Date;

    @UpdateDateColumn()
    updated_at: Date;

    @BeforeInsert()
    createUserName() {
        this.userName = uniqid.time();
    }
}

方案2:使用一对一关联(One-to-One)

如果后续需要让 Avatar 作为独立实体存在(如复用、单独查询),可以使用一对一关联:

  1. 修改 Avatar 实体为独立实体:
// avatar.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from 'typeorm';
import { User } from './user.entity';

@Entity()
export class Avatar {
    @PrimaryGeneratedColumn()
    id: number;

    @Column({ type: "text", nullable: true })
    public_key: string;

    @Column({ type: "text", nullable: true })
    url: string;

    @OneToOne(() => User, user => user.avatar)
    @JoinColumn()
    user: User;
}
  1. 更新 User 实体的关联方式:
// user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, BeforeInsert, OneToOne } from 'typeorm';
import { Avatar } from './avatar.entity';
import * as uniqid from 'uniqid';

@Entity()
export class User {
    // 其他字段保持不变

    @OneToOne(() => Avatar, { nullable: true, cascade: true })
    // cascade: true 可实现保存 User 时自动同步保存关联的 Avatar
    avatar: Avatar;

    // 其他字段保持不变
}

验证修改

完成上述修改后,重新执行创建用户的代码:

const result = await this.userRepository.create({
    ...userInput,
    otp: secret.base32,
    password: passwordHash,
});
await this.userRepository.save(result)

此时可以正常创建不带 Avatar 的 User,后续更新资料时再补充 Avatar 字段即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 10:21:12