文章问题导向
访问数据库的方式有哪些?
typeorm增删改查操作的方式有哪些?
如果你都有了答案,可以忽略本文章,或去nest学习导图寻找更多答案
注意
学习该文章,需要有一定的mysql知识基础
你已经会使用nest连接mysql,如果不会:去学习
此文章需要多次阅读,才能容易理解
多种访问数据库的方式
第一种:Connection
import { Injectable } from '@nestjs/common';
import { Connection } from 'typeorm';
import { UsersEntity } from './entities/user.entity';
@Injectable()
export class UserService {
constructor(
private readonly connection: Connection,
) { }
async test() {
使用封装好方法:
return await this.connection
.getRepository(UsersEntity)
.findOne({ where: { id: 1 } });
使用createQueryBuilder:
return await this.connection
.createQueryBuilder()
.select('user')
.from(UsersEntity, 'user')
.where('user.id = :id', { id: 1 })
.getOne();
}
}
第二种:Repository,需要@nestjs/typeorm的InjectRepository来注入实体
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { UsersEntity } from './entities/user.entity';
import { InjectRepository } from '@nestjs/typeorm';
@Injectable()
export class UserService {
constructor(
@InjectRepository(UsersEntity) 注入实体
private readonly usersRepository: Repository<UsersEntity>,
) { }
async test() {
使用封装好方法:
return await this.usersRepository.find({ where: { id: 1 } });
使用createQueryBuilder:
return await this.usersRepository
.createQueryBuilder('user')
.where('id = :id', { id: 1 })
.getOne();
}
}
第三种:getConnection():语法糖,是Connection类型
import { Injectable } from '@nestjs/common';
import { getConnection } from 'typeorm';
import { UsersEntity } from './entities/user.entity';
@Injectable()
export class UserService {
async test() {
使用封装好方法:
return await getConnection()
.getRepository(UsersEntity)
.find({ where: { id: 1 } });
使用createQueryBuilder:
return await getConnection()
.createQueryBuilder()
.select('user')
.from(UsersEntity, 'user')
.where('user.id = :id', { id: 1 })
.getOne();
}
}
第四种:getRepository:语法糖
import { Injectable } from '@nestjs/common';
import { getRepository } from 'typeorm';
import { UsersEntity } from './entities/user.entity';
@Injectable()
export class UserService {
async test() {
使用封装好方法:
return await getRepository(UsersEntity).find({ where: { id: 1 } });
使用createQueryBuilder:
return await getRepository(UsersEntity)
.createQueryBuilder('user')
.where('user.id = :id', { id: 1 })
.getOne();
}
}
第五种:getManager
import { Injectable } from '@nestjs/common';
import { getManager } from 'typeorm';
import { UsersEntity } from './entities/user.entity';
@Injectable()
export class UserService {
async test() {
使用封装好方法:
return await getManager().find(UsersEntity, { where: { id: 1 } });
使用createQueryBuilder:
return await getManager()
.createQueryBuilder(UsersEntity, 'user')
.where('user.id = :id', { id: 1 })
.getOne();
}
}
简单总结
使用的方式太多,建议使用:2,4,比较方便
Connection核心类:
connection 等于getConnection
connection.manager 等于getManager, 等于getConnection.manager
connection.getRepository 等于getRepository, 等于getManager.getRepository
connection.createQueryBuilder 使用QueryBuilder
connection.createQueryRunner 开启事务
1. EntityManager 和 Repository都封装了操作数据的方法,注意:两者的使用方式是不一样的,(实在不明白搞这么多方法做什么,学得头大)
getManager是EntityManager的类型,getRepository是Repository的类型
2. 都可以使用createQueryBuilder,但使用的方式略有不同
增删改查的三种方式
第一种:使用sql语句,适用于sql语句熟练的同学
第二种:typeorm封装好的方法,增删改 + 简单查询
第三种:QueryBuilder查询生成器,适用于关系查询,多表查询,复杂查询
其实底层最终都会生成sql语句,只是封装了几种方式而已,方便人们使用。
第一种:sql语句
export class UserService {
constructor(
@InjectRepository(UsersEntity)
private readonly usersRepository: Repository<UsersEntity>,
) { }
async findAll() {
return await this.usersRepository.query('select * from users'); 在query中填写sql语句
}
}
第二种:typeorm封装好的api方法
这里使用第二种访问数据库的方式
export class UserService {
constructor(
@InjectRepository(UsersEntity)
private readonly usersRepository: Repository<UsersEntity>,
) { }
async findAll() {
return await this.usersRepository.findAndCount(); 封装好的方法
}
}
api方法
更多方法,在代码中,按住ctrl + 左键 点击方法名, 即可进入ts文件,获取更多的方法和参数说明
增
save(user) 创建:返回该数据的所有字段
insert(user) 快速插入一条数据,插入成功:返回插入实体,与save方法不同的是,它不执行级联、关系和其他操作。
删
remove(user) 删除:返回该数据的可见字段
softRemove(user); 拉黑:返回该数据的可见字段,该删除实体必须拥有@DeleteDateColumn()字段,被拉黑的用户还存在数据库中,但无法被find查找到,会在@DeleteDateColumn()字段中添加删除时间,可使用recover恢复
改
update(id, user) 更新:返回更新实体,不是该数据的字段
恢复
recover({ id }) 恢复:返回id,将被softRemove删除(拉黑)的用户恢复,恢复成功后可以被find查找到
查找全部
find()
find({id:9}) 条件查找,写法一,找不到返回空对象
find({where:{id:10}}) 条件查找,写法二,找不到返回空对象
findAndCount() 返回数据和总的条数
查找一个
findOne(id); 根据ID查找,找不到返回undefined
findOne({ where: { username } }); 条件查找,找不到返回undefined
根据ID查找一个或多个
findByIds([1,2,3]); 查找n个,全部查找不到返回空数组,找到就返回找到的
其他
hasId(new UsersEntity()) 检测实体是否有合成ID,返回布尔值
getId(new UsersEntity()) 获取实体的合成ID,获取不到返回undefined
create({username: 'admin12345', password: '123456',}) 创建一个实体,需要调用save保存
count({ status: 1 }) 计数,返回数量,无返回0
increment({ id }, 'age', 2); 增加,给条件为id的数据的age字段增加2,成功返回改变实体
decrement({ id }, 'age', 2) 减少,给条件为id的数据的age字段增加2,成功返回改变实体
谨用
findOneOrFail(id) 找不到直接报500错误,无法使用过滤器拦截错误,不要使用
clear() 清空该数据表,谨用!!!
find更多参数
this.userRepository.find({
select: ["firstName", "lastName"], 要的字段
relations: ["photos", "videos"], 关系查询
where: { 条件查询
firstName: "Timber",
lastName: "Saw"
},
where: [{ username: "li" }, { username: "joy" }], 多个条件or, 等于:where username = 'li' or username = 'joy'
order: { 排序
name: "ASC",
id: "DESC"
},
skip: 5, 偏移量
take: 10, 每页条数
cache: 60000 启用缓存:1分钟
});
find进阶选项
TypeORM 提供了许多内置运算符,可用于创建更复杂的查询
import { Not, Between, In } from "typeorm";
return await this.usersRepository.find({
username: Not('admin'),
});
将执行以下查询:
SELECT * FROM "users" WHERE "username" != 'admin'
return await this.usersRepository.find({
likes: Between(1, 10)
});
SELECT * FROM "users" WHERE "likes" BETWEEN 1 AND 10
return await this.usersRepository.find({
username: In(['admin', 'admin2']),
});
SELECT * FROM "users" WHERE "title" IN ('admin', 'admin2')
第三种:QueryBuilder查询生成器
使用链式操作
QueryBuilder增,删,改
增加
return await this.usersRepository
.createQueryBuilder()
.insert() 声明插入操作
.into(UsersEntity) 插入的实体
.values([ 插入的值,可插入多个
{ username: 'Timber', password: '123456' },
{ username: 'Timber2', password: '123456' },
])
.execute(); 执行
修改
return this.usersRepository
.createQueryBuilder()
.update(UsersEntity)
.set({ username: 'admin22' })
.where('id = :id', { id: 2 })
.execute();
删除
return this.usersRepository
.createQueryBuilder()
.delete()
.from(UsersEntity)
.where('id = :id', { id: 8 })
.execute();
处理异常:请求成功会返回一个对象, 如果raw.affectedRows != 0 就是成功
"raw": {
"fieldCount": 0,
"affectedRows": 2,
"insertId": 13,
"serverStatus": 2,
"warningCount": 0,
"message": "&Records: 2 Duplicates: 0 Warnings: 0",
"protocol41": true,
"changedRows": 0
}
查询
简单例子
export class UserService {
constructor(
@InjectRepository(UsersEntity)
private readonly usersRepository: Repository<UsersEntity>,
) { }
async findAll() {
return await this.usersRepository
.createQueryBuilder('user') 创建生成器,参数:别名
.where('user.id = :id', { id: id }) 条件
.innerJoinAndSelect('user.avatar', 'avatar') 关系查询
.addSelect('user.password') 添加显示字段
.getOne(); 获取一条数据
}
}
QueryBuilder查询生成器说明
查询单表
访问数据库的方式不同:
方式一:没有指定实体,需要使用from指定实体
return await getConnection()
.createQueryBuilder()
.select('user.username') ‘user’:全部字段,‘user.username’:只获取username
.from(UsersEntity, 'user') 参1:连接的实体, 参2:别名
.where('user.id = :id', { id: 1 })
.getOne();
方式二:指定实体:默认获取全部字段
return await getConnection()
.createQueryBuilder(UsersEntity, 'user') 指定实体
.where('user.id = :id', { id: 1 })
.getOne();
方式三: 已经在访问时指定了实体:默认获取全部字段
return await this.usersRepository
.createQueryBuilder('user') 别名
.where('user.id = :id', { id: 1 })
.getOne();
获取结果
.getSql(); 获取实际执行的sql语句,用于开发时检查问题
.getOne(); 获取一条数据(经过typeorm的字段处理)
.getMany(); 获取多条数据
.getRawOne(); 获取一条原数据(没有经过typeorm的字段处理)
.getRawMany(); 获取多条原数据
.stream(); 返回流数据
如:经过typeorm的字段处理,获取到的就是实体设计时的字段
{
"status": 200,
"message": "请求成功",
"data": {
"id": 1,
"username": "admin",
"gender": "male",
"age": 18,
"status": 1,
"createdAt": "2021-04-26T09:58:54.469Z",
"updatedAt": "2021-04-28T14:47:36.000Z",
"deletedAt": null
}
}
如:没有经过typeorm的字段处理,将数据库的字段原生不动的显示出来
{
"status": 200,
"message": "请求成功",
"data": {
"user_id": 1,
"user_username": "admin",
"user_gender": "male",
"user_age": 18,
"user_status": 1,
"user_created_at": "2021-04-26T09:58:54.469Z",
"user_updated_at": "2021-04-28T14:47:36.000Z",
"user_deleted_at": null
}
}
查询部分字段
.select(["user.id", "user.name"])
实际执行的sql语句:SELECT user.id, user.name FROM users user;
添加隐藏字段:实体中设置select为false时,是不显示字段,使用addSelect会将字段显示出来
.addSelect('user.password')
where条件
.where("user.name = :name", { name: "joy" })
等于
.where("user.name = :name")
.setParameter("name", "Timber")
实际执行的sql语句:SELECT * FROM users user WHERE user.name = 'joy'
多个条件
.where("user.firstName = :firstName", { firstName: "Timber" })
.andWhere("user.lastName = :lastName", { lastName: "Saw" });
实际执行的sql语句:SELECT * FROM users user WHERE user.firstName = 'Timber' AND user.lastName = 'Saw'
in
.where("user.name IN (:...names)", { names: [ "Timber", "Cristal", "Lina" ] })
实际执行的sql语句:SELECT * FROM users user WHERE user.name IN ('Timber', 'Cristal', 'Lina')
or
.where("user.firstName = :firstName", { firstName: "Timber" })
.orWhere("user.lastName = :lastName", { lastName: "Saw" });
实际执行的sql语句:SELECT * FROM users user WHERE user.firstName = 'Timber' OR user.lastName = 'Saw'
子句
const posts = await connection
.getRepository(Post)
.createQueryBuilder("post")
.where(qb => {
const subQuery = qb
.subQuery()
.select("user.name")
.from(User, "user")
.where("user.registered = :registered")
.getQuery();
return "post.title IN " + subQuery;
})
.setParameter("registered", true)
.getMany();
实际执行的sql语句:select * from post where post.title in (select name from user where registered = true)
having筛选
.having("user.firstName = :firstName", { firstName: "Timber" })
.andHaving("user.lastName = :lastName", { lastName: "Saw" });
实际执行的sql语句:SELECT ... FROM users user HAVING user.firstName = 'Timber' AND user.lastName = 'Saw'
orderBy排序
.orderBy("user.name", "DESC")
.addOrderBy("user.id", "asc");
等于
.orderBy({
"user.name": "ASC",
"user.id": "DESC"
});
实际执行的sql语句:SELECT * FROM users user order by user.name asc, user.id desc;
group分组
.groupBy("user.name")
.addGroupBy("user.id");
关系查询(多表)
1参:你要加载的关系,2参:可选,你为此表分配的别名,3参:可选,查询条件
左关联查询
.leftJoinAndSelect("user.profile", "profile")
右关联查询
.rightJoinAndSelect("user.profile", "profile")
内联查询
.innerJoinAndSelect("user.photos", "photo", "photo.isRemoved = :isRemoved", { isRemoved: false })
例子:
const result = await this.usersRepository
.createQueryBuilder('user')
.leftJoinAndSelect("user.photos", "photo")
.where("user.name = :name", { name: "joy" })
.andWhere("photo.isRemoved = :isRemoved", { isRemoved: false })
.getOne();
实际执行的sql语句:
SELECT user.*, photo.*
FROM users user
LEFT JOIN photos photo ON photo.user = user.id
WHERE user.name = 'joy' AND photo.isRemoved = FALSE;
const result = await this.usersRepository
.innerJoinAndSelect("user.photos", "photo", "photo.isRemoved = :isRemoved", { isRemoved: false })
.where("user.name = :name", { name: "Timber" })
.getOne();
实际执行的sql语句:
SELECT user.*, photo.* FROM users user
INNER JOIN photos photo ON photo.user = user.id AND photo.isRemoved = FALSE
WHERE user.name = 'Timber';
多个关联
const result = await this.usersRepository
.createQueryBuilder("user")
.leftJoinAndSelect("user.profile", "profile")
.leftJoinAndSelect("user.photos", "photo")
.leftJoinAndSelect("user.videos", "video")
.getOne();
学习更多