总结一下typeorm常用的使用方法
entity
主键
typeorm中,每个entity必须有主键
-
普通主键
@Entity()
export class TunnelPart{
@PrimaryGeneratedColumn()
id: number;
}
-
uuid主键
@Entity()
export class BaseFileEntity {
@PrimaryGeneratedColumn("uuid")
id: string;
}
这样的主键,就是uuid
-
多列主键
@Entity()
export class StructureTeam {
@PrimaryColumn({ type: 'int' })
teamId: number;
@PrimaryColumn({ type: 'int' })
structureId: number;
}
这样,就形成了一个有多列形成的主键
ManyToOne与oneToMany
ManyToOne 与 oneToMany是最常用的关系,两者可同时使用,ManyToOne可以单独使用,基本操作,如:保存、查询、级联删除等,放到后边下边来写,这里只写关系的建立
只建立ManyToOne
@Entity()
export class TunnelSection{
@PrimaryGeneratedColumn()
id: number;
@Column('int')
length: number;
@ManyToOne(type=>TunnelMethod)
method: TunnelMethod;
}
在ManyToOne的创建中,只提用一个参数即可,这个参数是一个箭头函数,指向One所对应的表
关系在建立的时候,可以指明一些参数,比如OnDetete,当用CASCADE
时,可以用作级联删除。
@ManyToOne(type=>TunnelMethod, { onDelete: 'CASCADE' })
method: TunnelMethod;
同时建立ManyToOne 与 OneToMany
@Entity()
export class TunnelFixture{
@PrimaryGeneratedColumn()
id: number;
@Column('varchar', {length: 128})
name: string;
@OneToMany(type => TunnelProcedure, procedure => procedure.fixture)
procedures: TunnelProcedure[];
}
@Entity()
export class TunnelProcedure{
@PrimaryGeneratedColumn()
id: number;
@Column("varchar", {length: 128})
name: string;
@ManyToOne(type => TunnelFixture, fixture => fixture.procedures)
fixture: TunnelFixture;
}
在建立双向关系时,除了指明所在的entity,还要指明对方entity的属性
联合查询
relation查询
创建ManyToOne与OneToMany的关系以后,可以通过repository来查询
@Entity()
export class Photo {
@PrimaryGeneratedColumn()
id: number;
@Column()
url: string;
@ManyToOne(type => User, user => user.photos)
user: User;
}
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@OneToMany(type => Photo, photo => photo.user)
photos: Photo[];
}
const userRepository = connection.getRepository(User);
const users = await userRepository.find({ relations: ["photos"] });
const photoRepository = connection.getRepository(Photo);
const photos = await photoRepository.find({ relations: ["user"] });
也可以用createQueryBuilder形式
const users = await connection
.getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo")
.getMany();
const photos = await connection
.getRepository(Photo)
.createQueryBuilder("photo")
.leftJoinAndSelect("photo.user", "user")
.getMany();
一直觉得relations只在findOne中,可用,看了官网,发现都可以。
有些情况下,都是自己创建id字段来连接另外一个表使用,这种情况下,只能使用createQueryBuilder,查询出来的是地卡尔乘积的结果,有些情况下,需要经过去重处理。
这里要注意leftJoinAndSelect
与leftJoin
的区别。leftJoin不会查询出join表的字段
Raw查询
getRawMany()时,注意给列起别名,否则列名包括了表名。查询的数据不是entity时,采用raw方式查询。包括联表的自定义字段、SUM、COUNT等函数
public async getMaterialList(subprojId:number, type?:string):Promise<Material[]>{
let param = Object.create(null);
param.subprojId = subprojId;
let condition = "material.subprojId=:subprojId "
if(!isNullOrUndefined(type)){
param.type = type
condition += " and material.type=:type"
}
return await this.materialRepos.createQueryBuilder("material")
.select("material.id", "id")
.addSelect("material.type", "type")
.addSelect("material.name", "name")
.addSelect("material.unit", "unit")
.addSelect("material.metaQuantityId", "metaQuantityId")
.addSelect("material.subprojId", "subprojId")
.addSelect("material.createAt", "createAt")
.addSelect('materialPrice.price', "price")
.leftJoin("material.price", "materialPrice")
.where(condition, param)
.getRawMany();
}
注意join都是迪卡尔积,会有重复。
OneToOne关系比较适合,不会用重复
区间查询
LessThan、MoreThan、Between
这里在repository中查询时,使用了以上区间函数;
同样可以使用createQueryBuilder的当时完成相同的操作。
public async getMaterialPriceList(materialId:number, startDate?:string, endDate?:string){
let condition = Object.create(null);
condition.materialId = materialId;
if(startDate!=null && endDate==null){
condition.createAt = MoreThan(new Date(Date.parse(startDate)));
} else if(startDate==null && endDate!=null){
condition.createAt = LessThan(new Date(endDate));
} else if(startDate!=null && endDate!=null){
condition.createAt = Between(new Date(startDate), new Date(endDate));
}
return this.priceRepos.find({...condition});
}
用createQueryBuilder完成区间查询:
public async getMaterialPriceList(materialId:number, startDate?:string, endDate?:string){
return await this.priceRepos.createQueryBuilder("materialPice")
.where('materialPice.materialId = :materialId')
.andWhere('materialPice.createAt >= :startDate')
.andWhere('materialPice.createAt <= :endDate')
.setParameters({materialId:materialId, startDate:new Date(startDate),endDate:new Date(endDate)})
.getMany();
}
分页查询
同样是使用skip与take来完成
const users = await getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo")
.skip(5)
.take(10)
.getMany();
子查询
子查询是两个查询的嵌套,通常发生在where与from中
where中
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();
或者写成
const userQb = await connection.getRepository(User)
.createQueryBuilder("user")
.select("user.name")
.where("user.registered = :registered", { registered: true });
const posts = await connection.getRepository(Post)
.createQueryBuilder("post")
.where("post.title IN (" + userQb.getQuery() + ")")
.setParameters(userQb.getParameters())
.getMany();
from中
const posts = await connection
.createQueryBuilder()
.select("user.name", "name")
.from(subQuery => {
return subQuery
.select("user.name", "name")
.from(User, "user")
.where("user.registered = :registered", { registered: true });
}, "user")
.getRawMany();
from中的“user“是别名,或者写成
const userQb = await connection.getRepository(User)
.createQueryBuilder("user")
.select("user.name", "name")
.where("user.registered = :registered", { registered: true });
const posts = await connection
.createQueryBuilder()
.select("user.name", "name")
.from("(" + userQb.getQuery() + ")", "user")
.setParameters(userQb.getParameters())
.getRawMany();
全列查询
全表查询指的是对全部字段进行模糊查询,网上找资料看到一些方式,通过引入插件,再用函数的方式来实现,这样的实现在typeorm中很难实现。
事务
import {getManager} from "typeorm";
await getManager().transaction(async transactionalEntityManager => {
await transactionalEntityManager.save(users);
await transactionalEntityManager.save(photos);
});
事物可以使用装饰器方式来书写:
@Transaction()
save(@TransactionManager() manager: EntityManager, user: User) {
return manager.save(user);
}
@Transaction()
save(user: User, @TransactionRepository(User) userRepository: Repository<User>) {
return userRepository.save(user);
}
可以在事务中指明隔离级别
migration
- 需要注意其ormconfig.json配置文件的书写
- 其原理是通过检测migration中的文件,来执行文件,并且在数据库中创建一个migrations的数据表,用来存储执行过的文件。在执行的时候,会对比文件夹中的文件与数据库执行过的文件,并选择new的进行执行。
- 这种方式需要看一个revert如果执行。
- migration其实是一种命令模式。