开发笔记
开发笔记

nestjs框架结合typeorm的typescript实践

nestjs框架结合typeorm的typescript实践

最近在学习nestjs框架,相比之前用过的koa2和express有许多不同,koa2结合sequelize之前已经使用过,感觉还是不错的,这次也是学习一下强大的typeorm,其中一些关键操作记录在这里,一遍日后查看。网络上很多帖子已经过时了,对应的很多方法都已经弃用了,所以踩了不少坑。

如果用nestjs开发,那么cli命令一定要用,非常方便。

创建一个module所有文件

nest g res <module name> <dir>

比如nest g res user modules将会在modules目录下创建user文件夹,里面会创建controller、module、service、entities等文件和目录。我们只需进一步完善即可。

entity

这在typeorm中是对数据库表的映射,这里我们以mysql为例,后续所有操作都是基于mysql,比如以下文件

//modules/building/entities/house/entity.ts
import { Column, Entity, JoinColumn, ManyToOne, OneToMany, OneToOne, PrimaryColumn, PrimaryGeneratedColumn } from 'typeorm';
import { Building } from './building.entity';
import { Renter } from '@/modules/renter/renter.entity';
@Entity({
  name: 'house'
})
export class House {
  @PrimaryColumn()
  id: string;

  @Column()
  rank: number;

  @Column({ nullable: false})
  cname: string;

  @Column({ nullable: true})
  description: string;

  @Column({ nullable: true })
  usage: string;

  @Column({ nullable: true })
  buildingId: string;

  @ManyToOne(()=> Building, (c)=>c.houses)
  building: Building

  @OneToMany(()=> Renter, (c)=>c.house)
  renters: Renter[]
}

PrimaryColumn是定义主键字段的,如果是自增的话,有单独一个定义PrimaryGeneratedColumn

表与表之间的关系可以通过ManyToOneOneToManyManyToMany实现,这里暂时只用到了一对多的关系。上面的表HouseN:1BuildingHouse1:NRenter,就是这样定义的。按照本人的理解,如果这样定义,意味着mysql中也要定义相应的外键,如果数据库中不创建外键,不建立外键约束,也是可以做到的,那就要在createQueryBuilder中手动指定,没法在封装的findAllfindOne等方法中使用relations约束。

findAll中使用order排序


  async findAll() {
    const buildings = await this.buildingRep.find({
      relations:{
        houses:true
      },
      order:{
        rank: 'ASC'
      }
      
    });
    return buildings ;
  }

网上有的帖子是这么写的

find({
	relations:{
		houses:true
	},
	order:'rank ASC'
})

现在已经不行了,目前用的typeorm是10.0.0

关联查询中的count用法

这个在官方文档中未找到,可能是本人疏忽,后面在issue中找到了用法

  async findHouses(id: string){
    const houses = await  this.dataSource
    .getRepository(House)
    .createQueryBuilder("h")
    .loadRelationCountAndMap('h.renters_count', 'h.renters')
    .orderBy("h.rank")
    .addOrderBy("h.id")
    .where("h.buildingId = :id",{id: id})
    .printSql()
    .getMany()
    return houses;
  }

这里使用了loadRelationCountAndMap方法,意思就是相关表先count然后再map到原表字段中,还是比较好理解的,这样返回的数组中每一项都多了一个renters_count字段,就是关联子项的个数,这在一些接口中是很有用的,没必要返回子表的所有字段。

新建实例并插入到表中的优雅做法(官网上是这么说的)

    const newHouse = this.houseRep.create(createHouseDto);
    newHouse.id = randomUUID()
    newHouse.rank = newHouse.rank||99
    await  this.dataSource
    .createQueryBuilder()
    .insert()
    .into(House)
    .values([newHouse])
    .execute()
    return true;
  

这里还是用了dataSource的做法,实际上,中文文档是比较落后的,并没有dataSource的用法,还是在介绍connection的用法,也算是踩了个坑。

image-20240613141644899

看英文官网的介绍:

image-20240613141754229

需要注意的是,nestjs中使用DataSource需要单独设置一下,因为typeorm不限定于具体框架,所以具体实现各个框架略有不同。这里使用方法如下:

  1. 导入相关包
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { DataSource, Repository } from 'typeorm';
  1. 加入到构造器注入中(不知道正确说法)
@Injectable()
export class BuildingService {
  constructor(
    /*@InjectRepository(Building)
    private buildingRep: Repository<Building>,
    @InjectRepository(House)
    private houseRep: Repository<House>,
    */
    @InjectDataSource()
    private dataSource: DataSource,
  ) {}

这里是用在了service中,当然也可以用在controller中,方法一样。只要注入了,下面就可以使用this.DataSource进行连接使用了,具体用法上面已经用过了,参照一下。

dto的继承

便于统一管理,比如createDto中修改了,那么没有必要每一个dto都去修改一遍,用继承完美的实现了自动对应。示例代码如下:

import { PartialType } from '@nestjs/mapped-types';
import { CreateBuildingDto } from './create-building.dto';

export class UpdateBuildingDto extends PartialType(CreateBuildingDto) {}

已建立联系的两个表关联查询

const buildings = await this.buildingRep.find({
      relations:{
        houses:true
      },
      order:{
        rank: 'ASC'
      }
      
    });

未建立关联的两个表进行关联查询

这个需求也是非常大,因为关联表之间需要维护很多东西,在一些场景中,关联不需要非常强,那么直接关联查询将会比较方便,更重要的是,关联表指定返回部分字段。

首先看全部map的方法

    const logs =await this.dataSource
    .getRepository(Log)
    .createQueryBuilder("log")
   .leftJoinAndMapOne('log.renter_info',Renter,'renter','renter.uid=log.renterUid')
    .orderBy("log.createdAt","DESC")
    .limit(2000)
    .getMany()

这样就会将renter表所有字段都map到renter_info字段中

下面这种方法,可以只选择部分字段

    const logs =await this.dataSource
    .getRepository(Log)
    .createQueryBuilder("log")
    .leftJoinAndMapOne('log.renter', Renter,'renter','renter.uid=log.renterUid')
    .select('log')
    .addSelect(['renter.cname','renter.houseId'])
    .orderBy("log.createdAt","DESC")
    .limit(2000)
    .getMany()

上面代码中,select()里面选的是'log',这个一定要加,不然后续选择部分字段将会无效,也可以指定具体的字段,用数组表示即可,比如.select([]'log.id','log.balabala']),下面的addSelect()是针对关联表的字段选择,这里需要注意的是,select('log')必须要加上,否则addSelect将无效,会把所有字段都map进去。

windows11环境下编译安装openpose

windows11环境下编译安装openpose

基本环境

  • windows11
  • cuda11.7
  • cuDNN8.8
  • python3.10.13 (版本应该影响不大)
  • GPU Nvidia 4060

准备物料

models:

链接:https://pan.baidu.com/s/1WLy4sIL4y8kqz5iXBhnhYw?pwd=d34r
提取码:d34r

3rd_party

链接:https://pan.baidu.com/s/1uVJm0eLEGhG5GMUShDkHFw?pwd=nzh5
提取码:nzh5

安装过程

参考博客:OpenPose笔记–Windows+Cmake的pyhton接口编译(CPU_ONLY)_openpose cmake-CSDN博客

  1. 解压3rdparty压缩包,整个替换原有的目录,里面包含了各种依赖文件image-20240528143452881
  2. 解压models压缩包,整个替换image-20240528143655158
  3. 打开编译软件 cmake-guiimage-20240528143946931
  4. 添加python路径变量image-20240528144135770
  5. 选择openpose项目路径以及打包路径image-20240528144221956
  6. 勾选打包选项
    • 使用python的话必选image-20240528144307641
    • cuda支持image-20240528144405833
    • 模型都勾选,方便扩展image-20240528144453957
  7. configure和generateimage-20240528144646007
  8. 用vs 2022打开工程文件,可以直接点击open project按钮
  9. 选择release模式,分别构建右侧四个项目image-20240528144858839
  10. debug模式也可以,选择OpenPoseDemo,然后debug模式启动,默认会调用摄像头image-20240528145157697

    image-20240528145120981

  11. 4个目录都生成成功后,检查一下目录,是否有响应文件生成
    • build_GPU/bin 目录下都是dll文件image-20240528145420254
    • build_GPU/python/openpose/Release 目录下有包文件image-20240528145615509
    • build_GPU/x64/Release 目录下有cuda的openpose动态库image-20240528145747318

编译到此结束,如果以上文件都有,那么编译就成功了。

测试使用

这里主要针对python使用,那么测试python代码导入,打开examples\tutorial_api_python\01_body_from_image.py,将相对路径修改成如下

image-20240528150141573

image-20240528150506387

不知道为什么,系统变量都改了,还是导入失败,那么用替代方案,把build_GPU/bin下面的所有文件、build_GPU/x64/Release下面的openpose.dll都放到build_GPU/python/openpse/Release中,

image-20240528150640767

再次运行,python 01_body_from_image.py,成功!

image-20240528150802775

jetson orin 上安装pytorch避坑

第一个坑

安装官网教程直接安装 pip3 install torch torchvision torchaudio –index-url https://download.pytorch.org/whl/cu118

不管什么版本,都用不了cuda

找到nvidia官网论坛上有专门的介绍jetson板子的驱动:PyTorch for Jetson – Jetson & Embedded Systems / Jetson Nano – NVIDIA Developer Forums

然后下载whl后直接安装,下载文件名比较长

第二个坑 文件名不能修改

修改之后安装的时候会报错,报的就是文件名不对,也不知道这个设计思路到底是为啥

安装完torch之后,可以看到这个版本比较特殊,好歹cuda可以用了

使用yolo的时候下一个坑出现了,ultralytics 需要torchvision模块,想当然的就 pip install torchvision,结果把刚安装的torch给卸载了,重新装上了torch 2.1.0版本,一夜回到解放前

只能找jetson板子对应的torch vision模块,找了半天,竟然在刚才下载torch的页面上,而且还贴心的给出了torch和torchvision版本对应关系和安装方法,真的感动死

ubuntu安装xrdp时遇到的问题记录

手上有一台jstson orin的机器,装了ubuntu系统,因为之前别的机器上装过,没有遇到什么问题,没想到还是出了问题,特此记录!

安装:sudo apt-get install xrdp

在windows 上打开远程连接工具,发现连接不上,一点登陆就闪退,于是找问题

先是服务器上在设置里面打开屏幕共享和远程控制功能,然后还是不行,切换到home目录,有一个隐藏文件,.xsession-errors ,cat 命令打开,发现有错误,如下

网上一搜,说是sh命令在ubuntu上面默认是dash而不是bash,所以无法不兼容,卧槽塔猴,之前为啥没问题!

解决办法,要么就是修改脚本,但是这里没法改,只能将dash改为bash

方法: sudo dpkg -reconfigure dash

选择no即可

然后重启xrdp服务。systemctl restart xrdp

这下终于可以连上了,但是屏幕除了背景,啥也没有

这又是啥问题!

一搜原来是ubuntu 开启了3d桌面

这其实也没用,因为图中的帖子是2012年针对老版本的,新版本不适用,又找到一篇帖子,

这一次竟然解决了

成功出现了画面

特此记录

特色

python对视频或图片使用图片蒙版的小技巧

1.使用cv2将png图片作为蒙版进行应用,特别优雅的进行修改img[imgMask==255] = [128, 128, 128]

import cv2
img = cv2.imread('ori.png',cv2.IMREAD_COLOR)
imgMask = cv2.imread('1.png',cv2.IMREAD_GRAYSCALE)
img[imgMask==255] = [128, 128, 128] 
res = img
cv2.imshow('maskimg',res)
cv2.waitKey(0)

2.对于视频来说,也是类似操作

import cv2
cap = cv2.VideoCapture('../videos/1.mp4')
imgMask = cv2.imread('1.png',cv2.IMREAD_GRAYSCALE)
# 检查视频是否成功打开
if not cap.isOpened():
    print("无法打开视频文件")
    exit()
while cap.isOpened():
    ret,img = cap.read()
    img[imgMask==255] = [128, 128, 128] 
    cv2.imshow('maskimg',img)
    cv2.waitKey(1)
cap.release()

raid1阵列的信息查询和故障修复

在centos系统下,先试用命令cat /proc/mdstat查看,

cat /proc/scsi/scsi查看:

ls /dev可查看所有的设备:

最后可以试用mdadm命令查看相应的磁盘信息:

可以看到有一块磁盘降级且被移除了,有可能是断电导致,也有可能是磁盘有损伤导致,由于此盘是在x79主板上硬raid组成,所以需要在服务器端操作,大致的步骤就是把坏盘取下,然后检查磁盘状况,没有问题的话,就格式化一下再装回去,有问题的话就要换一块新的。装上之后执行重建即可。

参考删除阵列磁盘的帖子:https://www.itshiye.com/18/33768.html