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 的一部分存储的场景,无需单独的数据库表。
- 修改 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; }
- 更新 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 作为独立实体存在(如复用、单独查询),可以使用一对一关联:
- 修改 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; }
- 更新 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
相关产品推荐
相关产品推荐

