typeorm实体多对多关系指定表名与关联字段

表结构

user 用户表结构

在这里插入图片描述

course 课程表结构

在这里插入图片描述

user_course 用户课程表 (每个用户可以有多个课程, 每个课程可以有多个用户, 该表用以建立多对多关系)

在这里插入图片描述

实体

user.entity.ts

@Entity('user', { schema: 'test' })
export class User {
  @PrimaryGeneratedColumn({ type: 'int', name: 'id' })
  id: number;

  @Column('varchar', { name: 'name', nullable: true, length: 255 })
  name: string | null;

  @ManyToMany(() => Course, (course) => course.users)
  @JoinTable()
  courses: Course[];
}

course.entity.ts

@Entity('course', { schema: 'test' })
export class Course {
  @PrimaryGeneratedColumn({ type: 'int', name: 'id' })
  id: number;

  @Column('varchar', {
    name: 'name',
    nullable: true,
    comment: '课程名称',
    length: 255,
  })
  name: string | null;

  @ManyToMany(() => User, (user) => user.courses)
  users: User[];
}

查询

    // QueryBuilder查询
    const result2 = await this.userRepository
      .createQueryBuilder('user')
      .leftJoinAndSelect('user.courses', 'course')
      .getMany();

sql打印

SELECT
	`User`.`id` AS `User_id`,
	`User`.`name` AS `User_name`,
	`User__User_courses`.`id` AS `User__User_courses_id`,
	`User__User_courses`.`name` AS `User__User_courses_name` 
FROM
	`user` `User`
	LEFT JOIN `user_courses_course` `User_User__User_courses` ON `User_User__User_courses`.`userId` = `User`.`id`
	LEFT JOIN `course` `User__User_courses` ON `User__User_courses`.`id` = `User_User__User_courses`.`courseId`

很明显, 我们中间表的名称错误 (非 user_courses_course)

关联字段名称错误 (非userId 和 courseId)

上一篇文章写了 @JoinColumn()装饰器可以指定关联字段的名称, 那么 @JoinTable() 自然也是可以的

设置中间表名称/关联字段名称

@JoinTable() 装饰器设置

  @JoinTable({
    name: 'user_course',
    joinColumn: { name: 'user_id' },
    inverseJoinColumn: { name: 'course_id' },
  })

sql预览

SELECT
	`user`.`id` AS `user_id`,
	`user`.`name` AS `user_name`,
	`course`.`id` AS `course_id`,
	`course`.`name` AS `course_name` 
FROM
	`user` `user`
	LEFT JOIN `user_course` `user_course` ON `user_course`.`user_id` = `user`.`id`
	LEFT JOIN `course` `course` ON `course`.`id` = `user_course`.`course_id`
<think>好的,我现在需要帮用户解决这个SQL错误:“ERROR: column \"leader_id_card\" of relation \"law_clue\" does not exist (SQLSTATE 42703)”。首先,我得理解这个错误的含义。 用户遇到的错误提示指出,在“law_clue”中不存在为“leader_id_card”的列。SQLSTATE 42703通常示无效的列,也就是说,在执行的SQL语句中引用了一个不存在的列。这时候,我需要考虑可能的原因和解决方案。 首先,用户可能是在执行INSERT或UPDATE语句时,引用了这个不存在的列。或者是在SELECT语句中使用了这个列,但结构里没有定义。另外,也有可能是结构被修改过,但迁移脚本没有正确应用,或者缓存导致的问题。 接下来,我应该分步骤引导用户排查问题。第一步,检查结构,确认“leader_id_card”是否存在。如果用户有权限访问数据库,可以直接查询的列信息。例如,在PostgreSQL中使用\d law_clue命令,或者执行查询information_schema.columns来查看列是否存在。 如果确认列不存在,用户需要添加这个列。这时候需要考虑如何修改结构,使用ALTER TABLE语句添加列,并确保数据类型和约束正确。此外,还要检查是否有拼写错误,可能用户误将列写错,比如“leader_id_card”是否应该是“leader_id”或者其他类似的称。 然后,用户需要检查应用程序中的ORM模型或SQL语句,确认是否错误地引用了这个列。比如在模型定义中是否有拼写错误,或者在查询语句中是否错误地使用了别前缀。 另外,如果使用了数据库迁移工具,比如Flyway或Liquibase,需要检查迁移脚本是否正确,是否已经应用到当前数据库环境中。有时候,迁移脚本可能没有正确执行,导致结构不一致。 还要考虑数据库连接的问题,是否应用程序连接到了错误的数据库环境,比如测试环境而不是开发环境,导致结构不同。 最后,建议用户在修改结构后,重新启动应用程序,并清除可能的缓存,比如ORM的缓存,确保更改生效。 总结起来,解决步骤应该是:验证列是否存在,修正SQL语句或结构,检查迁移脚本和数据库连接,确保所有环境一致。同时,提醒用户注意拼写错误和数据库权限问题。</think>该错误属于 PostgreSQL 数据库的列不存在异常,以下是系统性解决方案: **错误诊断公式** `无效列引用 = 列拼写错误 + 结构不符 + 迁移未执行 + 缓存未更新` **分步排查方案** 1. **列存在性验证(关键第一步)** 执行元数据查询: ```sql -- PostgreSQL 验证方式 SELECT column_name FROM information_schema.columns WHERE table_name = 'law_clue' AND column_name = 'leader_id_card'; ``` - 返回空结果 ⇒ 列确实不存在 - 返回结果 ⇒ 检查应用层代码 2. **成因分析矩阵** | 可能原因 | 验证方法 | 解决方案 | |-------------------------|-----------------------------------|--------------------------------------------------------------------------| | 拼写错误 | 对比 ER 图 SQL 语句 | 修正为正确列(如 `leader_id` → `leader_id_card` 反向修正) | | 未执行迁移脚本 | 检查 flyway/liquibase 迁移历史 | 执行 pending 的迁移文件 | | 环境差异(DEV/TEST/PROD)| 对比不同环境结构 | 同步数据库结构或修正环境配置 | | 事务未提交 | 查看 `pg_stat_activity` 视图 | 提交 DDL 事务或断开残留连接 | | 权限问题 | 执行 `\dp law_clue` 查看权限 | 给当前用户授予 ALTER 权限:<br>`GRANT ALL ON law_clue TO current_user;` | 3. **紧急修复操作** ```sql -- 临时补救方案(正式环境需走变更流程) ALTER TABLE law_clue ADD COLUMN leader_id_card VARCHAR(18) CHECK (LENGTH(leader_id_card) = 18); COMMENT ON COLUMN law_clue.leader_id_card IS '负责人身份证号码'; ``` **防御性编程建议** 1. 在 ORM 层(如 TypeORM)增加实体字段校验 ```typescript @Entity() export class LawClue { @Column({ name: 'leader_id_card', type: 'varchar', length: 18, comment: '负责人身份证号', nullable: true // 根据业务调整 }) leaderIdCard?: string; } ``` 2. 使用 Flyway 版本控制(示例迁移文件) ```sql -- V2023.07.18.0942__add_leader_id_card_to_law_clue.sql BEGIN; ALTER TABLE law_clue ADD COLUMN leader_id_card VARCHAR(18); CREATE INDEX idx_leader_id_card ON law_clue(leader_id_card); COMMENT ON COLUMN law_clue.leader_id_card IS '负责人身份证号码'; COMMIT; ``` 3. 增加预检机制(Node.js 示例) ```javascript async function validateSchema(pool) { const res = await pool.query(` SELECT column_name FROM information_schema.columns WHERE table_name = 'law_clue' AND column_name = 'leader_id_card' `); if (res.rowCount === 0) { throw new Error('关键字段 leader_id_card 缺失,请执行数据库迁移'); } } ``` **典型误操作场景** - 在未开启事务的情况下直接修改生产环境结构 - 使用 `DROP COLUMN` 后未同步更新关联视图/存储过程 - 忽略字段长度约束导致公民身份证号截断(必须保持 18 位) 建议结合 `EXPLAIN ANALYZE` 分析执行计划,确认该字段是否被索引合理使用。若涉及敏感信息,应考虑增加数据脱敏策略。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值