返回介绍

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

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

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

一对一关系

  • 定义:一对一是一种 A 只包含一个 B ,而 B 只包含一个 A 的关系
  • 其实就是要设计两个表:一张是主表,一张是副表,查找主表时,关联查找副表
  • 有外键的表称之为副表,不带外键的表称之为主表
  • 如:一个账户对应一个用户信息,主表是账户,副表是用户信息
  • 如:一个用户对应一张用户头像图片,主表是用户信息,副表是头像地址

一对一实体设计

主表:

  • 使用 @OneToOne() 来建立关系
  • 第一个参数: () => AvatarEntity , 和谁建立关系? 和 AvatarEntity 建立关系
  • 第二个参数: (avatar) => avatar.user) ,和哪个字段联立关系? avatar 就是 AvatarEntity 的别名,可随便写,和 AvatarEntityuserinfo 字段建立关系
  • 第三个参数: 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;

  @OneToOne(() => AvatarEntity, (avatar) => avatar.userinfo)
  avatar: AvatarEntity;
}

副表

参数:同主表一样 主要:根据 @JoinColumn({ name: ‘user_id’ }) 来分辨副表, name 是设置数据库的外键名字,如果不设置是 userId

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

@Entity({ name: 'avatar' })
export class AvatarEntity {
  @PrimaryGeneratedColumn()
  id: number;

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

  @OneToOne(() => UsersEntity, (user) => user.avatar)
  @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

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

import { UsersEntity } from './entities/user.entity';
import { AvatarEntity } from './entities/avatar.entity';
import { ToolsService } from '../../utils/tools.service';

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(UsersEntity)
    private readonly usersRepository: Repository<UsersEntity>,
    @InjectRepository(AvatarEntity)
    private readonly avatarRepository: Repository<AvatarEntity>,
    private connection: Connection,
  ) { }

  一对一增删改查
  查找全部
  async findAll() {
    使用封装好的方式
    // return await this.usersRepository.findAndCount({ relations: ['avatar'] });

使用 QueryBuilder 的方式
    const list = await this.usersRepository
      .createQueryBuilder('UsersEntity')
      .leftJoinAndSelect('UsersEntity.avatar', 'AvatarEntity.userinfo')
      .getManyAndCount();
    return list;
  }

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

  根据主表 id 创建一对一
  async create(id: number, manager: EntityManager) {
    const urlObj = {
      url: `http://www.dmyxs.com/images/${id}.png`,
    };
    const user = await this.usersRepository.findOne({ id });      先查找用户
    if (!user) ToolsService.fail('用户 id 不存在');                  如果没找到,抛出错误,由过滤器捕获错误
    
    创建实体的两种方式:new 和 create,new 的方式方便条件判断
    创建实体方式一:
    const avatarEntity = this.avatarRepository.create({ url: urlObj.url });  创建实体

创建实体方式二:
//const avatarEntity = new AvatarEntity();
//avatarEntity.url = urlObj.url;

    const avatarUrl = await manager.save(avatarEntity);          使用事务保存副表
    user.avatar = avatarUrl;                                     主表和副表建立关系
    await manager.save(user);                                    使用事务保存主表
    return '新增成功';                                            如果过程出错,不会保存
  }

  根据主表 id 更改一对一
  要更改的副表 id,会从前端传递过来
  async update(id: number, manager: EntityManager) {
    const urlObj = {
      id: 18,
      url: `http://www.dmyxs.com/images/${id}-update.jpg`,
    };
    
    const user = await this.usersRepository.findOne( { id } );       先查找用户
    if (!user) ToolsService.fail('用户 id 不存在');                      如果没找到 id 抛出错误,由过滤器捕获错误
    
    const avatarEntity = this.avatarRepository.create({ url: urlObj.url });    创建要修改的实体

使用事务更新方法:1 参:要修改的表,2 参:要修改的 id, 3 参:要更新的数据
    await manager.update(AvatarEntity, urlObj.id, avatarEntity);   
    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(AvatarEntity, { user: id });
    
    如果连主表用户一起删,加下面这行代码
    //await manager.delete(UsersEntity, id);
    return '删除成功';
  }
}

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

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

发布评论

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