返回介绍

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

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

实体如何设计一对多与多对一关系,如何关联查询

一对多关系,多对一关系

定义:一对多是一种一个 A 包含多个 B ,而多个 B 只属于一个 A 的关系 其实就是要设计两个表:一张是主表(一对多),一张是副表(多对一),查找主表时,关联查找副表 有外键的表称之为副表,不带外键的表称之为主表 如:一个用户拥有多个宠物,多个宠物只属于一个用户的(每个宠物只能有一个主人) 如:一个用户拥有多张照片,多张照片只属于一个用户的 如:一个角色拥有多个用户,多个用户只属于一个角色的(每个用户只能有一个角色)

一对多和多对一实体设计

一对多

使用 @OneToMany() 来建立一对多关系 第一个参数: () => PhotoEntity , 和谁建立关系? 和 PhotoEntity 建立关系 第二个参数: (user) => user.photo ,和哪个字段联立关系? user 就是 PhotoEntity 的别名,可随便写,和 PhotoEntityuserinfo 字段建立关系 第三个参数: RelationOptions 关系选项

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

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

  @Column()
  username: string;

  @Column()
  password: string;

  @OneToMany(() => PhotoEntity, (avatar) => avatar.userinfo)
  photos: PhotoEntity;
}

多对一

使用 @ManyToOne() 来建立多对一关系,参数如同上

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

@Entity({ name: 'photo' })
export class PhotoEntity {
  @PrimaryGeneratedColumn()
  id: number;

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

  @ManyToOne(() => UsersEntity, (user) => user.photos)
  @JoinColumn({ name: 'userinfo_id' })
  userinfo: UsersEntity;
}

一对多和多对一增删改查

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

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

令人头大的地方:建立关系和查找使用实体,删除使用实体的 id,感觉设计得不是很合理,违背人的常识

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

import { UsersEntity } from './entities/user.entity';
import { PhotoEntity } from './entities/photo.entity';

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

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

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

    const list = await this.usersRepository
      .createQueryBuilder('UsersEntity')
      .leftJoinAndSelect('UsersEntity.photos', 'PhotoEntity.userinfo')
      .getManyAndCount();
    return list;
  }

  根据主表 id 查找一对多
  async findOne(id: number) {
    查询一个用户有多少张照片(一对多)
    const result = await this.usersRepository.findOne(id, {
      relations: ['photos'],
    });
    if (!result) ToolsService.fail('用户 id 不存在');
    return result;

    查询这张照片属于谁(多对一)
    // const result = await this.photoRepository.findOne(id, {
    //   relations: ['userinfo'],
    // });
    // if (!result) ToolsService.fail('图片 id 不存在');
    // return result;
  }

  根据主表 id 创建一对多
  async create(id: number, manager: EntityManager) {
    const urlList = [
      {
        url: `http://www.dmyxs.com/images/${id}.png`,
      },
      {
        url: `http://www.dmyxs.com/images/${id}.jpg`,
      },
    ];
    const user = await this.usersRepository.findOne({ id });
    if (!user) ToolsService.fail('用户 id 不存在');

遍历传递过来的数据
    if (urlList.length !== 0) {
for (let i = 0; i < urlList.length; i++) {
       创建实体的两种方式:new 和 create,new 的方式方便条件判断
       // const photo = new PhotoEntity();
       // photo.url = urlList[i].url;
       // photo.user = user;
       // await manager.save(PhotoEntity, photo);

       const photoEntity = this.photoRepository.create({
         url: urlList[i].url,
         userinfo: user,  注意:这里是使用实体建立关系,而不是实体 id
       });
       await manager.save(photoEntity);
    }
    }
    return '新增成功';
  }

  根据主表 id 更改一对多
  示例:删除一张,修改一张(修改的有 id),新增一张
  先使用创建,创建两张 photo
  async update(id: number, manager: EntityManager) {
    const urlList = [
      {
        id: 22,
        url: `http://www.dmyxs.com/images/${id}-update.png`,
      },
      {
        url: `http://www.dmyxs.com/images/${id}-create.jpeg`,
      },
    ];
    const user = await this.usersRepository.findOne({ id });
    if (!user) ToolsService.fail('用户 id 不存在');
    
    如果要修改主表,先修改主表用户信息,后修改副表图片信息
    修改主表
    const userEntity = this.usersRepository.create({
      id,
      username: 'admin7',
      password: '123456',
    });
    await manager.save(userEntity);

    修改副表
    如果前端附带了图片 list
    if (urlList.length !== 0) {
      查询数据库已经有的图片
      const databasePhotos = await manager.find(PhotoEntity, { userinfo: user });
      
      如果有数据,则进行循环判断,先删除多余的数据
      if (databasePhotos.length >= 1) {
        for (let i = 0; i < databasePhotos.length; i++) {
        
          以用户传递的图片为基准,数据库的图片 id 是否在用户传递过来的表里,如果不在,就是要删除的数据
          const exist = urlList.find((item) => item.id === databasePhotos[i].id);
          
          if (!exist) {
            await manager.delete(PhotoEntity, { id: databasePhotos[i].id });
          }
        }
      }

      否则就是新增和更改的数据
      for (let i = 0; i < urlList.length; i++) {
        const photoEntity = new PhotoEntity();
        photoEntity.url = urlList[i].url;
        
        如果有 id 则是修改操作,因为前端传递的数据是从服务端获取的,会附带 id,新增的没有
        if (!!urlList[i].id) {
        
          修改则让 id 关联即可
          photoEntity.id = urlList[i].id;
          await manager.save(PhotoEntity, photoEntity);
        } else {
        
          否则是新增操作,关联用户实体
          photoEntity.userinfo = user;
          await manager.save(PhotoEntity, photoEntity);
        }
      }
    } else {
      如果前端把图片全部删除,删除所有关联的图片
      await manager.delete(PhotoEntity, { userinfo: id });
    }
    return '更新成功';
  }

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

    只删副表的关联数据
    await manager.delete(PhotoEntity, { userinfo: id });
    如果连主表用户一起删,加下面这行代码
    //await manager.delete(UsersEntity, id);
    return '删除成功';
  }
}

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

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

发布评论

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