赞
踩
typeorm是一个数据库orm
框架,在nestjs
官网中有提到,可以充分发挥利用typescript
的特性,当然也支持js
其中涉及的概念包括
全局安装typeorm
typeorm cli的相关命令,可以快速测试初始化项目,生成实体等等
npm install typeorm -g
初始化项目
typeorm init --name MyProject
安装连接数据库驱动包,这里以msyql为2,安装msyql2
npm install mysql2
import "reflect-metadata" import { DataSource } from "typeorm" export const AppDataSource = new DataSource({ type: "mysql", host: "localhost", port: 3306, username: "root", password: "zhuang", database: "typeorm_test",// 连接的数据库 synchronize: true,// 开发过程使用,可以同步修改表结构,生产切忌使用。 logging: true,// 打印输出sql语句 connectorPackage: "mysql2",// 驱动包 entities: ["./**/entity/*.ts"],// 指定entity文件,也可以是实体数组,[User] migrations: [], subscribers: [] })
使用typeorm的装饰器定义实体
装饰器列表
快速成实体
typeorm entity:create -n User
typeorm entity:create src/entity/HelloWorld # 快速生成实体类
@Entity()
@Entity("user")
@Entity({
name: "users",// 表名
engine: "MyISAM", // 数据库引擎
database: 'example_dev',// 数据库
synchronize: false,// 是否同步更该表结构
orderBy: {// 查询时的默认排序
name: "ASC",
id: "DESC"
}
})
@ViewEntity({
expression: `
SELECT "post"."id" "id", "post"."name" AS "name", "category"."name" AS "categoryName"
FROM "post" "post"
LEFT JOIN "category" "category" ON "post"."categoryId" = "category"."id"
`
})
export class PostCategory {}
mysql
中,string - varchar(255), boolean - tinyint, number - int,Date - datetime(6)
@Column({primary:true})
具体的列装饰器都是固定了一些配置的装饰器.@Entity("users") export class User { @Column({ primary: true }) // 配置主键, @PrimaryGeneratedColumn("uuid"),配置uuid主键 id: number; @Column({ type: "varchar", length: 200, unique: true })// 配置数据库中具体类型,长度,唯一 firstName: string; @Column({ nullable: true })// 配置是否为空 lastName: string; @Column({ default: false })// 配置默认值,boolen默认类型被转换为 tinyint, false 对应 0,true 对应 1 isActive: boolean; @CreateDateColumn() // 创建时自动插入 createdDate: Date; @UpdateDateColumn() // 更新时自动更新 updatedDate: Date; }
指定表之间的关系,默认配置会生成物理外键,可以通过配置RelationOptions
中{createForeignKeyConstraints:false}
,不生成外键约束。一般该配置位于装饰器的最后一个参数,如下配置:
/** * 描述表之间关系的配置 */ export interface RelationOptions { /** * 配置不同表之间插入或更新时,相关的对象怎么配置,cascade:true 表示级联,如 user.roles, 当save的时候,把关联的roles实体也保存或者更新 * If set to true then it means that related object can be allowed to be inserted or updated in the database. * You can separately restrict cascades to insertion or updation using following syntax: * * cascade: ["insert", "update", "remove", "soft-remove", "recover"] // include or exclude one of them */ cascade?: boolean | ("insert" | "update" | "remove" | "soft-remove" | "recover")[]; /** * Indicates if relation column value can be nullable or not. */ nullable?: boolean; /** * 配置外键的onDelete * Database cascade action on delete. */ onDelete?: OnDeleteType; /** * 配置外键的onUpdate * Database cascade action on update. */ onUpdate?: OnUpdateType; /** * Indicate if foreign key constraints can be deferred. */ deferrable?: DeferrableType; /** * Indicates whether foreign key constraints will be created for join columns. * Can be used only for many-to-one and owner one-to-one relations. * Defaults to true. * 创建外键,默认是true */ createForeignKeyConstraints?: boolean; /** * Set this relation to be lazy. Note: lazy relations are promises. When you call them they return promise * which resolve relation result then. If your property's type is Promise then this relation is set to lazy automatically. */ lazy?: boolean; /** * Set this relation to be eager. * Eager relations are always loaded automatically when relation's owner entity is loaded using find* methods. * Only using QueryBuilder prevents loading eager relations. * Eager flag cannot be set from both sides of relation - you can eager load only one side of the relationship. */ eager?: boolean; /** * Indicates if persistence is enabled for the relation. * By default its enabled, but if you want to avoid any changes in the relation to be reflected in the database you can disable it. * If its disabled you can only change a relation from inverse side of a relation or using relation query builder functionality. * This is useful for performance optimization since its disabling avoid multiple extra queries during entity save. */ persistence?: boolean; /** * When a parent is saved (with cascading but) without a child row that still exists in database, this will control what shall happen to them. * delete will remove these rows from database. * nullify will remove the relation key. * disable will keep the relation intact. Removal of related item is only possible through its own repo. */ orphanedRowAction?: "nullify" | "delete" | "soft-delete" | "disable"; }
cascade
级联,createForeignKeyConstraints
创建外键,eager
查询时总是把关系类也查出来。其他的关系装饰器也大抵如此。cascade:true只能有一方配置
,如果两边都配置cascade:true
会报错,如果两边都配置,只要有一边不要配置cascade:[“remove”]就行。如果没有cascade:true
关系,那么保存实体时,关联的实体必须先保存到数据库中,否则报错。
报错信息如下
:
@Entity()
export class User {
// 指定关联实体,假如Profile实体通过@OnetoOne() 声明 user:User 字段,那么查询profile时也可以查询到user类
@OneToOne(type => Profile, profile => profile.user,{
createForeignKeyConstraints:false// 不创建外键
})
@JoinColumn() // 表示user表有profileId字段关联 profile 表,
profile: Profile;
}
@Entity() export class Photo { @PrimaryGeneratedColumn() id: number; @Column() url: string; // 第二个参数指定另一面关系的关联属性,这里可以省略 @JoinColumn(),默认生成字段 userId @ManyToOne(() => User, user => user.photos) user: User; } @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; @OneToMany(() => Photo, photo => photo.user) photos: Photo[]; }
@Entity()
export class Post {
@ManyToOne(type => Category)
@JoinColumn({
name: "cat_id",// 指定列名
referencedColumnName: "name" // 引用的Category的列名
})
category: Category;
}
@Entity() export class Post { @ManyToOne(type => Category) category: Category; @RelationId((post: Post) => post.category) // 需要指定目标关系 categoryId: number; } @Entity() export class Post { @ManyToMany(type => Category) categories: Category[]; @RelationId((post: Post) => post.categories) categoryIds: number[]; }
typeorm增删改查有三种途径:
Entity Manager
的api,Repository
的api, 其中还包括了 TreeRepository(树结构的仓库) 和 MongoRepsotirycreateQueryBuilder
的api构建sqlEntity Manager 和 Repository 的api基本相同,区别是 Manager 的api要指定实体类, 而 Repository 一般是通过 manager 指定 实体类获取到, 所以 Respository
可以少传一个类, 而 QueryBuilder
可以通过 数据源 DataSource
.createQueryBuilder
,或者 实体管理器 Entity Manager
或者 Repository
来获取.
这两者的api几乎相同,可以通过 manager.find(User,{where:{id:1}})
等价与 manager.getRepositry(User).find({where:{id:1}})
简单示例:
import { DataSource } from "typeorm" import { User } from "./entity/User" const myDataSource = new DataSource(/*...*/) // 这里 manager 指定了 User 类 const user = await myDataSource.manager.findOneBy(User, { id: 1, }) user.name = "Umed" await myDataSource.manager.save(user) import { User } from "./entity/User" // 这里通过 getRepository(User) 获取仓库 const userRepository = dataSource.getRepository(User) const user = await userRepository.findOneBy({ id: 1, }) user.name = "Umed" await userRepository.save(user)
创建实体
可以通过 new User() 这种来创建实体,也可以通过 create 方法
const user = manager.create(User, {
lastName: "xxx",
firstName: "xxx",
age: 20
});
const users = manager.create(User, [
{ firstName: "xxx", lastName: "xxx", age: 20 },
{ firstName: "yyy", lastName: "yyy", age: 22 }
]);
新增
save
保存给定的实体或实体数组,如果该实体已存在,那么就是更新,所有这个方法会先使用 select
语句后看是否存在决定是 insert
还是update
await manager.save(user)
await manager.save([user1,user2,user3])
insert
插入实体或实体数组
await manager.insert(User,{username:"xxx"})
await manager.insert(User,[{username:"xxx"},{username:"yyy"}])
删除
remove
删除给定的实体或实体数组
await manager.remove(user)
await manager.remove([user1,user2,user3])
delete
根据id,ids或者给定条件删除
await manager.delete(User,1)
await manager.delete(User,[1,2,3])
await manager.delete(User,{firstName:"xxx"})
修改
update
根据给定的id或条件修改实体,直接就是update语句,不像save方法要先select查询后再修改.
// 第二个参数是条件,第三个参数是修改的部分字段
await manager.update(User,{id:1},{firstName:"xxx"})
save
如果实体有id属性,并且在数据库中存在,那么save方法也可以修改数据
const user = await manager.findOneBy(User,{id:1})
await manager.save(User,user)
查询
查询方法最全的是 find 方法,其他的都是如 fineBy , findOne , findOneBy , findAndCount , findAndCountBy ,
find
查询与 FindOptions
相匹配的对象
await manager.find(User,{
select:["firstName","lastName","age"],
where:{
id:1,
firstName:Like("%jac%") // like
},
take:10, // 获取 10 条
skip:1 // 跳过 1 条
})
findBy
相当于是指定 where 条件的 find 方法
await manager.findBy(User,{id:1})
findOne
相当于 find 方法,然后 take 1 ,只获取第一条
await manager.findOne(User,{
where:{
firstName:"xxx"
}
})
findOneBy
相当于 findOne , 然后只传 where 条件
await manager.findOneBy(User,{lastName:'xxx'})
findAndCount
查找并统计返回结果,比 find 方法多了一个统计结果
await manager.findAndCount(User,{
where:{
firstName:"xxx"
}
})
findAndCountBy
类似 findAndCount , 多了一个 where 条件
await manager.findAndCountBy(User,{firstName:"xxx"})
计数
用于分页等
count
对匹配查询条件的进行计数,常用于分页
await manager.count(User,{
where:{
firstName:"xxx"
}
})
countBy
与count类似,固定了 where 参数
await manager.countBy(User,{firstName:"xxx"})
query
可以书写原本的 sql 语句
await manager.query("select * from user")
Entity Manager 和 Repository 的 find* 方法的选项,可以极大效率的帮助我们写常规的sql,复杂的sql再通过 QueryBuilder 去构建.
基本配置选项如下
userRepository.find({ select: { // 选中字段 firstName: true, lastName: true, }, relations: { // 关联关系是否一并查出来,子实体的关联关系也可以查出来,相当于 join 和 leftJoinAndSelect profile: true, photos: true, videos: true, videos: { videoAttributes: true, }, }, where: { firstName: "Timber", // 查询条件,等于 lastName: "Saw", profile: { userName: "tshaw", }, }, order: { name: "ASC", // 排序字段 id: "DESC", // 降序 , ASC 升序 }, skip: 5, // 跳过 5 条 take: 10, // 拿 10 条 cache: true, // 是否缓存 })
高级配置
typeorm内置了许多高级运算符,用于 FindWhereOptions 中,列举一些.
import { Not } from "typeorm"
const loadedPosts = await dataSource.getRepository(Post).findBy({
title: Not("About #1"),
})
import { Not } from "typeorm"
const loadedPosts = await dataSource.getRepository(Post).findBy({
title: Like("%out #%"),
})
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。