TypeORM 框架
认识 TypeORM
TypeORM
是一个ORM
(opens new window
)框架,它可以运行在NodeJS
、Browser
、Cordova
、PhoneGap
、Ionic
、React Native
、Expo
和Electron
平台上,可以与TypeScript
和JavaScript
(ES5,ES6,ES7,ES8
)一起使用。 它的目标是始终支持最新的JavaScript
特性并提供额外的特性以帮助你开发任何使用数据库的(不管是只有几张表的小型应用还是拥有多数据库的大型企业应用)应用程序。- 不同于现有的所有其他
JavaScript ORM
框架,TypeORM
支持Active Record
和Data Mapper
模式,这意味着你可以以最高效的方式编写高质量的、松耦合的、可扩展的、可维护的应用程序。
TypeORM 的特性
- 支持
DataMapper
和ActiveRecord
(随你选择) - 实体和列
- 数据库特性列类型
- 实体管理
- 存储库和自定义存储库
- 清晰的对象关系模型
- 关联(关系)
- 贪婪和延迟关系
- 单向的,双向的和自引用的关系
- 支持多重继承模式
- 级联
- 索引
- 事务
- 迁移和自动迁移
- 连接池
- 主从复制
- 使用多个数据库类型
- 跨数据库和跨模式查询
- 优雅的语法,灵活而强大的
QueryBuilder
- 左联接和内联接
- 使用联查查询的适当分页
- 查询缓存
- 原始结果流
- 日志
- 监听者和订阅者(钩子)
- 支持闭包表模式
- 在模型或者分离的配置文件中声明模式
json / xml / yml / env
格式的连接配置- 支持
MySQL / MariaDB / Postgres / SQLite / Microsoft SQL Server / Oracle / sql.js
- 支持
MongoDB NoSQL
数据库 - 可在
NodeJS / 浏览器 / Ionic / Cordova / React Native / Expo / Electron
平台上使用 - 支持
TypeScript
和JavaScript
- 生成高性能、灵活、清晰和可维护的代码
- 遵循所有可能的最佳实践
- 命令行工具
安装(一)
- 通过
npm
安装:npm install typeorm --save
- 你还需要安装
reflect-metadata
:npm install reflect-metadata --save
- 并且需要在应用程序的全局位置导入(例如在
app.ts
中)import "reflect-metadata"
;
- 你可能还需要安装
node typings
(以此来使用Node
的智能提示):npm install @types/node –save
- 安装数据库驱动
MySQL
npm install mysql --save
(也可以安装mysql2
)
- 此外,请确保你使用的是
TypeScript
编译器版本2.3
或更高版本,并且已经在tsconfig.json
中启用了以下设置:"emitDecoratorMetadata": true
,"experimentalDecorators": true
,
- 你可能还需要在编译器选项的lib中启用es ,或者从
@types
安装es6-shim
安装(二)
-
开始使用
TypeORM
的最快方法是使用其CLI
命令(脚手架)生成启动项目。 只有在NodeJS
应用程序中使用TypeORM
时,此操作才有效。 -
首先全局安装
TypeORM
:-
npm install typeorm –g
-
-
然后转到要创建新项目的目录并运行命令:
-
typeorm init --name LearnTypeorm --database mysql
-
-
其中
name
是项目的名称,database
是您将使用的数据库。- 数据库可以是以下值之一:
mysql, mariadb, postgres, sqlite, mssql, oracle, mongodb, cordova, react-native, expo, nativescript
. - 你还可以在现有
node
项目上运行typeorm init
,但要注意,此操作可能会覆盖已有的某些文件。
- 数据库可以是以下值之一:
-
然后用编辑软件打开创建好的项目,会先出现一个
md
说明文件-
第一步:
npm i
-
第二步:删掉
src
文件夹下面的migration
文件 -
第三步:配置
tsconfig.json
文件(把"module":"ESNext"
换成"module": "CommonJS"
) -
第四步:配置
ormconfig.json
{ "type": "mysql", "host": "localhost", "port": 3306, "username": "root", "password": "123456", "database": "mall", "synchronize": true, "logging": false, "entities": [ "src/entity/**/*.ts" ], "cli": { "entitiesDir": "src/entity" } }
-
包的依赖(package.json
)
-
注意版本:下面的文件如果执行出现错误,可能是你的包版本太高导致的
{ "name": "learn_typeorm", "version": "0.0.1", "description": "Awesome project developed with TypeORM.", "devDependencies": { "ts-node": "3.3.0", "@types/node": "^8.0.29", "typescript": "3.3.3333" }, "dependencies": { "mysql": "^2.14.1", "reflect-metadata": "^0.1.10", "ts-node-dev": "^1.1.8", "typeorm": "0.2.37" }, "scripts": { "start": "ts-node-dev src/find进阶选项.ts" } }
数据库配置文件
- 大多数情况下,我们希望将连接选项存储在单独的配置文件中,因为此方式使管理变得更方便和容易。
TypeORM
支持多个配置源。你只需要在应用程序的根目录(package.json
附近)中创建一个ormconfig.[format]
文件存放连接配置,并在应用程序中调用createConnection()
,而不传递任何参数配置 - 支持的
ormconfig
文件格式有:.json
,.js
,.env
,.yml
和.xml
Typeorm 使用哪个配置文件
- 有时你可能希望使用不同格式的多个配置。 当调用
getConnectionOptions()
或尝试在没有连接选项的情况下使用createConnection()
时,Typeorm
将尝试按以下顺序加载配置: - 从另一个
ormconfig.[format]
文件,按此顺序:[js,ts,json,yml,yaml,xml]
。 - 注意,
Typeorm
将使用找到的第一个有效方法,而不会加载其他方法。 例如,如果在环境中找到配置,Typeorm
将不会加载ormconfig.[format]
文件。
ConnectionOptions(一)
- 连接选项是你传递给
createConnection
或在ormconfig
文件中定义的连接配置。不同的数据库有自己的特定连接选项。 - 常用的连接选项:
type
- 数据库类型。你必须指定要使用的数据库引擎。该值可以是"mysql....
"。此选项是必需的。name
- 连接名。 在使用getConnection(name: string)
或ConnectionManager.get(name: string)
时候需要用到。不同连接的连接名称不能相同,它们都必须是唯一的。如果没有给出连接名称,那么它将被设置为"default
"。entities
- 要加载并用于此连接的实体。接受要加载的实体类和目录路径。目录支持 glob 模式。示例:entities: [Post, Category, "entity/*.js", "modules/**/entity/*.js"]
。subscribers
- 要加载并用于此连接的订阅者。接受要加载的实体类和目录。migrations
- 要加载和用于此连接的迁移。接受要加载的迁移类和目录logging
- 指示是否启用日志记录。如果设置为true
,则将启用查询和错误日志记录。你还可以指定要启用的不同类型的日志记录,例如["query", "error", "schema"]
ConnectionOptions(二)
maxQueryExecutionTime
- 如果查询执行时间超过此给定的最大执行时间(以毫秒为单位),则 logger 将记录此查询。entityPrefix
- 给此数据库连接上的所有表(或集合)加的前缀。dropSchema
- 每次建立连接时删除架构。请注意此选项,不要在生产环境中使用它,否则将丢失所有生产数据。但是此选项在调试和开发期间非常有用。synchronize
- 指示是否在每次应用程序启动时自动创建数据库架构。 请注意此选项,不要在生产环境中使用它,否则将丢失所有生产数据。但是此选项在调试和开发期间非常有用。cache
- 启用实体结果缓存。cli.entitiesDir
-CLI
默认情况下创建实体的目录。cli.migrationsDir
-CLI
默认情况下创建迁移的目录。cli.subscribersDir
-CLI
默认情况下创建订阅者的目录。
连接Connection
-
从
ormconfig
文件加载所有连接选项import 'reflect-metadata' import {createConnection} from "typeorm"; import {User} from "./entity/User"; const getConnection = async ()=>{ // 创建数据库连接 const connection = await createConnection() console.log(connection) } getConnection()
使用 EntityManager
-
Repository
就像EntityManager
一样,但其操作仅限于具体实体。// User.ts import { Entity, PrimaryGeneratedColumn, Column } from "typeorm" @Entity() export class User { @PrimaryGeneratedColumn() id: number @Column() firstName: string @Column() lastName: string @Column() age: number // 构造函数 constructor(firstName: string, lastName: string, age: number,id?:number) { this.firstName = firstName; this.lastName = lastName; this.age = age; this.id = id } }
// index.ts import 'reflect-metadata' import {createConnection, getManager} from "typeorm"; import {User} from "./entity/User"; createConnection().then(()=>{ // 获取实体管理器 const entityManger = getManager() /** * 添加 */ const save = async ()=>{ const user:User = new User('张','三',20) // 通过实体管理器添加用户 const res = await entityManger.save<User>(user) console.log(res) } // save() /** * 查询所有 */ const queryAll = async ()=>{ // 查询全部 const [users]:User[] = await entityManger.find<User>(User) console.log(users) } // queryAll() /** * 修改 */ const edit = async () => { const user:User = new User('李','四',20,1) const res = await entityManger.save<User>(user) console.log(res) } // edit() /** * 删除 */ const del = async ()=>{ const {affected} = await entityManger.delete<User>(User,[3,4]) console.log(affected?'删除成功':'删除失败') } // del() })
使用 Repository
-
Repository
就像EntityManager
一样,但其操作仅限于具体实体。// User.ts import { Entity, PrimaryGeneratedColumn, Column } from "typeorm" @Entity() export class User { @PrimaryGeneratedColumn() id: number @Column() firstName: string @Column() lastName: string @Column() age: number // 构造函数 constructor(firstName: string, lastName: string, age: number,id?:number) { this.firstName = firstName; this.lastName = lastName; this.age = age; this.id = id } }
// index.ts import 'reflect-metadata' import {createConnection, getRepository} from "typeorm"; import {User} from "./entity/User"; /** * Repository */ createConnection().then((connection)=>{ // 获取存储库 const userRepository = getRepository(User) /** * 添加 */ const save = async ()=>{ const user:User = new User('刘','能',30) const res = await userRepository.save(user) console.log(res) } // save() /** * 查询所有 */ const queryAll = async ()=>{ const users:User[] = await userRepository.find() console.log(users) } // queryAll() /** * 修改 */ const edit = async () => { const user:User = new User('刘','能',11,7) const res = await userRepository.save(user) console.log(res) } // edit() /** * 删除 */ const del = async ()=>{ const {affected} = await userRepository.delete(5) console.log(affected?'删除成功':'删除失败') } del() })
自定义存储库
-
可以创建一个自定义存储库,其中应包含使用数据库的方法。 通常为单个实体创建自定义存储库,并包含其特定的查询
import "reflect-metadata"; // 使用装饰器需要导入 import {EntityRepository, Repository, getCustomRepository, createConnection} from 'typeorm'; import {User} from "./entity/User"; // 装饰器 自定义的用户存储库存储库 @EntityRepository(User) class UserRepository extends Repository<User>{ // 自定义的方法 findByName(firstName: string, lastName: string) { return this.findOne({firstName,lastName}) } } createConnection().then(()=>{ // 获取自定义的存储库的实例 参数就是自定义的存储库 const userRepository= getCustomRepository(UserRepository) // const save = async()=>{ // const user:User = new User('王','五',18) // const res = await userRepository.save(user) // console.log(res) // } // save() const queryByName = async ()=>{ const user = await userRepository.findByName('王','五') console.log(user) } queryByName() })
Find 选项-基础选项
-
所有存储库和管理器
find
方法都接受可用于查询所需数据的特殊选项,而无需使用QueryBuilder
// index.ts import "reflect-metadata"; import {createConnection, getRepository} from "typeorm"; import {User} from "./entity/User"; // 使用装饰器需要导入 createConnection().then(async ()=>{ const userRepository = getRepository(User) const res = await userRepository.find({ // 限制查询字段 select:['id','firstName','lastName','age'], // 查询条件 // where:{firstName:'张'} // or 使用数组 where:[{firstName:'张'},{firstName: '刘'}], // order 排序 // ASC 1 升序 // DESC -1 降序 order:{id:'DESC'}, // 分页 // skip 起始位置 skip:0, // take 数量 take:1, // 是否开启缓存 cache:true }) console.log(res) })
Find 选项-进阶选项
-
TypeORM
提供了许多内置运算符,可用于创建更复杂的查询import "reflect-metadata"; import { createConnection, getRepository, LessThan, LessThanOrEqual, Not, MoreThan, MoreThanOrEqual, Equal, Like, Between, In } from "typeorm"; import {User} from "./entity/User"; /** * find 选项 - 进阶 */ // 创建数据库连接 createConnection().then(async()=> { // 获取用户的存储库 const userRepository = getRepository(User) // Not:SELECT * FROM "user" WHERE "firstName" != '张'; // user 表中不含有 firstName 为 张 的 const users: User[] = await userRepository.find({ firstName:Not('张') }) console.log("不信张的列表",users) // LessThan:SELECT * FROM "user" WHERE "age" < 15 // user 表中 age 小于 15 的列表 const users1: User[] = await userRepository.find({ age:LessThan(15) }) console.log("年龄小于15的列表",users1) // LessThanOrEqual:SELECT * FROM "user" WHERE "age" <= 11 // user 表中 age 小于等于 11 的列表 const users2: User[] = await userRepository.find({ age:LessThanOrEqual (11) }) console.log("年龄小于等于11的列表",users2) // MoreThan:SELECT * FROM "user" WHERE "age" > 10 // user 表中 age 大于 10 的列表 const users3: User[] = await userRepository.find({ age:MoreThan (10) }) console.log("年龄大于10的列表",users3) // MoreThanOrEqual: SELECT * FROM "user" WHERE "age" > = 20 // user 表中 age 大于等于 20 的列表 const users4: User[] = await userRepository.find({ age:MoreThanOrEqual (20) }) console.log("年龄大于等于20的列表",users4) // Equal: SELECT * FROM "user" WHERE "age" = 11 // user 表中 age 大于等于 20 的列表 const users5: User[] = await userRepository.find({ age:Equal (11) }) console.log("年龄等于11的列表",users5) // Like:SELECT * FROM "user" WHERE "firstName" LIKE '%张%' // 模糊查询 user 表中 firstName 姓 张 的列表 const users6: User[] = await userRepository.find({ firstName:Like ('%张%') }) console.log("模糊查询姓张的列表",users6) // Between:SELECT * FROM "user" WHERE "age" BETWEEN 1 AND 10 // 查询 user 表中 age 在 1 到 11 之间的 包括1和11 const users7: User[] = await userRepository.find({ age:Between (1,11) }) console.log("查询年龄1到11之间的",users7) // In: SELECT * FROM "user" WHERE "age" IN (11,20) // 查询 user 表中 age 在 11和20 const users8: User[] = await userRepository.find({ age:In([11,20]) }) console.log(users8) })
实体
-
实体:是一个映射到数据库表的类。 你可以通过定义一个新类来创建一个实体,并用
@Entity()
来标记 -
实体列:由于数据库表由列组成,因此实体也必须由列组成。 标有
@Column
的每个实体类属性都将映射到数据库表列-
主列:每个实体必须至少有一个主列。有几种类型的主要列:
@PrimaryColumn()
创建一个主列,它可以获取任何类型的任何值。你也可以指定列类型。 如果未指定列类型,则将从属性类型自动推断。@PrimaryGeneratedColumn()
创建一个主列,该值将使用自动增量值自动生成。@PrimaryGeneratedColumn("uuid")
创建一个主列,该值将使用uuid
自动生成,(Uuid
是一个独特的字符串id
)
-
特殊列:
@CreateDateColumn
自动为实体插入日期。无需设置此列,该值将自动设置。@UpdateDateColumn
在每次调用实体管理器或存储库的save
时,自动更新实体日期。无需设置此列,该值将自动设置。
-
列类型
@Column(“int”)
或者@Column({ type: "int" })
- 如果要指定其他类型参数,可以通过列选项来执行:例子:
@Column("varchar", { length: 200 })
import {Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn} from "typeorm"; /** * 实体类 => 数据库表 * 添加了 @Entity 装饰器就会被 typeorm 去映射,判断表是否存在,不存在表才会被创建,存在不做处理 * 你要想创建类的表结构,就需要创建 @Entity() */ @Entity() export default class Person { // 主键:PrimaryColumn // @PrimaryGeneratedColumn('uuid') 传入uuid会自动生成uuid作为主键 // 自动增长的主键 @PrimaryGeneratedColumn() id:number // 自动添加创建时间 @CreateDateColumn() createAt:Date // 自动修改时间 @UpdateDateColumn() updateAt:Date // 列 name varchar(255) // 添加了 @Column() 会映射为表中的字段 // 可以指定列的类型 也可以限制长度 @Column('varchar',{length:20}) name:string // 可以指定列的类型 @Column('int') gander:number constructor(name: string) { this.name = name; } }
-
mysql的列类型(一)
-
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
-
enum
列类型import {Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn} from "typeorm"; export enum PersonRole { STUDENT = 'student', TEACHER = 'teacher' } /** * 实体类 => 数据库表 * 添加了 @Entity 装饰器就会被 typeorm 去映射,判断表是否存在,不存在表才会被创建,存在不做处理 * 你要想创建类的表结构,就需要创建 @Entity() */ @Entity() export default class Person { // 主键:PrimaryColumn // @PrimaryGeneratedColumn('uuid') 传入uuid会自动生成uuid作为主键 // 自动增长的主键 @PrimaryGeneratedColumn() id:number // 自动添加创建时间 @CreateDateColumn() createAt:Date // 自动修改时间 @UpdateDateColumn() updateAt:Date // 列 name varchar(255) // 添加了 @Column() 会映射为表中的字段 // 可以指定列的类型 也可以限制长度 @Column('varchar',{length:20}) name:string // 可以指定列的类型 @Column('int') gander:number @Column({ type:"enum", enum:PersonRole }) role:PersonRole constructor(name: string, gander: number, role: PersonRole) { this.name = name; this.gander = gander; this.role = role; } }
-
simple-array
的列类型: 它可以将原始数组值存储在单个字符串列中。 所有值都以逗号分隔。 返回时,也将作为数组方式返回import {Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn} from "typeorm"; export enum PersonRole { STUDENT = 'student', TEACHER = 'teacher' } /** * 实体类 => 数据库表 * 添加了 @Entity 装饰器就会被 typeorm 去映射,判断表是否存在,不存在表才会被创建,存在不做处理 * 你要想创建类的表结构,就需要创建 @Entity() */ @Entity() export default class Person { // 主键:PrimaryColumn // @PrimaryGeneratedColumn('uuid') 传入uuid会自动生成uuid作为主键 // 自动增长的主键 @PrimaryGeneratedColumn() id:number // 自动添加创建时间 @CreateDateColumn() createAt:Date // 自动修改时间 @UpdateDateColumn() updateAt:Date // 列 name varchar(255) // 添加了 @Column() 会映射为表中的字段 // 可以指定列的类型 也可以限制长度 @Column('varchar',{length:20}) name:string // 可以指定列的类型 @Column('int') gander:number @Column({ type:"enum", enum:PersonRole }) role:PersonRole @Column({ // 列是一个数组 type:'simple-array' }) nickName:Array<string> constructor(name: string, gander: number, role: PersonRole) { this.name = name; this.gander = gander; this.role = role; } }
mysql的列类型(二)
-
simple-json
列类型:可以存储任何可以通过JSON.stringify
存储在数据库中的值。 当你的数据库中没有json
类型而你又想存储和加载对象,该类型就很有用了import {Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn} from "typeorm"; export enum PersonRole { STUDENT = 'student', TEACHER = 'teacher' } /** * 实体类 => 数据库表 * 添加了 @Entity 装饰器就会被 typeorm 去映射,判断表是否存在,不存在表才会被创建,存在不做处理 * 你要想创建类的表结构,就需要创建 @Entity() */ @Entity() export default class Person { // 主键:PrimaryColumn // @PrimaryGeneratedColumn('uuid') 传入uuid会自动生成uuid作为主键 // 自动增长的主键 @PrimaryGeneratedColumn() id:number // 自动添加创建时间 @CreateDateColumn() createAt:Date // 自动修改时间 @UpdateDateColumn() updateAt:Date // 列 name varchar(255) // 添加了 @Column() 会映射为表中的字段 // 可以指定列的类型 也可以限制长度 @Column('varchar',{length:20}) name:string // 可以指定列的类型 @Column('int') gander:number @Column({ type:"enum", enum:PersonRole }) role:PersonRole @Column({ // 列是一个数组 type:'simple-array' }) nickName:Array<string> @Column('simple-json') perfile:{name:string,nickName:string} constructor(name: string, gander: number, role: PersonRole) { this.name = name; this.gander = gander; this.role = role; } }
列选项
-
列选项定义实体列的其他选项。 你可以在
@Column
上指定列选项:-
ColumnOptions
中可用选项列表: -
type: ColumnType
- 列类型。 -
name: string
- 数据库表中的列名。默认情况下,列名称是从属性的名称生成的。 你也可以通过指定自己的名称来更改它。 -
length: number
- 列类型的长度。 例如,如果要创建varchar(150)
类型,请指定列类型和长度选项。 -
nullable: boolean
- 在数据库中使列NULL
或NOT NULL
。 默认情况下,列是nullable:false
。 -
update: boolean
- 指示"save"
操作是否更新列值。如果为false
,则只能在第一次插入对象时编写该值。 默认值为"true"
。 -
select: boolean
- 定义在进行查询时是否默认隐藏此列。 设置为false
时,列数据不会显示标准查询。 默认情况下,列是select:true
-
default: string
- 添加数据库级列的DEFAULT
值。 -
primary: boolean
- 将列标记为主要列。 使用方式和@PrimaryColumn
相同。 -
unique: boolean
- 将列标记为唯一列(创建唯一约束)。 -
comment: string
- 数据库列备注,并非所有数据库类型都支持。 -
enum: string[]|AnyEnum
- 在enum
列类型中使用,以指定允许的枚举值列表。 你也可以指定数组或指定枚举类。import {Column, Entity, PrimaryGeneratedColumn} from "typeorm"; @Entity() export class Student { @PrimaryGeneratedColumn() id:number @Column({ // 自定义字段的类型 type:'varchar', // 自定义字段的名称 name:'sname', // 自定义字段的长度 length:20, // not null 允许为空 false 不为空 nullable:true, update:true, // false 查询时不显示 select:false, // 列的默认值 default:'123', // 唯一约束 unique:false, // 列的注释 comment:'姓名', }) name:string constructor(name: string) { this.name = name; } }
import 'reflect-metadata' import {createConnection, getRepository} from "typeorm"; import {Student} from "./entity/Student"; createConnection().then(async ()=>{ const studentRepository = getRepository(Student) // 添加 const stu = new Student('张三') await studentRepository.save(stu) // 查询 const student:Student[] = await studentRepository.find() console.log(student) })
-
嵌入实体类
- 通过使用
embedded columns
,可以减少应用程序中的重复 - 嵌入列是一个列,它接受具有自己列的类,并将这些列合并到当前实体的数据库表中。
关系
- 什么是关系
- 关系可以帮助你轻松地与相关实体合作。 有几种类型的关系
- 一对一 使用
@OneToOne
- 多对一 使用
@ManyToOne
- 一对多 使用
@OneToMany
- 多对多 使用
@ManyToMany
- 一对一 使用
- 关系选项
eager: boolean
- 如果设置为true
,则在此实体上使用find *
或QueryBuilder
时,将始终使用主实体加载关系cascade: boolean
- 如果设置为true
,则将插入相关对象并在数据库中更新。onDelete: "RESTRICT"|"CASCADE"|"SET NULL"
- 指定删除引用对象时外键的行为方式primary: boolean
- 指示此关系的列是否为主列。nullable: boolean
-指示此关系的列是否可为空。 默认情况下是可空。orphanedRowAction: "nullify" | "delete"
- 将子行从其父行中删除后,确定该子行是孤立的(默认值)还是删除的。
一对一的关系(一)
-
一对一是一种
A
只包含一个B
实例,而B
只包含一个A
实例的关系。 我们以Users
和Profile
实体为例。 -
用户只能拥有一个配置文件,并且一个配置文件仅由一个用户拥有。
// Profile.ts import {Column, Entity, PrimaryGeneratedColumn} from "typeorm"; @Entity() export class Profile { @PrimaryGeneratedColumn() id:number @Column() gander:number constructor(gander: number) { this.gander = gander; } }
// Users.ts import {Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn} from "typeorm"; import {Profile} from "./Profile"; @Entity() export class Users { @PrimaryGeneratedColumn() id:number @Column() name:string // OneToOne 一对一 指定关系 @OneToOne(()=>Profile) // 外键 @JoinColumn() profile:Profile constructor(name: string, profile: Profile) { this.name = name; this.profile = profile; } }
-
这里我们将
@OneToOne
添加到profile
并将目标关系类型指定为Profile
。 我们还添加了@JoinColumn
,这是必选项并且只能在关系的一侧设置。你设置@JoinColumn
的哪一方,哪一方的表将包含一个"relation id
"和目标实体表的外键。
一对一的关系(二)
-
同样,
@JoinColumn
必须仅设置在关系的一侧且必须在数据库表中具有外键的一侧。// 一对一.ts import {createConnection, getRepository} from "typeorm"; import {Profile} from "./entity/Profile"; import {Users} from "./entity/Users"; createConnection().then(()=>{ const usersRepository = getRepository(Users) const profileRepository = getRepository(Profile) const save = async ()=>{ // 创建 profile 对象 const profile = new Profile(20) // 添加 profile 信息到数据库 await profileRepository.save(profile) // 创建 users 对象 const users = new Users('张三',profile) // 添加 users 信息到数据库 const res = await usersRepository.save(users) console.log(res) } // save() // 查询 const queryOne = async () =>{ const users = await usersRepository.findOne({ relations:['profile'], where:{id:1} }) console.log(users) } queryOne() })
多对一/一对多的关系(一)
-
多对一/一对多
是指A
包含多个B
实例的关系,但B
只包含一个A
实例。 让我们以User
和Photo
实体为例。User
可以拥有多张photos
,但每张photo
仅由一位user
拥有// Photo.ts import {Column, Entity, ManyToOne, PrimaryGeneratedColumn} from "typeorm"; import {User} from "./User"; @Entity() export class Photo{ @PrimaryGeneratedColumn() id:number @Column() url:string @ManyToOne(()=>User,person =>person.photos) person:User constructor(url: string) { this.url = url; } }
// User.ts import {Column, Entity, OneToMany, PrimaryGeneratedColumn} from "typeorm"; import {Photo} from "./Photo"; @Entity() export class User { @PrimaryGeneratedColumn() id:number @OneToMany(()=>Photo,photo=> photo.person) photos:Photo[] constructor(photos: Photo[]) { this.photos = photos; } }
-
这里我们将
@OneToMany
添加到photos
属性中,并将目标关系类型指定为Photo
。 你可以在@ManyToOne / @OneToMany
关系中省略@JoinColumn
,除非你需要自定义关联列在数据库中的名称。@ManyToOne
可以单独使用,但@OneToMany
必须搭配@ManyToOne
使用。 如果你想使用@OneToMany
,则需要@ManyToOne
。 在你设置@ManyToOne
的地方,相关实体将有"关联 id"
和外键
。
多对一/一对多的关系(二)
-
需要查询带有
photos
的user
,必须在FindOptions
中指定关系import {createConnection, getRepository} from "typeorm"; import {User} from "./entity/User"; import {Photo} from "./entity/Photo"; createConnection().then(()=>{ const userRepository = getRepository(User) const photoRepository = getRepository(Photo) const save = async () => { const photo1 = new Photo('./img/01.jpg') const photo2 = new Photo('./img/02.jpg') await photoRepository.save([photo1,photo2]) const user = new User([photo1,photo2]) const res = await userRepository.save(user) console.log(res) } // save() const queryAll = async () =>{ const res = await userRepository.find({ relations:['photos'] }) console.log(res) } queryAll() })
多对多的关系(一)
-
多对多是一种
A
包含多个B
实例,而B
包含多个A
实例的关系。 我们以Question
和Category
实体为例。Question
可以有多个categories
, 每个category
可以有多个questions
// Category 类别 import {Column, Entity, PrimaryGeneratedColumn} from "typeorm"; @Entity() export class Category { @PrimaryGeneratedColumn() id:number @Column() name:string constructor(name: string) { this.name = name; } }
// Question 问题 import {Column, Entity, JoinTable, ManyToMany, PrimaryGeneratedColumn} from "typeorm"; import {Category} from "./Category"; @Entity() export class Question { @PrimaryGeneratedColumn() id:number @Column() title:string @Column() text:string @ManyToMany(()=>Category) @JoinTable() categories:Category[] constructor(title: string, text: string, categories: Category[]) { this.title = title; this.text = text; this.categories = categories; } }
-
@JoinTable()
是@ManyToMany
关系所必需的
多对多的关系(二)
-
要在
categories
里面加载question
,你必须在FindOptions
中指定关系import {createConnection, getRepository} from "typeorm"; import {Category} from "./entity/Category"; import {Question} from "./entity/Question"; createConnection().then(()=>{ const categoryRepository = getRepository(Category) const questionRepository = getRepository(Question) const save = async () =>{ const category1 = new Category('animals') const category2 = new Category('zoo ') await categoryRepository.save([category1,category2]) const question = new Question('标题','文本',[category1,category2]) const res = await questionRepository.save(question) console.log(res) } // save() const queryAll = async ()=>{ const res = await questionRepository.find({ relations:['categories'] }) console.log(res) } queryAll() })
使用 Query Builder
QueryBuilder
是TypeORM
最强大的功能之一 ,它允许你使用优雅便捷的语法构建SQL
查询,执行并获得自动转换的实体。
逆向生成数据库模型
- 首先全局安装:
npm install typeorm-model-generator
- 然后逆向生成代码:
typeorm-model-generator -h localhost -d mall -u root -x 123456 -e mysql -o entity
-h localhost -d 数据库名字 -p 端口 -u 用户名 -x 密码 -e 数据库类型
-o entities
表示输出到指定的文件夹