返回介绍

typeorm 多对多关系设计与增删改查

发布于 2024-01-18 22:07:39 字数 7323 浏览 0 评论 0 收藏 0

实体如何设计多对多关系?如何增删改查?

多对多关系

定义:多对多是一种 A 包含多个 B,而 B 包含多个 A 的关系 如:一个粉丝可以关注多个主播,一个主播可以有多个粉丝 如:一篇文章属于多个分类,一个分类下有多篇文章 比如这篇文章,可以放在 nest 目录,也可以放在 typeorm 目录或者 mysql 目录

实现方式

第一种:建立两张表,使用装饰器 @ManyToMany 建立关系, typeorm 会自动生成三张表 第二种:手动建立 3 张表

这里使用第一种

实体设计

这里将设计一个用户(粉丝) 与 明星的 多对多关系

用户(粉丝)可以主动关注明星,让 users 变为主表,加入 @JoinTable()

使用 @ManyToMany() 来建立多对多关系 第一个参数: () => StarEntity , 和谁建立关系? 和 StarEntity 建立关系 第二个参数: (star) => star.photo ,和哪个字段联立关系? star 就是 StarEntity 的别名,可随便写,和 PhotoEntityfollowers 字段建立关系

用户(粉丝)表:follows 关注/跟随

import {
  Column,
  Entity,
  PrimaryGeneratedColumn,
  ManyToMany,
  JoinTable,
} from 'typeorm';
import { AvatarEntity } from './avatar.entity';

@Entity({ name: 'users' })
export class UsersEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  username: string;

  @Column()
  password: string;

  @ManyToMany(() => StarEntity, (star) => star.followers)
  @JoinTable()
  follows: StarEntity[]; 注意这里是数组类型
}

明星表:followers 跟随者

import { Entity, PrimaryGeneratedColumn, Column, ManyToMany } from 'typeorm';
import { UsersEntity } from './user.entity';

@Entity({ name: 'star' })
export class StarEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ type: 'varchar' })
  name: string;

  @ManyToMany(() => UsersEntity, (user) => user.follows)
  followers: UsersEntity;
}

注意:

程序运行后,将会默认在数据库中生成三张表,users,star,users_follows_star,users_follows_star 是中间表,用于记录 users 和 star 之间的多对多关系,它是自动生成的。

为了测试方便,你可以在 users 表和 star 表创建一些数据:这些属于单表操作

多对多增删改查

只要涉及两种表操作的,就需要开启事务:同时失败或同时成功,避免数据不统一 注意:所有数据应该是由前端传递过来的,这里为了方便,直接硬编码了(写死)

user.controller.ts

import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Query,
  Param,
  Delete,
  HttpCode,
  HttpStatus,
  ParseIntPipe,
} from '@nestjs/common';
import { Transaction, TransactionManager, EntityManager } from 'typeorm';  开启事务第一步:引入
import { UserService } from './user.service-oto';

@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) { }

  @Get()
  async findAll() {
    const [data, count] = await this.userService.findAll();
    return { count, data };
  }

  @Get(':id')
  async findOne(@Param('id', new ParseIntPipe()) id: number) {
    return await this.userService.findOne(id);
  }

  @Post(':id')
  @HttpCode(HttpStatus.OK)
  @Transaction() 开启事务第二步:装饰接口
  async create(
    @Param('id', new ParseIntPipe()) id: number,
    @TransactionManager() maneger: EntityManager,  开启事务第三步:获取事务管理器
  ) {
    return await this.userService.create(id, maneger);  开启事务第四步:传递给 service,使用数据库时调用
  }

  @Patch(':id')
  @Transaction()
  async update(
    @Param('id', new ParseIntPipe()) id: number,
    @TransactionManager() maneger: EntityManager,
  ) {
    return await this.userService.update(id, maneger);
  }

  @Delete(':id')
  @Transaction()
  async remove(
    @Param('id', new ParseIntPipe()) id: number,
    @TransactionManager() maneger: EntityManager,
  ) {
    return await this.userService.remove(id, maneger);
  }
}

user.service.ts

import { Injectable } from '@nestjs/common';
import { Repository, EntityManager } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';

import { UsersEntity } from './entities/user.entity';
import { StarEntity } from './entities/star.entity';

import { ToolsService } from '../../utils/tools.service';

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(UsersEntity)
    private readonly usersRepository: Repository<UsersEntity>,
    @InjectRepository(StarEntity)
    private readonly starRepository: Repository<StarEntity>,
  ) { }

  一对多增删改查
  async findAll() {
    // return await this.usersRepository.findAndCount({ relations: ['follows'] });

    const list = await this.usersRepository
      .createQueryBuilder('UsersEntity')
      .leftJoinAndSelect('UsersEntity.follows', 'StarEntity.followers')
      .getManyAndCount();
    return list;
  }

  根据主表 id 查找多对多
  async findOne(id: number) {
    查询一个用户关注了哪些明星
    // const result = await this.usersRepository.findOne(id, {
    //   relations: ['follows'],
    // });
    // if (!result) ToolsService.fail('用户 id 不存在');
    // return result;

    查询一个明星有多少粉丝
    const result = await this.starRepository.findOne(id, {
      relations: ['followers'],
    });
    if (!result) ToolsService.fail('明星 id 不存在');
    return result;
  }

  根据主表 id 创建多对多
  粉丝关注明星
  async create(id: number, manager: EntityManager) {
  要关注的明星 id 数组
    const willFollow = [3, 4];
    const user = await this.usersRepository.findOne({ id });
    if (!user) ToolsService.fail('用户 id 不存在');

    if (willFollow.length !== 0) {
      const followList = [];
      for (let i = 0; i < willFollow.length; i++) {
        const star = await manager.findOne(StarEntity, {
          id: willFollow[i],
        });
        if (!star) ToolsService.fail('主播 id 不存在');
        followList.push(star);
      }

      const userEntity = new UsersEntity();
      重点:
      不指定 id 是创建新的用户,还需要填写 username 和 password 等必填的字段
      指定 id 就是更新某些字段:只关注明星,不创建新的用户,同样可用于修改
      userEntity.id = id; 
      userEntity.follows = followList; 建立关联,数据表会自动更新
      await manager.save(userEntity);
    }
    return '新增成功';
  }

  根据主表 id 更改多对多
  假设:某用户关注了 id 为[3, 4]的明星, 现在修改为只关注[2]
  逻辑和创建一样
  async update(id: number, manager: EntityManager) {
    const willFollow = [2];
    const user = await this.usersRepository.findOne({ id });
    if (!user) ToolsService.fail('用户 id 不存在');

    if (willFollow.length !== 0) {
      const followList = [];
      for (let i = 0; i < willFollow.length; i++) {
        const listOne = await manager.findOne(StarEntity, {
          id: willFollow[i],
        });
        if (!listOne) ToolsService.fail('主播 id 不存在');
        followList.push(listOne);
      }

      const userEntity = new UsersEntity();
      userEntity.id = id;
      userEntity.follows = followList;
      await manager.save(userEntity);
    }
    return '更新成功';
  }

  根据主表 id 删除多对多
  多种删除
  async remove(id: number, manager: EntityManager): Promise<any> {
    const user = await this.usersRepository.findOne(id, {
      relations: ['follows'],
    });
    if (!user) ToolsService.fail('用户 id 不存在');

    根据 id 删除一个:取消关注某个明星,明星 id 应由前端传递过来,这里写死
    需要获取当前用户的的 follows,使用关系查询
    const willDeleteId = 2;
    if (user.follows.length !== 0) {
      过滤掉要删除的数据,再重新赋值
      const followList = user.follows.filter((star) => star.id != willDeleteId);
      const userEntity = new UsersEntity();
      userEntity.id = id;
      userEntity.follows = followList;
      await manager.save(userEntity);
    }

    全部删除关联数据,不删用户
    // const userEntity = new UsersEntity();
    // userEntity.id = id;
    // userEntity.follows = [];
    // await manager.save(userEntity);

    如果连用户一起删,会将关联数据一起删除
    // await manager.delete(UsersEntity, id);
    return '删除成功';
  }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文