nestjs[typeorm学习之联表一对一关系探究与使用]

nestjs入门学习规划:https://blog.csdn.net/lxy869718069/article/details/114028195

一对一表关系

一对一是一种 A 只包含一个 B 实例,而 B 只包含一个 A 实例的关系
例如:
有两张表,一个为用户账号表,另一个为用户信息表,信息表中带有一个外键来保存与账号表的关联信息。而一个账号只能有一个用户信息与之对应。
在这里插入图片描述
在这里插入图片描述
这就是一个典型的一对一关系,其中带有外键的表称之为副表,不带外键的表称之为主表。

项目实例与解析

目录结构:
在这里插入图片描述

1.建立两个entity并使用@OneToOne装饰器绑定关系:

主表 user.account.entity.ts 内容如下:

import {
  Column,
  Entity,
  PrimaryGeneratedColumn,
  BaseEntity,
  OneToOne,
} from 'typeorm';

import { UserInfo } from './user.info.entity';

/* 
  关于主表和副表的概念:
  主表就是你经常操作的那个表。比如:user_account就是主表,UserAccount就是主表对应的实体类
  而操作:
  getRepository(UserAccount)
    .createQueryBuilder('user_account')
    .leftJoinAndSelect('user_account.userinfo', 'user_info.id')
    .getMany();
  上面建立表关联查询
  外键 即 @JoinColumn() 通常是放在副表里面
  
  双向关联:允许你使用QueryBuilder从双方加入关系
*/

/* 账号信息表---与用户信息建立一对一双向关联 */
@Entity()
export class UserAccount extends BaseEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({
    type: 'varchar',
    name: 'account',
    length: 20,
    default: null,
    comment: '账号',
  })
  account: string;

  @Column({
    type: 'varchar',
    name: 'password',
    length: 20,
    default: null,
    comment: '密码',
  })
  password: string;

  @OneToOne(() => UserInfo, (m) => m.account)
  userinfo: UserInfo;
}

副表 user.info.entity.ts 内容如下:

import {
  Column,
  Entity,
  PrimaryGeneratedColumn,
  BaseEntity,
  OneToOne,
  JoinColumn,
} from 'typeorm';

import { UserAccount } from './user.account.entity';

/* 用户信息表---与账号信息建立一对一双向关联 */
@Entity()
export class UserInfo extends BaseEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({
    type: 'varchar',
    name: 'name',
    length: 20,
    default: null,
    comment: '姓名',
  })
  name: string;

  @Column({ type: 'int', name: 'age', default: null, comment: '年龄' })
  age: Number;

  @Column({
    type: 'varchar',
    name: 'phone',
    length: 12,
    default: null,
    comment: '手机号',
  })
  phone: Number;

  @OneToOne(() => UserAccount, (m) => m.userinfo)
  @JoinColumn()
  account: UserAccount;
}

解析:这两个实体建立完成之后,将会在数据库中自动生成两个表,表与表之间的关联依靠@OneToOne()这个装饰器来绑定,其中带有@JoinColumn()为副表并且外键放置生成在该字段。
@OneToOne()这个方法传递两个参数第一个参数为一个回调函数,直接指向绑定关联的实体类,第二个参数也是一个回调函数,但是包含一个参数,这个参数代指第一个参数中的实体类,如上的user.info.entity.ts中的m即代指UserAccount实体类,因此可以调用其中的userinfo,即m.userinfo。
这是一对一的关联方式,因此固定这么做就是了。

2.建立控制层和服务层,进行增删查改操作。

控制层 user.controller.ts 内容如下:

import {
  Controller,
  Get,
  Post,
  Delete,
  Body,
  Query,
  Put,
} from '@nestjs/common';

import { Transaction, TransactionManager, EntityManager } from 'typeorm';

import { UserService } from './user.service';

import { UserAccount } from '../entities/user.account.entity';

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

  /* 
    查询所有列表 
    @params 无
  */
  @Get('list')
  findAll(): Promise<UserAccount[]> {
    return this.userService.findAll();
  }
  /* 
    查询单个详情
    @Query ?id=xxx
   */
  @Get('detail')
  findOne(@Query() query): Promise<UserAccount> {
    return this.userService.findOne(query);
  }
  /* 
    新增账号和用户信息数据
    新增时候由于有可能出现两张表同时进行操作的情况
    因此开启事务事件:为了让同时进行的表操作要么一起完成,要么失败
    @Transaction()和 @TransactionManager() manager: EntityManager 是事务的装饰器和对象
    @Body 
    {
      "account": "admin1",
      "password": "000000",
      "userinfo": {
          "name": "zhang1",
          "age": 12,
          "phone": "15622222211"
      }
    }
  */
  @Post('add')
  @Transaction()
  addOne(
    @Body() rUser,
    @TransactionManager() manager: EntityManager,
  ): Promise<String> {
    return this.userService.addOne(rUser, manager);
  }
  /* 
    修改账号和用户信息数据
    事务同上
    @Body 
    {
      "id": "14",
      "account": "admin21112222",
      "password": "00000022",
      "userinfo": {
          "id":'10',
          "name": "zhang111222",
          "age": 12,
          "phone": "156222222"
      }
    }
  */
  @Put('update')
  @Transaction()
  updateOne(
    @Body() uUser,
    @TransactionManager() manager: EntityManager,
  ): Promise<String> {
    return this.userService.updateOne(uUser, manager);
  }
  /* 
    删除数据
    事务同上
    @Query ?id=xxx
   */
  @Delete('del')
  @Transaction()
  delOne(
    @Query() query,
    @TransactionManager() manager: EntityManager,
  ): Promise<String> {
    return this.userService.delOne(query, manager);
  }
}

这里的内容非常简单:就是加了四个接口而已。其中涉及到了事务的概念。可以参考文章:
https://editor.csdn.net/md?articleId=114012116

服务层user.service.ts内容如下:

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

import { UserInfo } from 'src/entities/user.info.entity';
import { UserAccount } from '../entities/user.account.entity';

/* 
  使用Repository<>对象执行增删查改的操作
*/
@Injectable()
export class UserService {
  constructor(
    /* 
      使用InjectRepository装饰器并引入Repository<>对象
      这样就可以使用typeorm的Repository Api相关的操作
      Repository Api:https://typeorm.biunav.com/zh/repository-api.html#repositoryapi 
      UserAccount是主表对应的实体类
      */
    @InjectRepository(UserAccount)
    private readonly userRepository: Repository<UserAccount>,
  ) {}
  /* 
    获取所有用户数据列表
  */
  async findAll(): Promise<UserAccount[]> {
    /*
      第一种方式直接调用Repository Api的相关方法
      联表查询:
      第一个参数:relations: ['userinfo'] 属性
      (注意:userinfo字段来源于UserAccount实体类中的userinfo字段)
    */

    // let list = await this.userRepository.find({ relations: ['userinfo'] });

    /*
      第二种方式构建QueryBuilder查询
      // 这里讲个个人理解:getConnection()、getManager()、getRepository()三者的区别
      // getConnection().manager === getManager()
      // getConnection().getRepository() === getRepository()
      // getManager().getRepository() === getRepository()
      // getConnection()获取连接,其实就是将数据库连接所有的内容都拿到了
      // getManager()获取实体存储库集合,这样就可以管理任何实体,并且随意使用任何实体
      // getRepository()获取具体实体,然后可以对具体实体操作
      // 开发中三者使用方式:三个都能使用.createQueryBuilder()方法构建内容,但是构建的结构有所区别
      // 例如:
      /* 获取user表所有数据getConnection()方式
        getConnection()
        .createQueryBuilder()
        .select('user')
        .from(User, 'user')
        .getMany();
      */
    /* 获取user表所有数据getManager()方式
        getManager()
        .createQueryBuilder(User,'user')
        .getMany();
      */
    /* 获取user表所有数据getRepository()方式
        getRepository(User)
        .createQueryBuilder('user')
        .getMany();
      */

    let list = await getRepository(UserAccount)
      .createQueryBuilder('UserAccount')
      .leftJoinAndSelect('UserAccount.userinfo', 'UserInfo.id')
      .getMany();

    /*
      第三种方式直接使用getManager().query("sql语句")执行原生sql操作即可。
      补充:
      1.三种方式中以第一种和第二种方式使用最多,
        争取能用第一种就用第一种,否则可选择第二种,实在不行则使用第三种。
      2.相关方法的参数请参考官网
    */
    return list;
  }
  /* 
    获取单个用户详情
  */
  async findOne(query): Promise<UserAccount> {
    /*
      联表查询:
      第一个参数:传递{id:xx}
      第二个参数:传递{relations: ['userinfo'] }
    */
    let list = await this.userRepository.findOne(
      { id: query.id },
      { relations: ['userinfo'] },
    );
    return list;
  }
  /* 
    新增用户
    rUser:用户传递的内容
    manager:实体管理对象,参考官网实体管理器api
    rUser格式
    {
      "account": "admin1",
      "password": "000000",
      "userinfo": {
          "name": "zhang1",
          "age": 12,
          "phone": "15622222211"
      }
    }
  */
  async addOne(rUser, manager: EntityManager): Promise<String> {
    // 第一种方式:常规新增方式(typeorm的示例)---先增副表,绑定关联后再增主表
    // 副表新增
    let muser = new UserInfo();
    muser.name = rUser.userinfo.name;
    muser.age = rUser.userinfo.age;
    muser.phone = rUser.userinfo.phone;
    await manager.save(UserInfo, muser);
    // 主表新增
    let mAccount = new UserAccount();
    mAccount.account = rUser.account;
    mAccount.password = rUser.password;
    // 绑定关联
    mAccount.userinfo = muser;
    // 保存。注意:只能是save方法,如果用inset将导致副表无外键(即关联无用)
    await manager.save(UserAccount, mAccount);

    /*
      // 另一种方式,先增主表,然后得到主表信息后,再增副表
      let mAccount = new UserAccount();
      mAccount.account = rUser.account;
      mAccount.password = rUser.password;
      // 新增主表信息,并保存
        // 注意:
        // save方法保存后返回的是一个对象{ id: 12, account: 'awqe9527', password: '123654' }
        // 而insert方法保存后则返回的是一个
        // {
        //   identifiers: [ { id: 12 } ],
        //   generatedMaps: [ { id: 12, account: 'awqe9527', password: '123654' } ],
        //   raw: OkPacket {
        //     fieldCount: 0,
        //     affectedRows: 1,
        //     insertId: 12,
        //     serverStatus: 3,
        //     warningCount: 0,
        //     message: '',
        //     protocol41: true,
        //     changedRows: 0
        //   }
        // }
      const accountInfo: { [propName: string]: any } = await manager.save(
        UserAccount,
        mAccount,
      );
      console.log('accountInfo', accountInfo);
      // 副表新增
      let muser = new UserInfo();
      muser.name = rUser.userinfo.name;
      muser.age = rUser.userinfo.age;
      muser.phone = rUser.userinfo.phone;
      // 设置外键id内容
      muser.account = accountInfo.id;
      await manager.insert(UserInfo, muser);
    */
    return '新增成功!';
  }
  /* 
    修改用户
    uUser:用户传递的内容
    manager:实体管理对象,参考官网实体管理器api
    uUser格式
    {
      "id": "14",
      "account": "admin21112222",
      "password": "00000022",
      "userinfo": {
          "id":'10',
          "name": "zhang111222",
          "age": 12,
          "phone": "156222222"
      }
    }
  */
  async updateOne(uUser, manager: EntityManager): Promise<String> {
    // 修改则无顺序要求,只需要拿到对应的id,然后到对应的表中修改数据即可,
    let muser = new UserInfo();
    muser.name = uUser.userinfo.name;
    muser.age = uUser.userinfo.age;
    muser.phone = uUser.userinfo.phone;
    await manager.update(UserInfo, uUser.userinfo.id, muser);

    let mAccount = new UserAccount();
    mAccount.account = uUser.account;
    mAccount.password = uUser.password;
    await manager.update(UserAccount, uUser.id, mAccount);

    return '修改成功!';
  }
  /* 
    删除用户
  */
  async delOne(query, manager): Promise<String> {
    /* 
      1.为什么这里传入的是account而不是accountId:注意前面注入的是实体类UserInfo,因此传入实体类中对应的account即可
      2.删除必须先删副表,再删主表
      3.使用remove方法可以做到一个方法连带删除
    */
    manager.delete(UserInfo, { account: query.id });
    manager.delete(UserAccount, query.id); // 这里的第二个参数等同于{ id: query.id }
    return '删除成功!';
  }
}

注意:记得在user.module.ts中注册
user.module.ts内容如下:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserController } from './user.controller';
import { UserService } from './user.service';

import { UserAccount } from '../entities/user.account.entity';
import { UserInfo } from '../entities/user.info.entity';

@Module({
  imports: [TypeOrmModule.forFeature([UserAccount, UserInfo])],
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
nestjs可以和typeorm配合使用typeorm是一个支持多种数据库的ORM框架,可以简化数据库的操作。下面是nestjs如何使用typeorm的步骤: 1. 安装typeorm和相应的数据库驱动,例如mysql, postgres等。可以使用npm命令进行安装: ``` npm install typeorm mysql ``` 2. 在nestjs中创建一个数据库模块,可以在模块中配置数据库连接等信息。例如: ```typescript import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from './user.entity'; @Module({ imports: [ TypeOrmModule.forRoot({ type: 'mysql', host: 'localhost', port: 3306, username: 'root', password: 'password', database: 'test', entities: [User], synchronize: true, }), ], }) export class DatabaseModule {} ``` 3. 创建一个实体类,表示数据库中的一个表格。例如: ```typescript import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column() age: number; @Column() email: string; } ``` 4. 在nestjs的服务中使用typeorm进行数据库操作。例如: ```typescript import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { User } from './user.entity'; @Injectable() export class UserService { constructor( @InjectRepository(User) private readonly userRepository: Repository<User>, ) {} async findAll(): Promise<User[]> { return this.userRepository.find(); } async create(user: User): Promise<User> { return this.userRepository.save(user); } } ``` 这样,nestjs就可以和typeorm配合使用进行数据库操作了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值