nest笔记十:typeorm使用经验小结
- nestjs系列笔记
- 之前的笔记:nest记笔五:使用TypeORM连接mysql,可以使用typeorm的基本功能。随着开发的深入,很多已经满足不了要求了。
- 写这小结的时候,typeorm已经更新0.3.9了, 与之前0.2.x相比,变化蛮大的。这里的内容也是针对0.3.7及以上的版本
- 因为我这里主要使用mysql 5.7,其它的数据库暂时没有涉及,所以这里的内容在mysql数据库,都是基本OK的。
- 我这里的很多代码,都会使用xmcommon这个库。只需要npm i xmcommon。为什么用这个库,因为它是我自己这些积累的工具库,我使用起来很顺手。也是我的一个开源项目。
1. 使用 typeorm-model-generator 生成对已有数据库的实体类
- typeorm可以手动改实体类,然后同步到数据库,这个功能我一直没有用。主要是担心,不小心应用到生产环境,造成数据丢失。
- 我现在的做法是使用工具改好数据库表后,再用typeorm-model-generator生成对应typeorm需要的实体类, 再将生成好的代码文件,替换现有的。
- typeorm-model-generator不能满足我的生成文件的要求,我fork了一个,然后发布了一个修改。参考这个文章基于typeorm的nestjs项目使用@zdhsoft/tmg将数据库生成数据模型
2. 一些特殊类型
FindOptionsWhere 是用于生成实体类where的选项,
- 使用FindOptionsWhere的几个用处:1、typescript的语法检查,2、代码提示,提示可以有哪些做条件的字段。
- 示例
@Entity('Account')
export class Account {
@Column('int', {
name: 'accountType',
comment: '账号类型',
})
public accountType: number;
@Column('varchar', {
name: 'accountId',
comment: '用户的登陆账号',
length: 200,
})
public accountId: string;
}
@Injectable()
export class AccountService {
constructor(@InjectRepository(Account) private accountRepo: Repository<Account>){
}
public async found(paramType: number, account: string) {
const where: FindOptionsWhere<Account> = {
accountType: paramType,
accountId: account,
};
const accountRec = await this.accountRepo.createQueryBuilder().where(where).getOne();
}
}
其它类似的
- DeepPartial 用于新增记录的时候,定义新的记录实例
- FindOptionsSelect 一般情况下,typeorm会返回该条记录的所有字段,其实这是没有必要的。通过它,来设置我们想要的返回字段。其实还有一个FindOptionsSelectByString,但是已经标记为:deprecated
- FindOptionsOrder 排序相关字段
3. 事物
- 在0.3.x的版本,事物已经与0.2.x事物大不同了
import { utils, XCommonRet, getLogger } from 'xmcommon';
class XTypeormUtils {
public static async transaction<T = unknown>(
paramDS: DataSource,
paramRunInTransaction: (paramMgr: EntityManager) => Promise<XCommonRet<T>>,
paramTransName?: string,
): Promise<XCommonRet<T>> {
let transTag = '';
if (!utils.isEmpty(paramTransName)) {
transTag = `[${paramTransName}]`;
}
const r = new XCommonRet<T>();
const queryRunner = paramDS.createQueryRunner();
log.info(`开始事物:${transTag}`);
await queryRunner.startTransaction();
try {
const result = await paramRunInTransaction(queryRunner.manager);
if (result.isNotOK) {
log.warn(`事物${transTag}执行失败:${JSON.stringify(result)}`);
await queryRunner.rollbackTransaction();
} else {
await queryRunner.commitTransaction();
}
r.assignFrom(result);
} catch (e) {
r.setError(-1, `事物异常:${String(e)}`);
log.warn(`事物${transTag}异常:${JSON.stringify(r)}`);
await queryRunner.rollbackTransaction();
}
await queryRunner.release();
return r;
}
}
@Injectable()
export class AccountService {
constructor(@InjectRepository(Account) private accountRepo: Repository<Account>, private dataSource: DataSource){
}
public async found(paramType: number, account: string) {
const where: FindOptionsWhere<Account> = {
accountType: paramType,
accountId: account,
};
const accountRec = await this.accountRepo.createQueryBuilder().where(where).getOne();
}
public async transSample() {
const result = XTypeormUtils.transaction<Account[]>(this.dataSource, (paramMgr: EntityManager) => {
const r = new XCommonRet<Account[]>();
do {
const list = await paramMgr.createQueryBuilder(Account).getMany();
r.setOk(list);
} while (false);
return r;
});
if (result.isOK) {
return result.data;
} else {
return [];
}
}
}
4. 一个简化处理的工具类
import { Like, In, DataSource, EntityManager, EntityTarget, Between, MoreThanOrEqual, LessThanOrEqual } from 'typeorm';
import { utils, XCommonRet, getLogger } from 'xmcommon';
const log = getLogger(__filename);
export class XTypeormUtils {
public static like(paramValue?: string) {
if (utils.isNull(paramValue)) {
return undefined;
} else {
return Like(`%${paramValue}%`);
}
}
public static in(paramList: unknown[]) {
return In(paramList);
}
public static scope<T>(paramForm?: T | null, paramTo?: T | null) {
let v = 0;
if (utils.isNotNull(paramForm)) {
v += 1;
}
if (utils.isNotNull(paramTo)) {
v += 2;
}
switch (v) {
case 1:
return MoreThanOrEqual<T>(paramForm as T);
case 2:
return LessThanOrEqual<T>(paramTo as T);
case 3:
return Between<T>(paramForm as T, paramTo as T);
default:
return undefined;
}
}
public static like_begin(paramValue?: string) {
if (utils.isNull(paramValue)) {
return undefined;
} else {
return Like(`${paramValue}%`);
}
}
public static like_end(paramValue?: string) {
if (utils.isNull(paramValue)) {
return undefined;
} else {
return Like(`%${paramValue}`);
}
}
public static cleanNull(paramWhere: any) {
const delKey: string[] = [];
for (const k in paramWhere) {
if (utils.isNull(paramWhere[k])) {
delKey.push(k);
}
}
for (const k of delKey) {
delete paramWhere[k];
}
return paramWhere;
}
public static bigInt(paramValue?: number): string | undefined {
if (utils.isNull(paramValue)) {
return undefined;
} else {
return String(paramValue);
}
}
public static builder<T>(
paramMgr: EntityManager,
paramEntity: EntityTarget<T>,
paramAliasName: string = 'a'
) {
return paramMgr.createQueryBuilder<T>(paramEntity, paramAliasName);
}
public static async transaction<T = unknown>(
paramDS: DataSource,
paramRunInTransaction: (paramMgr: EntityManager) => Promise<XCommonRet<T>>,
): Promise<XCommonRet<T>>;
public static async transaction<T = unknown>(
paramDS: DataSource,
paramRunInTransaction: (paramMgr: EntityManager) => Promise<XCommonRet<T>>,
paramTransName: string,
): Promise<XCommonRet<T>>;
public static async transaction<T = unknown>(
paramDS: DataSource,
paramRunInTransaction: (paramMgr: EntityManager) => Promise<XCommonRet<T>>,
paramTransName?: string,
): Promise<XCommonRet<T>> {
let transTag = '';
if (!utils.isEmpty(paramTransName)) {
transTag = `[${paramTransName}]`;
}
const r = new XCommonRet<T>();
const queryRunner = paramDS.createQueryRunner();
log.info(`开始事物:${transTag}`);
await queryRunner.startTransaction();
try {
const result = await paramRunInTransaction(queryRunner.manager);
if (result.isNotOK) {
log.warn(`事物${transTag}执行失败:${JSON.stringify(result)}`);
await queryRunner.rollbackTransaction();
} else {
await queryRunner.commitTransaction();
}
r.assignFrom(result);
} catch (e) {
r.setError(-1, `事物异常:${String(e)}`);
log.warn(`事物${transTag}异常:${JSON.stringify(r)}`);
await queryRunner.rollbackTransaction();
}
await queryRunner.release();
return r;
}
}
5. 其它
别名
- 很奇怪typeorm的SQL语句,都会重新命名,结查SQL事句很长,所以我这里都会使用别名, createQueryBuilder的参数可以传入别名, 一般情况下,我传入字终a
使用createQueryBuilder不能指定返回字段
- 使用createQueryBuilder查询返回的结果,好像都会返回查询记录的所有字段,很多时间,我仅需要几个字段就可以了。 这个时候可以使用FindOptionsSelect。设置完成后,这个builder一个方法,叫setFindOptions可以用
- 如下代码:
const fields: FindOptionsSelect<Account> = {
accountType: true
};
const list: Account[] = await this.repo.createQueryBuilder('a').setFindOptions({select: fields}).getMany();
- 还有一种情况是,你不管怎么设置,都会返回一些字段,仔细观查后,发现这些字段都是主键。
打印耗时
- typeorm初始化的时候,将logging设为true, 就可以打印出SQL语句了。但是每个SQL语句的耗时却没有,这个耗时的功能可以帮我们发现问题SQL,并优化。 其实,只要设置maxQueryExecutionTime的值就可以了,我这里将它设为1,就可以打印出来了。
最后,喜欢的话就收藏点个赞,希望对你使用有所帮助