nestjs入门学习规划:https://blog.csdn.net/lxy869718069/article/details/114028195
多对多是一种 A 包含多个 B 实例,而 B 包含多个 A 实例的关系。
例如:
一个问题可能有多种类别,比如:问题A是既是一个地理类又是计算类问题,而一个类别也可以修饰多个问题,比如:地理类问题可以有问题A也可以有问题C。
数据库显示如下:
多对多关系,实际上就是多用一张表来存放两种数据之间的关联关系。
目录结构:
表1 category.entity.ts 内容如下:
import {
Column,
Entity,
PrimaryGeneratedColumn,
BaseEntity,
ManyToMany,
} from 'typeorm';
import {
Question } from './question.entity';
/* 类别表 */
@Entity()
export class Category extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({
type: 'varchar', name: 'name', comment: '类别名称' })
name: string;
@ManyToMany(() => Question, (question) => question.categories)
questions: Question[];
}
表2 question.entity.ts 内容如下:
import {
Column,
Entity,
PrimaryGeneratedColumn,
BaseEntity,
JoinTable,
ManyToMany,
RelationId,
} from 'typeorm';
import {
Category } from './category.entity';
/* 问题表---主表带有@JoinTable */
@Entity()
export class Question extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({
comment: '标题' })
title: string;
@ManyToMany(() => Category, (category) => category.questions)
@JoinTable()
categories: Category[];
}
解析:这两个实体建立完成之后,将会默认在数据库中生成三张表,虽然只有两个实体,但是确实会生成三张表,其中第三张为关联表,只记录两表之间的关联关系。
@ManyToMany这个方法传递两个参数第一个参数为一个回调函数,直接指向绑定关联的实体类,第二个参数也是一个回调函数,但是包含一个参数,这个参数代指第一个参数中的实体类
因此可以:category.questions 和 question.categories
@JoinTable()通常放置在主要操作的那个表中
控制层 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 {
Question } from '../entities/question.entity';
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {
}
/*
查询所有列表
@Query 无
*/
@Get('list')
findAll(): Promise<Question[]> {
return this.userService.findAll();
}
/*
查询单个详情
@Query ?id=xxx
*/
@Get('detail')
findOne(@Query() query): Promise<Question> {
return this.userService.findOne(query);
}
/*
新增账号和用户信息数据
新增时候由于有可能出现两张表同时进行操作的情况
因此开启事务事件:为了让同时进行的表操作要么一起完成,要么都失败
@Transaction()和 @TransactionManager() manager: EntityManager 是事务的装饰器和对象
@Body
{
"title": "问题5",
"categorylist": [
1,
3
]
}
*/
@Post('add')
@Transaction()
addOne(
@Body() rUser,
@TransactionManager() manager: EntityManager,
): Promise<String> {
return this.userService.addOne(rUser, manager);
}
/*
修改账号和用户信息数据
事务同上
@Body 有数据id则修改否则新增,然后对比数据库数据进行多余的删除
{
"id":3,
"title": "问题555",
"categorylist": [
1,
2
]
}
*/
@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 {
getRepository } from 'typeorm';
import {
Question } from './../entities/question.entity';
import {
Category } from './../entities/category.entity';
/*
使用Repository<>对象执行增删查改的操作
*/
@Injectable()
export class UserService {
constructor() {
}
/*
获取所有用户数据列表
*/
async findAll(): Promise<Question[]> {
/*
构建QueryBuilder查询
*/
let list = await getRepository(Question)
.createQueryBuilder('question')
.leftJoinAndSelect('question.categories', 'category')
.getMany();
return list;
}
/*
获取单个用户详情
*/
async findOne(query): Promise<Question> {
let list = await getRepository(Question)
.createQueryBuilder('question')
.leftJoinAndSelect('question.categories', 'category')
.where('question.id = :id', {
id: query.id })
.getOne();
return list;
}
/*
新增用户
rUser格式
{
"title": "问题5",
"categorylist": [
1,
3
]
}
*/
async addOne(rUser, manager): Promise<String> {
let lists = []; // 用于保存拥有的categories
// 目的用于获取关联信息相关的内容
if (Object.keys(rUser.categorylist).length != 0) {
for (let i = 0; i < rUser.categorylist.length; i++) {
let listOne = await manager.findOne(Category, {
id: rUser.categorylist[i],
});
lists.push(listOne);
}
}
const question = new Question();
question.title = rUser.title;
// 此处为关联表内容新增的关键
question.categories = lists;
await manager.save(Question, question);
return '新增成功!';
}
/*
修改用户
uUser格式
{
"id":3,
"title": "问题555",
"categorylist": [
1,
2
]
}
*/
async updateOne(uUser, manager): Promise<String> {
let lists = []; // 用于保存拥有的categories
// 目的用于获取关联信息相关的内容
if (Object.keys(uUser.categorylist).length != 0) {
for (let i = 0; i < uUser.categorylist.length; i++) {
let listOne = await manager.findOne(Category, {
id: uUser.categorylist[i],
});
lists.push(listOne);
}
}
const question = new Question();
// 此处给了id并且categories赋了新内容,这样就会自动更新关联表中的数据(删除旧的新增新的)
question.id = uUser.id;
question.title = uUser.title;
// 此处为关联表内容新增的关键
question.categories = lists;
await manager.save(Question, question);
return '修改成功!';
}
/*
删除用户
*/
async delOne(query, manager): Promise<String> {
// 删除时候关联信息会自动删除
await manager.delete(Question, {
id: query.id });
return '删除成功!';
}
}
解读:单个查询和总体查询就不多做描述,仅仅只是用了关联查询而已。
新增和修改则是直接关联起来两者即可。如: question.categories = lists;,并且关联表会自动进行数据改变。
删除同样是如此,以带有@JoinTable()这个装饰器的实体类为主要操作对象。
注意:记得在user.module.ts中注册
user.module.ts内容如下:
import {
Module } from '@nestjs/common';
import {
UserController } from './user.controller';
import {
UserService } from './user.service';
import {
TypeOrmModule } from '@nestjs/typeorm';
import {
Category } from './../entities/category.entity';
import {
Question } from './../entities/question.entity';
@Module({
imports: [TypeOrmModule.forFeature([Question, Category])],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {
}
目录结构:
注意:这里是有三个实体类的。
实体1 :category.entity.ts 内容为:
import {
Column, Entity, PrimaryGeneratedColumn, BaseEntity } from 'typeorm';
/* 类别表 */
@Entity()
export class Category extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({
type: 'varchar', name: 'type', comment: '类别名' })
type: string;
@Column({
type: 'varchar', name: 'value', comment: '类别值' })
value: string;
}
实体2 :question.entity.ts 内容为:
import {
Column, Entity, PrimaryGeneratedColumn, BaseEntity } from 'typeorm';
/* 问题表 */
@Entity()
export class Question extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({
comment: '标题',
name: 'title',
})
title: string;
@Column({
comment: '描述',
name: 'description',
nullable: true,
})
description: string;
}
实体3 :question_category.entity.ts 内容为:
import {
Column, Entity, PrimaryGeneratedColumn, BaseEntity } from 'typeorm';
/* 问题和类别---关联表 */
@Entity()
export class QuestionCategory extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({
comment: '问题id',
name: 'questionId',
})
questionId: string;
@Column({
comment: '类别id',
name: 'categoryId',
})
categoryId: string;
}
仔细观察上面三个实体的关系会知道,question_category.entity就是前两者的关联内容,但是他们之间并没有用@ManyToMany来进行绑定,甚至category.entity和question.entity看不出有任何的关联关系。
但我们就是利用question_category.entity 来进行多对多的数据保存于处理。这样的好处是能够让实体类与表进行完整的一一对应。我更喜欢这种。
控制层 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 {
Question } from '../entities/question.entity';
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {
}
/*
查询所有列表
@params 无
*/
@Get('list')
findAll(): Promise<Question[]> {
return this.userService.findAll();
}
/*
查询单个详情
@Query ?id=xxx
*/
@Get('detail')
findOne(@Query() query): Promise<Question> {
return this.userService.findOne(query);
}
/*
新增账号和用户信息数据
新增时候由于有可能出现两张表同时进行操作的情况
因此开启事务事件:为了让同时进行的表操作要么一起完成,要么都失败
@Transaction()和 @TransactionManager() manager: EntityManager 是事务的装饰器和对象
@Body :注意先给category表随便加三条数据
{
"title": "问题1",
"description": "这是1号问题的描述",
"list": [1,3]
}
}
*/
@Post('add')
@Transaction()
addOne(
@Body() rUser,
@TransactionManager() manager: EntityManager,
): Promise<String> {
return this.userService.addOne(rUser, manager);
}
/*
修改账号和用户信息数据
事务同上
@Body
{
"id": 1,
"title": "问题1.111",
"description": "问题1.111的描述",
"list": [1,2]
}
*/
@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);
}
}
解析:控制层的内容和第一种方式基本上没什么区别,都是为了定义几个接口名称和事务而已。
import {
Injectable } from '@nestjs/common';
import {
getRepository } from 'typeorm';
import {
Question } from './../entities/question.entity';
import {
Category } from './../entities/category.entity';
import {
QuestionCategory } from './../entities/question_category.entity';
/*
使用Repository<>对象执行增删查改的操作
*/
@Injectable()
export class UserService {
constructor() {
}
/*
获取所有用户数据列表
*/
async findAll(): Promise<Question[]> {
// 构建QueryBuilder查询
/*
1.Question、Category,QuestionCategory 指的是实体类的名称
2.innerJoin()是对关联表进行关联,quescate,question,category都是别名
------ 三个参数含义分别是:实体对象,别名,关联关系
3.innerJoinAndMapMany()中的question.list是为了给question增加一个list字段用于保存Category的所有内容
------ 四个参数含义分别是:展示列表名,实体对象,别名,关联关系
*/
let list = await getRepository(Question)
.createQueryBuilder('question')
.innerJoin(
QuestionCategory,
'quescate',
'question.id = quescate.questionId',
)
.innerJoinAndMapMany(
'question.list',
Category,
'category',
'category.id = quescate.categoryId',
)
.getMany();
return list;
/*
另一种使用getManager().query("sql语句")执行原生sql操作即可。
补充:联表查询建议使用QueryBuilder自由构建出所需要的查询内容
*/
}
/*
获取单个用户详情
*/
async findOne(query): Promise<Question> {
let list = await getRepository(Question)
.createQueryBuilder('question')
.innerJoin(
QuestionCategory,
'quescate',
'question.id = quescate.questionId',
)
.innerJoinAndMapMany(
'question.list',
Category,
'category',
'category.id = quescate.categoryId',
)
.where('question.id = :id', {
id: query.id })
.getOne();
return list;
}
/*
新增用户
rUser格式:注意先给category表随便加三条数据
{
"title": "问题1",
"description": "这是1号问题的描述",
"list": [1,3]
}
*/
async addOne(rUser, manager): Promise<String> {
// 先保存问题的数据
let question = new Question();
question.title = rUser.title;
question.description = rUser.description;
const que = await manager.save(Question, question);
if (Object.keys(rUser.list).length != 0) {
// 后保存关联表的数据
for (let i = 0; i < rUser.list.length; i++) {
let qescat = new QuestionCategory();
qescat.categoryId = rUser.list[i];
qescat.questionId = que.id;
await manager.save(QuestionCategory, qescat);
}
return '新增成功!';
}
}
/*
修改用户:有数据id则修改否则新增,然后对比数据库数据进行多余的删除
uUser格式
{
"id": 1,
"title": "问题1.111",
"description": "问题1.111的描述",
"list": [1,2]
}
*/
async updateOne(uUser, manager): Promise<String> {
// 先修改问题表的数据
let question = new Question();
question.id = uUser.id;
question.title = uUser.title;
question.description = uUser.description;
await manager.update(Question, {
id: question.id }, question);
// 在根据问题的id删除关联表中对应的数据
await manager.delete(QuestionCategory, {
questionId: uUser.id });
// 之后在将新的关联数据添加进关联表
for (let i = 0; i < uUser.list.length; i++) {
let qescat = new QuestionCategory();
qescat.categoryId = uUser.list[i];
qescat.questionId = uUser.id;
await manager.save(QuestionCategory, qescat);
}
return '修改成功!';
}
/*
删除用户
*/
async delOne(query, manager): Promise<String> {
// 先删除关联表内容
await manager.delete(QuestionCategory, {
questionId: query.id });
// 然后在删除主表内容
await manager.delete(Question, {
id: query.id });
return '删除成功!';
}
}
解析:
查询使用了 innerJoin 和 innerJoinAndMapMany这两个方法,这是关联查询的意思,用于关联第三张表,然后紧接着查询第二张表,这样三张表都能进行联动。具体方式代码有说明。
新增则是根据用户提交的数据,先新增主要的表数据,然后循环保存到关联表中去。分成两步来做,
修改则是修改完主数据之后,删除该主数据id在关联表中的内容,然后再把全新的关联关系新增。
删除则是先删除关联表中的管理数据,然后再删除主表的数据。
而 user.module.ts 中的内容如下:
import {
Module } from '@nestjs/common';
import {
UserController } from './user.controller';
import {
UserService } from './user.service';
import {
TypeOrmModule } from '@nestjs/typeorm';
import {
Category } from './../entities/category.entity';
import {
Question } from './../entities/question.entity';
@Module({
imports: [TypeOrmModule.forFeature([Question, Category])],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {
}
基本上和第一种方法一样,这里同样不需要注册第三方的关联表
文章浏览阅读113次。1]岑威钧,王肖鑫,蒋明欢.基于EEMD-LSTM-ARIMA的土石坝渗压预测模型研究[J].水资源与水工程学报,2023,34(02):180-185.[2]沈露露,梁嘉乐,周雯.基于ARIMA-LSTM的能量预测算法[J].无线电通信技术,2023,49(01):150-156.(3)采用确定好阶数的ARIMA(p , d ,q)拟合时间序列,并根据预测后的数据和原时间序列进行结果统计和预测精度分析。# ===========主程序================dim = 5 # 鲸鱼的维度。
文章浏览阅读609次。都会需要有用到BaseActivity,从最开始的initData、initView,到后来需要承载监听推送、监听网络变化的广播、延迟锁定等等各种需求,BaseActivity的功能越来越杂,越来越密集。相对实际的页面上的功能需求,基类的封装经过这样长时间的无脑堆砌,到最后看起来会更匪夷所思。所以从一开始,Base的封装就要足够清晰、稳健、可扩展。AndroidBase我的思路是分层继承,每一层只..._android initviewcomponent initdatacomponent
文章浏览阅读1.2k次。系统主要功能模块:1)系统功能模块:地方美食分享网站,在网站首页可以查看首页,外国美食,中式美食,热门菜品,论坛,新闻资讯,留言板,个人中心,后台管理等内容,并进行详细操作;2)管理员功能模块:管理员登录,进入系统前在登录页面根据要求填写用户名和密码,选择角色等信息,点击登录进行登录操作;3)用户功能模块:用户登录进入地方美食分享网站可以对首页,个人中心,外国美食管理,中式美食管理,热门菜品管理,论坛管理,我的收藏管理,留言板管理等进行相应操作_springbootvue项目源码网站
文章浏览阅读901次。一、winsock中#include <winsock.h>原型intselect(intnfds,fd_set*readfds,fd_set*writefds,fd_set*exceptfds,const struct timeval*timeout);nfds:本参数忽略,仅起到兼容作用。readf..._socket fd_set
文章浏览阅读2.2k次。引言前段时间进新公司,做视频会议,会诊方向的。不采用任何第三方框架,包括推流,拉流,编解码等视频处理,都是自己来编写,除了音频部分要用到webrtc来处理声音降噪,增益等。过程很艰辛,也是踩了无数的坑,总算项目算是整完了。在此,记录一下音视频这块所需要掌握的一些知识点,很多网上能找到的,在这里我就尽量不会太费笔墨。写的不好,轻拍,大家一起进步。1.视频流 H264 分析网上有很多的对裸流H264_ios15.1的缺陷 h264
文章浏览阅读1.8w次,点赞13次,收藏75次。Marvell推出了四款车载用交换机芯片,88Q5050,88Q5050, 88Q5072和88Q6113。其中88Q5030有5 Port用于通信,88Q5050有8 Port用于通信,5072与6113有11 Port用于通信。由于项目中用到了88Q5050,所以本文中只涉及到88Q5050的内容。本文是对使用88Q5050的梳理和总结。_88q5050
文章浏览阅读9.1k次。PyCharm: 4.0.5PyQ5: 5.4.1Python: 3.4.31.安装插件:Settings->Plugins->Browse Repositories, 找到 Native Neighbourhood, 点 install plugin。2. Settings-> External Tools 点 + 号,添加 QT DesignerWo_can't open file 'c.pyuic
文章浏览阅读958次,点赞10次,收藏25次。(杂谈)攻击者与开发者的无形碰撞–逻辑漏洞的挖掘实战及反思(一) 逻辑漏洞的挖掘 (本文约3000字看完大约20分钟) 本文只代表个人的经历与见解,写的不好请多多理解。前瞻 随着框架技术的不断发展,传统的漏洞,sql,xss,早已经被底层框架所消灭,(当然,不排除能够一一些其他方式利用 比如说“组合拳”),另外,加上各种“云waf”的_记一次某运营商逻辑漏洞挖掘
文章浏览阅读2.4k次。NDK,JNI编程时,有时候会因为改了函数接口,导致出现LD.exe ...undefined reference to .XXX的错误,很可能只要把obj/local/armeabi-v7ma/objs/下面的目录删掉重新编译就ok了_ld.exe undefined reference to
文章浏览阅读720次。sigmoid函数(也叫逻辑斯谛函数):其实逻辑斯谛函数也就是经常说的sigmoid函数,它的几何形状也就是一条sigmoid曲线。logistic曲线如下:softmax函数的定义:softmax是logistic函数的一般化,它将任意实数的k维向量z压缩(映射)为范围(0,1)内的实数的k维向量矩阵Z,向量Z中的所有元素的总和为1。这句话既表明了softmax函数与logistic函数的关系,也同时阐述了softmax函数的本质就是将一个K维的任意实数向量压缩(映射)成另一个K维的实数向量,其_1-sigmoid
文章浏览阅读6.2k次。在windows server 2012操作系统上安装sqlserver 2005,会提示兼容性问题,不要理会她,点击“运行程序”即可:在安装过程中,如果提示“SQL Server服务无法启动”,如下:下载“SQL2005服务启动失败.zip”,里面含有文字说明,如下:..._sql2005安装提示sql server服务无法启动
文章浏览阅读2.9k次。我尝试了好多机器人框架,大部分都开发前景不好,无意中发现了这个go-cqhttp机器人框架,使用http协议将消息推送到URL上面,也是使用URL对他回应,即可实现发信。_go-cq怎么更新最新版