DataMapper和ActiveRecord两种模式:
两种模式是如何组织增删改查代码的不同模式
Active Record:
在模型本身内定义所有查询方法,并使用模型方法保存、删除和加载对象。
所有 active-record 实体都必须扩展BaseEntity
类,BaseEntity
具有标准Repository
的大部分方法。
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from "typeorm";
@Entity()
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
static findByName(firstName: string, lastName: string) {
return this.createQueryBuilder("user")
.where("user.firstName = :firstName", { firstName })
.andWhere("user.lastName = :lastName", { lastName })
.getMany();
}
}
this.createQueryBuilder("user") 创建数据库查询对象
Data Mapper
实体只是定义了相应的属性,并且可能有一些简单的方法。
可以在单独类中定义所有查询方法
const userRepository = connection.getRepository(User);
通过getRepository()函数获取repository的对象
也可以通过继承Repository类:
import { EntityRepository, Repository } from "typeorm";
import { User } from "../entity/User";
@EntityRepository()
export class UserRepository extends Repository<User> {
findByName(firstName: string, lastName: string) {
return this.createQueryBuilder("user")
.where("user.firstName = :firstName", { firstName })
.andWhere("user.lastName = :lastName", { lastName })
.getMany();
}
}
Repository类提供了createQueryBuilder()函数
const userRepository = connection.getCustomRepository(UserRepository);
const timber = await userRepository.findByName("Timber", "Saw");
而使用的时候还是需要使用connection.getCustomRepository(UserRepository),不如 connection.getRepository(User); 简单。但是更加规范
总结:active record适合小项目;data mapper适合大项目
创建连接:
createConnection()
createConnections()
connectionManager:(适用动态创建连接的需求)
const connectionManager = getConnectionManager();
const connection = connectionManager.create() // 连接管理器
connection.connect()
注意,createConnection在新的版本里已经被舍弃,使用DataSource()
使用连接:
getConnection().manager : 对应createConnection()
const defaultConnection = getConnectionManager().get("default");-> 对应createConnections()
getManager()
getRepository(User)
建立连接选项:
支持不同的持久化存储,type字段可以指定,指定后需要查询对应存储终端对应的字段配置
支持主从模式:所有模式更新和写入操作都使用master
服务器执行。 find方法或select query builder执行的所有简单查询都使用随机的slave
实例。
但是也可以指定查询也是master
connection API:
connection.connect()
connection.close()
connection.getRepository(User): 获取给定实体的repository,后面用返回的对象进行增删改查
connection.query(`SELECT * FROM USERS`): 执行sql语句
connection.createQueryBuilder():创建查询器,查询器不绑定表
connection.createQueryRunner():管理和使用单个真实数据库连接的查询运行器。返回的对象可以connect(),也可以release()
const manager: EntityManager = connection.manager; 实体管理器
主要概念:
实体对应的repository
查询器:QueryBuilder
查询运行器: 管理连接
连接管理器:connectionManager.get("default");
实体:
@PrimaryGeneratedColumn("uuid")自增主键/自增uuid
@Column() 可传参
@Column("int")
@Column({ type: "int" })
@Column("varchar", { length: 200 })
@Column({ type: "int", length: 200 })
@Column({
type: "varchar",
length: 150,
unique: true,
})
其他配置字段:
type ,name, length, width, onUpdate, nullable, update, select, default, primary,(同@PrimaryColumn)
,unique , comment, precision, scale, zerofill(unsigned), charset, collation, enum, asExpression , generatedType,hstoreType
mysql
/mariadb
的列类型:
int
, tinyint
, smallint
, mediumint
, bigint
, float
, double
, dec
, decimal
, numeric
, date
, datetime
, timestamp
, time
, year
, char
, varchar
, nvarchar
, text
, tinytext
, mediumtext
, blob
, longtext
, tinyblob
, mediumblob
, longblob
, enum
, json
, binary
, geometry
, point
, linestring
, polygon
, multipoint
, multilinestring
, multipolygon
, geometrycollection
@PrimaryColumn()
@OneToMany()
@ManytoOne()
@ManyToMany()
@JoinColumn()
@OneToOne
实体关系:
一对一:
class User{
@OneToOne(()=>Profile)
@JoinColumn()
profile:Profile
}
@OneToOne
添加到profile
并将目标关系类型指定为Profile
@JoinColumn
,这是必选项并且只能在关系的一侧设置。 设置@JoinColumn
一方,表将包含一个"relation id"和目标实体表的外键。上面会在User表里生成profileId的外键
关联关系查询:
const userRepository = connection.getRepository(User);
const users = await userRepository.find({ relations: ["profile"] });
或者:
const users = await connection
.getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.profile", "profile")
.getMany();
单向关系和双向关系:
单向是仅在一侧与关系装饰器的关系
双向是与关系两侧的装饰者的关系。
一对多/多对一:
@OneToMany():
@OneToMany
关系中省略@JoinColumn
,除非你需要自定义关联列在数据库中的名称
@ManyToOne():
@OneToMany
必须搭配@ManyToOne
使用
多对多:
@ManyToMany()
@JoinTable()
JoinTable会生成中间的关联表,表的列都是主键,且是外键。表的名字是表1名+字段名+表2名
多对多也区分单向关系和双向关系
eager:
eager是什么:原意是热切的,渴求的。在这里可以理解关联关系会自动携带上,省去特殊指定是左关联查询。
eager只能在find()函数起作用,QueryBuilder则会被禁用。
内部默认使用leftJoinAndSelect
来加载
Eager 的关系只能用于关系的一方,在关系的两边使用eager:true
是不允许的。
@ManyToMany(type => Category, category => category.questions, {
eager: true
})
Lazy关系:
延迟加载
这是非标准技术,而且在 TypeORM 中被认为是实验性的。
想拥有延迟加载的关系,你必须使用 promises
@ManyToMany(type => Category, category => category.questions)
@JoinTable()
categories: Promise<Category[]>;
const question = await connection.getRepository(Question).findOne(1);
const categories = await question.categories;
加载lazy关系中的数据,使用await
实体关系加载
使用relations:
const users = await connection.getRepository(User).find({ relations: ["profile", "photos", "videos"] });
使用QueryBuilder和innerJoinAndSelect,leftJoinAndSelect等:
const user = await connection
.getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.profile", "profile")
.leftJoinAndSelect("user.photos", "photo")
.leftJoinAndSelect("user.videos", "video")
.getMany();
外键约束:
createForeignKeyConstraints
@ManyToOne(type => Person, {
createForeignKeyConstraints: false
})
person: Person;