TypeORM - 快速指南
TypeORM - 简介
TypeORM 框架是一个对象关系映射 (ORM)框架。一般来说,对象部分指的是应用程序中的域/模型,关系部分指的是关系数据库管理系统 (例如 Oracle、MySQL、MS-SQL、PostgreSQL 等) 中表之间的关系,最后映射部分指的是连接模型和表的行为。
ORM 是一种将实体与数据库表映射的工具。ORM 通过自动化对象到表和表到对象的转换来简化开发过程。一旦您可以在一个地方编写数据模型,更新、维护和重用代码就会变得更加容易。
由于模型与应用程序的其余部分弱绑定,您可以更改它而无需与应用程序的其他部分有任何硬依赖,并且可以轻松地在应用程序内的任何地方使用它。TypeORM 非常灵活,将 DB 系统从应用程序中抽象出来,并允许我们从使用 OOPS 概念中受益。
概述
TypeORM 是一个在 node.js 中运行并用 TypeScript 编写的对象关系映射器库。TypeScript 是对 JavaScript 的改进,具有可选类型。TypeScript 是一种编译语言。它不会在运行时被解释。TypeScript 编译器获取 TypeScript 文件 (.ts) 并将其编译为 JavaScript 文件 (.js)。
TypeORM 支持多种数据库,如 MySQL、PostgreSQL、MariaDB、SQLite、MS SQL Server、Oracle、SAP Hana 和 WebSQL。 TypeORM 是一种易于使用的 ORM,用于构建连接到数据库的新应用程序。TypeORM 功能是 RDBMS 特定的概念。
我们可以快速创建新项目和微服务。它受到其他编程语言中类似工具的启发,如 Hibernate、Doctrine、Entity Framework 等。
TypeORM 的功能
TypeORM 具有以下功能 −
- 根据您的模型自动创建数据库表方案。
- 轻松在数据库中插入、更新和删除对象。
- 在表之间创建映射(一对一、一对多和多对多)。
- 提供简单的 CLI 命令。
TypeORM 的优势
TypeORM 是一个易于使用的 ORM 框架,编码简单。它具有以下优点 −
- 高质量且松散耦合的应用程序。
- 可扩展的应用程序。
- 轻松与其他模块集成。
- 完美适合从小型到企业应用程序的任何架构。
TypeORM - 安装
本章介绍如何在您的机器上安装 TypeORM。在安装之前,请确保已安装 npm。要确认您已安装 npm,您可以在终端中运行以下命令。
npm -v
显示版本。如果未安装,请下载最新版本并安装在您的机器上。
安装 TypeORM
让我们使用 npm 模块在本地安装 TypeORM −
npm install typeorm --save
执行上述命令后,您将收到如下所示的响应 −
+ typeorm@0.2.24 + added 1 package and audited 1236 packages in 4.729s
或者,要全局安装 TypeORM,请使用以下命令 −
npm install typeorm -g
之后,使用 npm 安装可选包 reflect-metadata −
npm install reflect-metadata --save
您可以看到以下响应 −
+ reflect-metadata@0.1.13 added 1 package and audited 1236 packages in 2.426s
现在,使用以下命令安装另一个可选包。node typings −
npm install @types/node --save
您可以看到以下响应 −
+ @types/node@12.12.31 added 1 package and audited 1236 packages in 2.167s
安装数据库驱动程序
在本节中,让我们为我们的数据库安装必要的节点包。
要安装 MySQL 或 MariaDB 包,请使用以下命令 −
npm install mysql --save
您可以看到以下响应 −
+ mysql@2.18.1 added 1 package and audited 1236 packages in 4.36s
要安装 PostgreSQL 包,请使用以下命令 −
npm install pg --save
您可以看到以下响应 −
+ pg@7.18.2 added 1 package and audited 1236 packages in 2.863s
要安装 SQLite 包,请使用以下命令 −
npm install sqlite3 --save
您可以看到以下响应 −
+ sqlite3@4.1.1 added 48 packages from 45 contributors and audited 1396 packages in 7.31s
要安装 Microsoft SQL Server 包,请使用以下命令 −
npm install mssql --save
您的屏幕看起来与此类似,
+ mssql@6.2.0 added 1 package and audited 1655 packages in 2.378s
要安装 sql.js 包,请使用以下命令 −
npm install sql.js --save
您可以看到以下响应 −
+ sql.js@1.2.1 added 1 package and audited 1655 packages in 6.462s
要安装 Oracle 服务器 包,请使用以下命令 −
npm install oracledb --save
您可以看到以下响应 −
+ oracledb@4.2.0 added 1 package and audited 1655 packages in 2.265s
要安装 mongodb 包,请使用以下命令 −
npm install mongodb --save
您可以看到以下响应 −
+ mongodb@3.5.5 added 1 package and audited 1655 packages in 3.716s
TypeORM - 创建一个简单的项目
本章介绍如何创建简单的 TypeORM 应用程序。让我们创建一个名为"TypeORM"的新目录并进入该目录。
cd /path/to/TypeORM/
语法
使用以下命令创建新项目 −
typeorm init --name <project-name> --database <database-name>
示例
typeorm init --name FirstProject --database mysql
这里,
FirstProject 是您的项目名称,sqlite3 是数据库名称。执行上述命令后,你会看到以下响应,
在 /path/to/TypeORM/FirstProject 目录中创建的项目
现在,进入我们的项目目录并使用 npm 模块安装项目依赖项,
$ cd FirstProject $ npm install
项目结构
让我们了解我们新创建的项目 FirstProject 的项目结构。
FirstProject ├──> src │ ├──> entity │ │ └──> User.ts │ ├──> migration │ └──> index.ts ├──> node_modules ├──> ormconfig.json ├──> package.json ├──> package-lock.json └──> tsconfig.json
此处,
- src − 包含您的应用程序的源代码(采用 TypeScript 语言)。它有一个文件 index.ts 和两个子目录 entity 和 migration。
- index.ts − 应用程序的入口点。
- entity − 包含数据库模型。
- migration − 包含数据库迁移代码。
- node_modules − 本地保存的 npm 模块。
- ormconfig.json − 应用程序的主配置文件。它包含数据库配置详细信息和实体配置。
- package.json −包含节点模块依赖项。
- package-lock.json − 自动生成的文件并与 package.json 相关。
- tsconfig.json − 包含 TypeScript 特定的编译器选项。
ormconfig.json 文件
让我们检查应用程序可用的配置选项。打开 ormconfig.json 文件,它看起来类似于此 −
{ "type": "mysql", "host": "localhost", "port": 3306, "username": "test", "password": "test", "database": "test", "synchronize": true, "logging": false, "entities": [ "src/entity/**/*.ts" ], "migrations": [ "src/migration/**/*.ts" ], "subscribers": [ "src/subscriber/**/*.ts" ], "cli": { "entitiesDir":"src/entity", "migrationsDir":"src/migration", "subscribersDir":"src/subscriber } }
此处,
type, host, username, password, database 和 port 选项与数据库设置相关。mysql 可以使用以下配置进行配置 −
{ "type": "mysql", "host": "localhost", "port": 3306, "username": "db_username", "password": "db_password", "database": "db_name" }
- entities − 指的是您的实体类的位置。
- migrations − 指的是您的迁移类的位置。
- subscribers − 指的是您的订阅者类的位置。
- cli − 指的是 TypeORM CLI 用于自动生成代码的选项
启动 MySql 服务器
在启动应用程序之前,请启动您的 MySQL 服务器或您使用的任何数据库服务器,并确保它正常运行。
运行应用程序
配置完所有内容后,我们可以使用以下命令执行应用程序 −
npm start
您可以看到以下响应 −
> FirstProject@0.0.1 start /Users/../../TypeORM/FirstProject > ts-node src/index.ts Inserting a new user into the database... Saved a new user with id: 1 Loading users from the database... Loaded users: [ User { id: 1, firstName: 'Timber', lastName: 'Saw', age: 25 }] Here you can setup and run express/koa/any other framework.
应用程序将新用户插入数据库,然后从数据库反向加载,最后在控制台中显示加载的用户。我们已成功创建一个新的 TypeORM 应用程序,对其进行配置并运行该应用程序。
我们将在接下来的章节中详细讨论如何执行数据。
TypeORM - Connection API
要与数据库交互,我们需要一个数据库连接对象。我们需要在执行数据库操作之前创建一个连接对象,并且在数据库操作完成后必须终止它。让我们在本节中了解 TypeORM 提供的连接 API。
创建新连接
在创建新连接之前,我们需要在 ormconfig.json 配置文件中配置数据库连接详细信息。示例连接详细信息如下所示 −
ormconfig.json
{ name: "firstconnection", type: "mysql", host: "localhost", port: 3306, username: "root", password: "root", database: "firstDB" }
这里,
- name − 数据库连接的名称。
- type − 数据库类型。
- host − 数据库服务器的主机名。
- port − 数据库服务器端口。
- username − 有权访问数据库的帐户名称。
- password − 上述帐户的密码。
- database − 要连接的数据库的名称。
createConnection
CreateConnection 方法由 TypeORM 提供,用于创建新连接。其定义如下,
import { createConnection, Connection } from "typeorm"; const connection = await createConnection({ });
此处,createConnection 将使用 ormconfig.json 文件中指定的配置详细信息。
或者,您可以将连接 URL 定义为 createConnection 方法的参数,如下所示 −
const connection = createConnection({ type: 'mysql', url: 'localhost:8888/firstDB' })
此处,
createConnection 返回一个对象,可用于打开/关闭与数据库的连接。
多个连接
TypeORM 还提供了创建多个数据库连接的选项。首先,配置文件 ormconfig.json 可用于指定多个数据库连接的详细信息。让我们在 ormconfig.json 中配置多个数据库,如下所示,
ormconfig.json
{ name: "firstconnection", type: "mysql", host: "localhost", port: 3306, username: "root", password: "root", database: "firstDB" }, { name: "secondconnection", type: "mysql", host: "localhost", port: 3306, username: "root", password: "root", database: "secondDB" }, { name: "thirdconnection", type: "mysql", host: "localhost", port: 3306, username: "root", password: "root", database: "thirdDB" }
现在,我们可以使用 createConnection 方法提供的参数来指定连接的名称,以创建连接对象,如下所示 −
const firstconnection: Connection = await createConnection("firstconnection");
这里,
createConnection 将使用 ormconfig.json 文件中指定的 firstconnection 的配置详细信息来创建连接对象。
TypeORM 还提供了另一个 API,createConnections 可以一次创建多个连接,然后在需要时使用它,如下所示 −
import { createConnections, Connection } from "typeorm"; const connections: Connection[] = await createConnections([ ]);
这里,
connections 将所有连接对象保存为一个数组。
ConnectionManager
TypeORM 还提供了另一个 API connectionManager 来创建连接。它定义如下−
import {getConnectionManager, ConnectionManager, Connection} from "typeorm"; const connectionManager = getConnectionManager(); const connection = connectionManager.create({ }); await connection.connect();
TypeORM 更喜欢使用 createConnection 而不是 ConnectionManager 来创建连接对象。
TypeORM - 实体
实体是字段和相关数据库操作的集合。它用于将数据库表及其字段与实体及其属性进行映射。本章详细介绍了 TypeORM 实体。
简介
让我们在代码中创建一个简单的实体类。移至项目根位置,进入 src 文件夹并移至实体文件夹。现在,创建一个 TypeScript 文件 Student.ts 并输入以下代码 −
Student.ts
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm"; @Entity() export class Student { @PrimaryGeneratedColumn() id: number; @Column() Name: string; @Column() age: number; }
这里,
- Entity()装饰器类用于表示Student类是一个实体。
- PrimaryGeneratedColumn()装饰器类用于表示id列是Student实体的主键列。
- Column()装饰器类用于表示Student实体的其他列,例如Name和Age。
现在,实体类Student已创建。TypeORM将自动在我们的数据库中生成与Student实体相对应的表,并将其命名为student。现在,转到 src/index.ts 文件并添加以下代码 −
index.ts
import "reflect-metadata"; import {createConnection} from "typeorm"; import {Student} from "./entity/Student"; //导入Student实体 createConnection().then(async connection => { console.log("Inserting a new record into the student database..."); //创建学生对象 const stud = new Student(); //在此分配学生姓名和年龄 stud.Name = "student1"; stud.age = 12; //save student object in connection await connection.manager.save(stud); console.log("Saved a new user with id: " + stud.id); console.log("Loading users from the database..."); //Display student saved records const students = await connection.manager.find(Student); console.log("Loaded users: ", students); console.log("Here you can setup and run express/koa/any other framework."); }).catch(error => console.log(error));
此处,
- 第 1 - 3 行导入相关类 createConnection 和 Student
- 第 5 行使用 createConnection 创建与数据库的新连接,如果建立了连接,则执行 then 块内的代码。
- 第 10 行创建新的 Student 对象 stud。
- 第 13-14 行设置我们新创建的 stud 对象的属性。
- 第 17 行使用 connection.manager 对象中提供的 save 方法将实体保存到数据库。
- 第 23 行使用 connection.manager 对象中提供的 find 方法从数据库获取学生详细信息。
启动 Mysql 服务器并运行应用程序
我们已经在 index.ts 中创建了 Student 实体并创建了连接。让我们启动 MySql 服务器和您的应用程序。
npm start
这将在您的屏幕上返回以下输出 −
输出
打开 mysql 服务器,然后将 student 表添加到您的数据库中。
列
如前所述,Entity 实际上是属性的集合。因为实体对象引用数据库表。其属性/成员变量引用相应数据库表的字段/列。 TypeORM 通过 Column 类支持所有类型的数据库字段。本章我们将了解 TypeORM 支持的不同类型的列。
@Column() 装饰器类用于表示实体中的列及其类型。
例如,学生实体的 age 属性和 age 属性的类型可以定义如下 −
@Column("int") age: integer; // OR @Column({ type: "int" }) age: integer;
这里,
- age是实体的属性。换句话说,age是数据库中student表的一个字段/列。
- int表示数据库中age列的类型。
TypeORM支持流行数据库引擎中几乎所有可用的类型。实际上,TypeORM为每个数据库引擎启用不同的类型集。我们可以使用我们的数据库引擎支持的任何数据库类型,而不会出现任何问题。
例如,TypeORM为postgresql数据库引擎支持的类型如下 −
int, int2, int4, int8, smallint, integer, bigint, decimal, numeric, real, float, float4, float8, double precision, money, character varying, varchar, character, char, text, citext, hstore, bytea, bit, varbit, bit varying, timetz, timestamptz, timestamp, timestamp without time zone, timestamp with time zone, date, time, time without time zone, time with time zone, interval, bool, boolean, enum, point, line, lseg, box, path, polygon, circle, cidr, inet, macaddr, tsvector, tsquery, uuid, xml, json, jsonb, int4range, int8range, numrange, tsrange, tstzrange, daterange, geometry, geography, cube
同样,TypeORM 支持一组不同的 MySQL 数据类型。
列选项
TypeORM 提供了一组除类型之外的选项来描述列。例如,length 选项指的是数据库字段的长度,可以按以下方式指定 −
@Column("varchar", { length: 100 })
一些最常见的列选项如下 −
- name − 数据库字段/列的名称。
- length − 数据库字段/列的长度。
- nullable − 指定数据库字段/列是否允许为空。
- default −数据库字段/列的默认值。
- primary − 指定数据库字段/列是否为表的主键。
- unique − 指定数据库字段/列是否唯一
- *precision** − 数据库字段/列的精度
- scale − 数据库字段/列的小数位数
- comment − 数据库字段/列的注释或描述
@Generated 装饰器
TypeORM 提供了额外的装饰器 @Generated 来自动生成列值。例如,通用唯一标识符 (UUID) 在数据库中非常常见,用于在列中存储唯一值。生成 UUID 的示例代码如下 −
@Entity() export class Student { @PrimaryColumn() id: number; @Column() @Generated("uuid") uuid: string; }
此处,
uuid 自动生成并存储在数据库中。
主列
数据库中的任何实体至少有一个主列字段是必需的。它分为不同类型的装饰器。我们将逐一讨论。
@PrimaryColumn()
@PrimaryColumn() 装饰器用于为任何类型的数据创建主列。下面显示了简单示例,
import {Entity, PrimaryColumn} from "typeorm"; @Entity() export class Student { @PrimaryColumn() id: number; }
这里,
id是一个整数,它不接受重复的值,但我们需要分配值。
如果情况需要,我们也可以为一个或多个字段分配主列。
示例
import {Entity, PrimaryColumn} from "typeorm"; @Entity() export class Student { @PrimaryColumn() id: number; @PrimaryColumn() email: string; @PrimaryColumn() phone: number; }
@PrimaryGeneratedColumn()
@PrimaryGeneratedColumn() 字段用于指定主列以及自动生成数据库中的列值。如下所示 −
import {Entity, PrimaryGeneratedColumn} from "typeorm"; @Entity() export class Student { @PrimaryGeneratedColumn() id: number; }
这里,
您不必指定 id 值;它将由数据库表中的 TypeORM 自动生成。
@PrimaryGeneratedColumn("uuid")
@PrimaryGeneratedColumn 还接受一个参数来指定生成器的类型。主要用途之一是根据 UUID 生成唯一 id。
import {Entity, PrimaryGeneratedColumn} from "typeorm"; @Entity() export class Student { @PrimaryGeneratedColumn("uuid") id: string; }
简单数组列类型
高级关系数据库支持数组数据类型。为了支持数组数据类型,TypeORM 提供了一种特殊的列类型"简单数组"来存储原始数组值。使用它的示例代码如下−
@Entity() export class Student { @PrimaryGeneratedColumn() id: number; @Column("simple-array") names: string[]; }
simple-json 列类型
许多现代数据库引擎都支持 JSON 数据库。为了使用 JSON 数据类型,TypeORM 提供了一种特殊类型 single-json。使用它的示例代码如下 −
@Entity() export class Student { @PrimaryGeneratedColumn() id: number; @Column("simple-json") info: { firstName: string, middleName: string, lastName: string }; }
该值可以在index.ts中定义为,
index.ts
const stud = new Student(); stud.info = { firstName: "John", middleName: "peter", lastName: "Michael" };
特殊列
TypeORM支持以下特殊列
- @CreateDateColumn − 这是一个用于自动设置实体插入日期的特殊列。
- @UpdateDateColumn − 它用于自动设置实体的更新时间。
- @VersionColumn −自动为实体设置版本号。
实体继承
实体继承用于减少实体的重复。考虑以下实体 −
Result.ts
@Entity() export class Result { @PrimaryGeneratedColumn() id: number; @Column() title: string; @Column() description: string; @Column() eligible: string }
Grade.ts
grade.ts 的代码如下 −
@Entity() export class Grade { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column() title: string; @Column() description: string; @Column() grading : string; }
此处,
上述两个实体具有列 id、title 和 description。使用实体继承,我们创建一个基类 Details,并按如下所示组合上述两个实体。
Details.ts
export abstract class Details { @PrimaryGeneratedColumn() id: number; @Column() title: string; @Column() description: string; } @Entity() export class Result extends Details{ @Column() eligible: string } @Entity() export class Grade extends Details{ @Column() name : string; @Column() grading : string; }
现在启动您的服务器,您可以看到以下响应,
现在打开您的 mysql 服务器并移动到您的数据库,您可以看到以下表格,
成绩表
结果表
TypeORM - 关系
关系用于引用数据库中表之间的关系。通常,当两个表中的一个具有引用另一个表的主键的外键时,两个表之间存在关系。此功能使关系数据库更强大,更有效地存储信息。
TypeORM 允许实体相互关联,随后与数据库表关联。通常,关系可以分为四大类。它们如下,
一对一 − 给定实体的一个对象仅与目标实体的一个对象相关,反之亦然。例如,一个国家只有一个首都,同样,一个城市也只有一个国家的首都。
多对一 − 给定实体的多个对象与目标实体的一个对象相关。例如,城市只属于一个国家,但国家可以有多个城市。
一对多 − 与多对一相同,只是关系是相反的。
多对多 − 给定实体的多个对象与目标实体的多个对象相关。例如,一篇文章可能被标记为多个主题,如编程语言、金融等,同时特定标签也可能有多篇文章。
TypeORM 还提供了增强实体关系的选项。它们如下 −
- eager − 源实体对象也会加载目标实体对象。
- cascade − 在源实体对象插入或更新时,目标实体对象也会被插入或更新。
- onDelete −删除源实体对象时,目标实体对象也会被删除。
- primary − 用于指定关系列是否为主键。
- nullable − 用于指定关系列是否可空。
让我们详细了解不同类型的关系映射。
一对一
正如我们之前所了解的,它是指一个表字段的实例包含另一个表字段的实例,反之亦然。让我们创建一个 Details 表 −
Details.ts
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm"; @Entity() export class Details { @PrimaryGeneratedColumn() id: number; @Column() gender: string; @Column() country: string; }
让我们创建另一个实体 Customer,如下所示 −
Customer.ts
import {Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn} from "typeorm"; import {Details} from "./Details"; @Entity() export class Customer { @PrimaryGeneratedColumn() id: number; @Column() name: string; @OneToOne(type => Details) @JoinColumn() details: Details; }
这里,
我们已将 OneToOne 映射到 Details 表。@JoinColumn() 包含一个"关系 ID"和指向 Customer 表的外键。我们可以按如下方式将关系保存在 index.ts 中 −
const details = new Details(); details.gender = "female"; details.country = "india" await connection.manager.save(details); const customer = new Customer(); customer.name = 'customer1'; customer.details = Details; await connection.manager.save(Customer);
一对多和多对一
正如我们前面所了解的,它由第一个表字段的实例引用,其中包含第二个表字段的多个实例,称为一对多映射,并且第一个表的多个实例仅包含第二个表的一个实例,称为多对一映射。
考虑学生和项目实体的示例,其中学生可以从事多个项目,但每个项目仅由一名学生负责。
让我们创建一个项目实体,如下所示 −
项目
import {Entity, PrimaryGeneratedColumn, Column, ManyToOne} from "typeorm"; import {Student} from "./Student"; @Entity() export class Project { @PrimaryGeneratedColumn() id: number; @Column() projects: string; @ManyToOne(type => Student, student => student.projects) student: Student; }
现在,我们创建 Student 实体,如下所示 −
import {Entity, PrimaryGeneratedColumn, Column, OneToMany} from "typeorm"; import {Project} from "./Project"; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; @OneToMany(type => Project, project => project.student) projects: Project[]; }
此处,
@OneToMany 属性映射了 Project,而 @ManyToOne 属性映射了 Student。但是,如果没有 @ManyToOne,@OneToMany 就无法存在,并且 @ManyToOne 属性包含"关系 ID"和外键。
我们可以在 index.ts 中保存连接,如下所示 −
const proj1 = new Project(); proj1.projects = "database management"; await connection.manager.save(proj1); const proj2 = new Project(); proj2.projects = "web application"; await connection.manager.save(proj2); const stud = new Student(); stud.name = "Student1"; stud.projects = [proj1, proj2]; await connection.manager.save(stud);
多对多
正如我们前面所学的,它是指一个表中的多个记录与另一个表中的多个记录相关。考虑一个例子,大学生可以同时注册多个课程,这意味着学生每学期可能有四到五门课程,一个班级可以有很多学生。
我们可以简单地得出结论,一个学生有很多课程,一个班级有很多学生。让我们为班级创建一个实体,如下所示 −
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm"; @Entity() export class Classes { @PrimaryGeneratedColumn() id: number; @Column() name: string; }
现在,我们创建 Student 实体,如下所示 −
import {Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable} from "typeorm"; import {Classes} from "./Classes"; @Entity() export class Student { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column() subjects: string; @ManyToMany(type => Classes) @JoinTable() classes: Classes[]; }
TypeORM - 使用存储库
存储库特定于一个实体。换句话说,每个实体都有自己的内置存储库,可以使用连接对象的 getRepository() 方法访问它,如下所示 −
const studRepository = manager.getRepository(Student);
一旦创建了学生存储库对象,就可以使用它来执行学生对象的所有数据库操作。
存储库类型
存储库 分为四类。它们如下 −
存储库
实体的默认存储库,可以使用 getRepository() 方法访问它,如下所示 −
const studRepository = manager.getRepository(Student);
现在,studRepository 可用于查询学生表
TreeRepository
用于树状结构实体,可以使用 getTreeRepository() 方法访问,如下所示 −
const studcaRepository = manager.getTreeRepository(Student);
MongoRepository
在 mongoDB 操作实体内使用,可以使用 getMongoRepository() 方法访问,如下所示 −
const detailsRepository = manager.getMongoRepository(Details);
CustomRepository
用于自定义存储库,可以使用下面指定的 getCustomRepository() 方法访问它,
const myUserRepository = manager.getCustomRepository(UserRepository);
存储库 API
让我们在本章中学习 EntityManager 的最重要方法。
manager
我们可以使用下面指定的 manager 方法访问 EntityManager −
const manager = storage.manager;
queryRunner
queryRunner 方法返回自定义查询运行器对象,它用于存储库的数据库操作。示例代码如下 −
const queryRunner = storage.queryRunner;
metadata
metadata 返回存储库的元数据。示例代码如下 −
const metadata = storage.metadata;
query
query 方法执行 SQL 查询。简单的选择查询如下所示 −
const qur = await storage.query(`select * from students`);
insert
insert 方法用于向数据库插入新实体或实体数组。示例代码如下 −
await repository.insert({ Name: "Student3", Age: 14 });
上述查询相当于,
insert into student(Name,age) values("Student3",14)
update
update 用于更新数据库中的现有记录。
await storage.update(1, { Name: "Adam" });
此查询的工作原理与下面提到的查询类似 −
update student SET Name = "Adam" where id = 1
delete
delete 方法将从表中删除指定的记录,
await storage.delete(Student, 1);
这将从 student 表中删除 ID 为 1 的学生。这相当于,
delete from student where id=1;
如果您想按姓名删除,请使用以下查询,
await storage.delete({ Name: "Student1" });
此查询将删除所有姓名为 Student1 的学生
** softDelete 和 restore **
它用于软删除数据,您可以根据学生的 ID 恢复记录。示例代码如下 −
await storage.softDelete(1);
您可以使用以下命令恢复学生记录 −
await storage.restore(1);
删除和恢复的另一种方法是使用 softRemove 和 recover 方法。示例代码如下 −
//查找实体 const enty = await storage.find(); //软删除实体 const entySoftRemove = await storage.softRemove(enty);
并且,您可以使用下面指定的恢复方法恢复它们,
await storage.recover(entySoftRemove);
save
save 用于将给定实体保存到数据库中。简单的学生实体可以按如下所示保存 −
import {Student} from "./entity/Student"; createConnection().then(async connection => { console.log("Inserting a new record into the student database..."); const stud = new Student(); stud.Name = "student1"; stud.age = 12; await repository.save(stud);
这将向数据库中添加新的学生记录。
remove
remove用于从数据库中删除给定的实体。简单的学生实体可以删除,如下所示 −
await storage.remove(stud);
count
count方法将返回表中可用的记录数,您可以使用它进行分页。示例代码如下 −
const cnt = await storage.count(Student, { age: 12 });
find
find方法用于搜索目的。它从数据库中获取所有记录,如下所示 −
const result = await storage.find({ id: 1 });
findOne
与 find 方法类似,但返回第一个匹配的记录。示例代码如下 −
const result = await storage.findOne({ id: 1 });
clear
clear 方法清除表中的所有数据。示例代码如下 −
await repository.clear();
TypeORM - 使用实体管理器
EntityManager 与 Repository 类似,用于管理数据库操作,例如插入、更新、删除和加载数据。虽然 Repository 处理单个实体,但 EntityManager 对所有实体都通用,并且能够对所有实体执行操作。
实体管理器 API
我们可以使用 getManager() 方法访问 EntityManager,如下所示 −
import { getManager } from "typeorm"; const entityManager = getManager();
让我们在本章中学习 EntityManager 最重要的方法。
connection
connection 方法返回到特定数据库的数据库 ORM 连接。示例代码如下 −
const connection = manager.connection;
QueryRunner
queryRunner方法返回自定义查询运行器对象,供实体管理器用于数据库操作。示例代码如下 −
const queryRunner = manager.queryRunner;
transaction
如果调用多个数据库请求,事务将在单个数据库事务中执行。获取事务的示例代码如下 −
await manager.transaction(async manager => { });
query
query方法执行sql查询。简单的插入查询如下所示 −
const qur = await manager.query(`insert into student(name,age) values('stud2',13)`);
insert
insert 方法用于向数据库插入新实体或实体数组。示例代码如下 −
await manager.insert(Student, { Name: "Student3", Age: 14 });
update
update 用于更新数据库中的现有记录。
await manager.update(User, 1, { Name: "Adam" });
此查询的工作方式类似于以下 SQL 查询,
UPDATE student SET Name = "Adam" WHERE id = 1
delete
delete 方法将从表中删除指定的记录,
await manager.delete(Student, 1);
这将删除 id 为 1 的学生记录。
save
save 用于将给定实体保存到数据库中。简单的 Student 实体可以按如下所示保存 −
import {Student} from "./entity/Student"; createConnection().then(async connection => { console.log("Inserting a new record into the student database..."); const stud = new Student(); stud.Name = "student1"; stud.age = 12; await connection.manager.save(stud); }
这将向数据库中添加新的学生记录。如果数据库中不存在给定的学生,则 save 方法将插入学生。否则,save 将更新数据库中现有的学生记录。
remove
remove 用于从数据库中删除给定的实体。简单的学生实体可以删除,如下所示 −
await manager.remove(stud);
count
count 方法将返回表中可用的记录数,您可以使用它进行分页。示例代码如下 −
const cnt = await manager.count(Student, { age: 12 });
find
find 方法用于搜索目的。它从数据库中获取所有记录,如下所示 −
console.log("正在从数据库加载用户..."); const students = await connection.manager.find(Student); console.log("已加载用户: ", students);
findOne
与 find 方法类似,但返回第一个匹配的记录。示例代码如下 −
const stud = await manager.findOne(Student, 1);
clear
clear 方法清除表中的所有数据。示例代码如下 −
await manager.clear(Student);
TypeORM - Query Builder 查询生成器
查询生成器用于以简单的方式构建复杂的 SQL 查询。它由 Connection 方法和 QueryRunner 对象初始化。
我们可以通过三种方式创建 QueryBuilder。
连接
考虑一个使用连接方法使用 QueryBuilder 的简单示例。
import {getConnection} from "typeorm"; const user = await getConnection() .createQueryBuilder() .select("user") .from(User, "user") .where("user.id = :id", { id: 1 }) .getOne();
实体管理器
让我们使用实体管理器创建一个查询生成器,如下所示 −
import {getManager} from "typeorm"; const user = await getManager() .createQueryBuilder(User, "user") .where("user.id = :id", { id: 1 }) .getOne();
存储库
我们可以使用存储库来创建查询生成器。如下所述,
import {getRepository} from "typeorm"; const user = await getRepository(User) .createQueryBuilder("user") .where("user.id = :id", { id: 1 }) .getOne();
别名
别名与 SQL 别名相同。我们使用 QueryBuilder 为 Student 表创建别名,如下所述 −
import {getConnection} from "typeorm"; const user = await getConnection() .createQueryBuilder() .select("stud") .from(Student, "stud")
此查询相当于,
select * from students as stud
参数
参数用作查询中动态值的占位符。在许多情况下,查找不同实体对象的查询除了值之外都是相同的。例如,查找不同学生的查询除了学生 ID数据之外都是相同的。在这种情况下,我们可以将参数用于学生 ID,然后更改参数以获取不同的学生对象。
参数的另一个重要用途是防止 SQL 注入。它是现代 Web 应用程序中的重要安全漏洞之一。通过在查询中使用参数,我们可以抵御 SQL 注入攻击。
参数的另一个重要用途是防止 SQL 注入。它是现代 Web 应用程序中的重要安全漏洞之一。通过在查询中使用参数,我们可以避免 SQL 注入攻击。
例如
"student.id = :id", { id: 1 }
这里,
:id - 参数名称。
{ id: 1 } - 参数的值
添加表达式
本节介绍如何使用表达式。
where
where 用于在条件匹配时过滤记录。
createQueryBuilder("student") .where("student.id = :id", { id: 1 })
此查询相当于,
select * from students student where student.id=1;
我们还可以在里面使用 AND、OR、NOT、IN 条件。
having
简单的 having 表达式定义如下 −
createQueryBuilder("student") .having("student.id = :id", { id: 1 })
此查询相当于,
select * from students student having student.id=1;
orderBy
orderby 用于根据字段对记录进行排序。
createQueryBuilder("student") .orderBy("student.name")
此查询相当于,
select * from students student order by student.name;
groupBy
用于根据指定列对记录进行分组。
createQueryBuilder("student") .groupBy("student.id")
此查询相当于,
select * from students student group by student.id;
limit
用于限制行的选择。下面的示例显示了如何在查询生成器中使用限制,
createQueryBuilder("student") .limit(5)
此查询相当于,
select * from students student limit 5;
offset
Offset 用于指定跳过结果的行数。它定义如下 −
createQueryBuilder("student") .offset(5)
此查询相当于,
select * from students student offset 5;
joins
join 子句用于根据相关列合并来自两个或多个表的行。考虑两个实体 −
Student.ts
import {Entity, PrimaryGeneratedColumn, Column, OneToMany} from "typeorm"; import {Project} from "./Project"; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; @OneToMany(type => Project, project => project.student) projects: project[]; }
Project.ts
import {Entity, PrimaryGeneratedColumn, Column, ManyToOne} from "typeorm"; import {Student} from "./Student"; @Entity() export class Project { @PrimaryGeneratedColumn() id: number; @Column() title: string; @ManyToOne(type => Student, student => student.projects) student: Student; }
让我们使用以下查询执行简单的左连接 −
const student = await createQueryBuilder("student") .leftJoinAndSelect("student.projects", "project") .where("student.name = :name", { name: "Student1" }) .getOne();
此查询相当于,
SELECT student.*, project.* FROM students student LEFT JOIN projects project ON project.student = student.id WHERE student.name = 'Student1'
同样,我们也可以尝试内部连接。
无选择连接
我们可以不使用选择来连接数据。让我们尝试使用内部连接的示例,如下所示 −
const student = await createQueryBuilder("student") .innerJoin("student.projects", "project") .where("student.name = :name", { name: "student1" }) .getOne();
上述查询相当于 −
SELECT student.* FROM students student INNER JOIN projects project ON project.student = student.id WHERE student.name = 'Student1';
分页
如果您的应用程序中有更多数据,则需要分页、页面滑块或滚动功能。
例如,如果您想在应用程序中显示前五个学生的项目,
const students = await getRepository(Student) .createQueryBuilder("student") .leftJoinAndSelect("student.projects", "project") .take(5) .getMany();
子查询
它被称为另一个查询中的查询或嵌套查询。我们在 FROM、WHERE 和 JOIN 表达式中使用子查询。
下面显示了简单示例 −
const projects = await connection .createQueryBuilder() .select("project.id", "id") .addSelect(subQuery => { return subQuery .select("student.name", "name") .from(Student, "student") .limit(1); }, "name") .from(Project, "project") .getMany();
隐藏字段
如果您的任何列字段被标记为 {select: false},则该列将被视为隐藏列。考虑以下实体 −
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm"; @Entity() export class Student { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column({select: false}) address: string; }
此处,
address 字段被标记为 hidden。我们可以使用 addSelect 方法从列中检索信息。它定义如下,
const student = await connection.getRepository(Student) .createQueryBuilder() .select("student.id", "student") .addSelect("student.address") .getMany();
getSql()
此方法用于获取查询生成器生成的 SQL 查询。它定义如下 −
const sql = createQueryBuilder("student") .where("student.name = :name", { name: "Student1" }) .orWhere("student.age = :age", { age: 14 }) .getSql();
TypeORM - 查询操作
数据操作用于管理和查看数据。本节介绍如何使用 QueryBuilder 访问数据库查询,如插入、更新、选择和删除查询。让我们逐一详细介绍。
构建插入查询
让我们创建一个 Customer 实体,如下所示 −
Customer.ts
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm"; @Entity() export class Customer { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column() age: number; }
我们在 index.ts 中添加以下更改,如下所示 −
index.ts
import "reflect-metadata"; import {createConnection} from "typeorm"; import {Customer} from "./entity/Customer"; import {getConnection} from "typeorm"; createConnection().then(async connection => { await getConnection().createQueryBuilder() .insert() .into(Customer) .values([ { name: "Adam",age:11}, { name: "David",age:12} ]) .execute(); }).catch(error => console.log(error));
现在,使用以下命令启动您的应用程序 −
npm start
输出
您可以在屏幕上看到以下输出 −
现在打开您的 mysql 服务器,表中插入了两个字段,如下所示 −
构建更新查询
最后一节,我们插入了两行数据。让我们检查一下更新查询是如何工作的。在 index.ts 中添加以下更改,如下所示 −
import "reflect-metadata"; import {createConnection} from "typeorm"; import {Customer} from "./entity/Customer"; import {getConnection} from "typeorm"; createConnection().then(async connection => { await getConnection() .createQueryBuilder() .update(Customer) .set({ name: "Michael" }) .where("id = :id", { id: 1 }) .execute(); console.log("data updated"); }).catch(error => console.log(error));
现在,使用以下命令启动您的应用程序 −
npm start
您可以在屏幕上看到以下输出 −
Mysql 表已修改,如下所示 −
构建 select 查询
select 查询用于显示表中的记录。让我们在 index.ts 中添加以下代码,如下所示 −
index.ts
import "reflect-metadata"; import {createConnection} from "typeorm"; import {Customer} from "./entity/Customer"; createConnection().then(async connection => { console.log("Display records from Customer table..."); const cus = new Customer(); console.log("Loading customers from the database..."); const customers = await connection.manager.find(Customer); console.log("Loaded users: ", customers); }).catch(error => console.log(error));
您可以在屏幕上看到以下输出 −
where 表达式
让我们在查询中添加 where 表达式来过滤客户。示例代码如下 −
import "reflect-metadata"; import {createConnection} from "typeorm"; import {Customer} from "./entity/Customer"; import {getConnection} from "typeorm"; createConnection().then(async connection => { const customer = await getConnection() .createQueryBuilder() .select("cus") .from(Customer, "cus") .where("cus.id = :id", { id: 1 }) .getOne(); console.log(customer); }) .catch(error => console.log(error));
上述程序将返回第一个 id 记录。您可以在屏幕上看到以下输出,
同样,您也可以尝试其他表达式。
构建删除查询
最后一节,我们插入、更新和选择数据。让我们检查一下删除查询是如何工作的。在 index.ts 中添加以下更改,如下所示 −
import "reflect-metadata"; import {createConnection} from "typeorm"; import {Customer} from "./entity/Customer"; import {getConnection} from "typeorm"; createConnection().then(async connection => { await getConnection() .createQueryBuilder() .delete() .from(Customer) .where("id = :id", { id: 1 }) .execute(); console.log("data deleted"); }).catch(error => console.log(error));
您可以在屏幕上看到以下输出 −
并且您的 mysql 表被修改如下 −
TypeORM - 事务
一般来说,事务是负责执行数据检索和更新的逻辑单元。本节详细介绍了事务。
创建事务
我们可以使用连接或EntityManage创建事务。下面的示例用于指定创建连接并在其中保存数据。
import {getConnection} from "typeorm"; await getConnection().transaction(async transactionalEntityManager => { await connection.manager.save(students); });
EntityManager如下所示 −
import {getManager} from "typeorm"; await getManager().transaction(async transactionalEntityManager => { await transactionalEntityManager.save(students); });
装饰器
TypeORM 中有三种与事务相关的装饰器。
- @Transaction - 将所有执行包装在单个数据库事务中。
- @TransactionManager - 用于在事务内执行查询。它定义如下,
@Transaction({isolation: "SERIALIZABLE"}) save(@TransactionManager() manager: EntityManager, student: Student) { return manager.save(student); }
这里,
我们对事务使用了SERIALIZABLE隔离级别。
- @TransactionRepository - 用于在存储库中注入事务。其定义如下,
@Transaction() save(student: Student, @TransactionRepository(Student) studentRepository: Repository<Student>) { return studentRepository.save(student); }
QueryRunner 中的事务
QueryRunner 用于执行所有数据库查询。它具有单个数据库连接。可以使用 QueryRunner 组织数据库事务。让我们使用 QueryRunner 执行单个事务。
import {getConnection} from "typeorm"; // 获取连接并创建新的查询运行器 const connection = getConnection(); const queryRunner = connection.createQueryRunner(); // 使用我们的新查询运行器建立真正的数据库连接 await queryRunner.connect(); // 现在我们可以在查询运行器上执行任何查询,例如:await queryRunner.query("SELECT * FROM students");
现在,使用以下语句启动事务 −
await queryRunner.startTransaction();
然后,使用以下语句提交并回滚事务,
try { await queryRunner.commitTransaction(); }
如果有任何错误,则由 catch() 处理,
catch (err) { // 由于我们有错误,让我们回滚我们所做的更改 await queryRunner.rollbackTransaction(); }
现在,按以下方式释放 queryRunner −
finally { // 您需要释放手动创建的查询运行器: await queryRunner.release(); }
TypeORM - 索引
一般来说,索引是通过优化数据存储来优化数据库性能的过程。它用于快速定位和访问数据库中的数据。本节介绍如何在 TypeORM 中使用索引。索引分为不同类型。让我们逐一详细介绍。
列索引
我们可以使用 @Index 为特定列创建索引。考虑一个 Customer 实体的示例,如下所示,并为 firstName 列定义索引,
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm"; @Entity() export class Student { @PrimaryGeneratedColumn() id: number; @Index() @Column() firstName: string; @Column() lastName: string; @Column() age: number; @Column() address: string; }
@Index 允许为索引指定名称 −
@Index("Name-idx") @Column() firstName: string;
唯一索引
要在列中指定 Unique 约束,请使用以下属性 −
{ unique: true }
例如,以下是为 Name 列指定唯一索引的代码 −
@Index({ unique: true }) @Column() firstName: string;
要为多个列应用索引,我们可以在 @Entity() 之后直接指定它。示例代码如下 −
@Entity() @Index(["firstName", "lastName"]) @Index(["firstName", "lastName"], { unique: true })
空间索引
空间索引允许访问空间对象。MySQL 和 PostgreSQL 支持空间索引。要在列中启用空间索引,请添加以下属性 −
{ spatial: true }
空间类型有多个子类型,例如几何、点、线串、多边形等,例如,如果您想在列中添加点空间类型,请使用以下代码 −
@Column("point") @Index({ spatial: true }) point: string;
禁用同步
要禁用同步,请在 @Index 装饰器上使用以下选项 −
{ synchronize: false }
TypeORM - 实体监听器和日志记录
实体监听器用于支持自定义方法和监听特定事件的实体。我们可以使用装饰器定义任何实体自定义方法。让我们简要了解一下装饰器。
- @AfterLoad − 使用 QueryBuilder 或存储库/管理器加载实体时,将调用此方法。
- @BeforeInsert − 此方法将在使用存储库/管理器插入实体之前调用。
- @AfterInsert − 此方法将在使用存储库/管理器插入实体之后调用。
- @BeforeUpdate − 此方法将在使用存储库/管理器更新现有实体之前调用它。
- @AfterUpdate −它将在实体更新后调用。
- @BeforeRemove − 它将在实体被删除之前调用。
- @AfterRemove − 它将在实体被删除后调用。
Subscriber
Subscriber 用于监听特定实体事件。它从 EntitySubscriberInterface 实现。让我们了解一个如何在订阅者中使用实体监听器的简单示例。考虑下面显示的 Student 实体 −
Student.ts
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm"; @Entity() export class Student { @PrimaryGeneratedColumn() id: number; @Column() Name: string; @Column() age: number; }
创建学生订阅者
使用以下命令创建订阅者 −
typeorm subscriber:create -n StudentSubscriber
上述命令在您的项目 src 中创建一个订阅者目录。然后,在您的订阅者中创建 StudentSubscriber.ts 文件。您可以看到以下响应,
订阅者 /Users/workspace/TypeORM/FirstProject/src/subscriber/StudentSubscriber.ts 已成功创建。
现在移动到文件,您可以看到以下代码 −
StudentSubscriber.ts
import {EventSubscriber, EntitySubscriberInterface} from "typeorm"; @EventSubscriber() export class StudentSubscriber implements EntitySubscriberInterface<any> { }
现在,在文件中添加以下更改,
import {EventSubscriber, EntitySubscriberInterface,InsertEvent} from "typeorm"; import {Student} from "../entity/Student"; @EventSubscriber() export class StudentSubscriber implements EntitySubscriberInterface<any> { listenTo() { return Student; } afterInsert(event: InsertEvent<Student>) { console.log(event); } }
这里,
我们使用了 afterInsert() 方法来调用实体事件。同样,您也可以使用其他事件。我们已经配置了 ormconfig.json 文件。现在,在 index.ts 文件中添加以下更改,如下所示 −
index.ts
import "reflect-metadata"; import {createConnection} from "typeorm"; import {Student} from "./entity/Student"; createConnection().then(async connection => { console.log('connection established'); }).catch(error => console.log(error));
执行应用程序后,您可以在屏幕上看到以下输出,
日志记录
数据库日志记录是高可用性数据库解决方案设计的重要组成部分,因为数据库日志可以从故障中恢复,并且可以同步主数据库和辅助数据库。
所有数据库都有与之关联的日志。这些日志记录数据库更改。如果需要将数据库恢复到上次完整离线备份之后的某个时间点,则需要日志将数据前滚到故障点。
日志记录选项
通过在数据库连接中添加 {logging: true} 来启用日志记录。日志记录选项分为不同类型。它们如下 −
查询 − 返回所有日志查询。它的定义如下所示 −
{ host: "localhost", ... logging: ["query"] }
error − 返回所有失败查询和错误的日志。其定义如下−
{ host: "localhost", ... logging: ["error"] }
schema − 返回模式的日志。
warn − 返回内部 ORM 警告。
info − 返回日志内部 ORM 信息性消息。
log − 返回内部 ORM 日志消息。
自定义记录器
自定义日志记录是一种简单且高度可定制的日志记录选项。我们可以使用以下代码创建自己的记录器类 −
import {Logger} from "typeorm"; export class MyCustomLogger implements Logger { // 实现记录器类中的所有方法 }
连接选项在 ormconfig.json 中指定如下 −
name: "mysql", type: "mysql", host: "localhost", port: 3306, username: "root", password: "root", database: "test", logger: new MyCustomLogger()
TypeORM 与 JavaScript
TypeORM 支持的默认语言是 TypeScript。由于 TypeScript 支持静态类型、类和装饰器,因此很容易定义实体及其属性。同时,在某些首选语言为 JavaScript 的项目中,JavaScript 也是必需的。TypeORM 也为 JavaScript 语言提供全面支持。 TypeORM 支持 es5 和 es6 两种 JavaScript 版本。
在本章中,我们将学习如何使用 JavaScript ES5 (ECMAScript 5) 编写 TypeORM 应用程序。
打开命令提示符并转到您的工作区。
cd /path/to/workspace/
运行以下命令创建 TypeORM 项目。
typeorm init --name typeorm-javascript-student-app --database mysql
打开 package.json 文件以删除 typescipt 引用。
original
{ "name": "typeorm-javascript-student-app", "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": { "typeorm": "0.2.24", "reflect-metadata": "^0.1.10", "mysql": "^2.14.1" }, "scripts": { "start": "ts-node src/index.ts" } }
updated
{ "name": "typeorm-javascript-student-app", "version": "0.0.1", "description": "Awesome project developed with TypeORM.", "dependencies": { "typeorm": "0.2.24", "mysql": "^2.14.1" }, "scripts": { "start": "node src/index.js" } }
这里,
- 删除了 devDependencies 部分和 dependences 部分中的 typescript 相关包。
- 将启动脚本更改为指向 javascript 代码而不是 typescript 代码。
运行以下命令安装必要的包。
npm install
删除 tsconfig.json 和 index.ts 文件。
删除 entity 文件夹中的 User.ts 文件,然后按照下面指定的方式创建 json 格式的学生实体 student.json −
{ "name": "Student", "columns": { "id": { "primary": true, "type": "int", "generated": true }, "name": { "type": "varchar" }, "age": { "type": "integer" } } }
创建一个新文件 src/index.js 并输入以下代码 −
var typeorm = require("typeorm"); var EntitySchema = typeorm.EntitySchema; typeorm.createConnection({ "type": "mysql", "host": "localhost", "port": 3306, "username": "root", "password": "123456", "database": "typeorm_test_db", "synchronize": true, "logging": false, entities: [ new EntitySchema(require("./entity/student.json")) ] }) .then(function(connection) { return connection.getRepository("Student"); }) .then(function(studentRepository) { var student = { name: "Student1", age: 18 }; return studentRepository.save(student) .then(function(savedStudent) { console.log("Student has been successfully saved: ", savedStudent); return studentRepository.find(); }) .then(function(students) { console.log("All students: ", students); return; }) .catch(function(error) { console.log("Error: ", error); return; }) }) .catch(function(error) { console.log("Error: ", error) return; });
这里,
除了下面提到的更改外,我们使用了相同的 typeORM 方法,
- 使用 EntitySchema 配置学生实体。
- 使用 JavaScript Promise 概念 (then / catch / finally) 块。
现在,使用以下命令运行应用程序 −
npm start
应用程序将学生信息插入数据库,然后获取数据库中的所有学生并将其显示在控制台中,如下所示 −
> typeorm-javascript-student-app@0.0.1 start /path/to/workspace/typeorm-javascript-student-app > node src/index.js Student has been successfully saved: { name: 'Student1', age: 18, id: 1 } All students: [ { id: 1, name: 'Student1', age: 18 } ]
TypeORM - 使用 MongoDB
本章介绍 TypeORM 提供的广泛 MongoDB 数据库支持。希望我们已经使用 npm 安装了 mongodb。如果尚未安装,请使用以下命令安装 MongoDB 驱动程序,
npm install mongodb --save
创建项目
让我们使用 MongoDB 创建一个新项目,如下所示 −
typeorm init --name MyProject --database mongodb
配置 ormconfig.json
让我们在 ormconfig.json 文件中配置 MongoDB 主机、端口和数据库选项,如下所示 −
ormconfig.json
{ "type": "mongodb", "host": "localhost", "port": 27017, "database": "test", "synchronize": true, "logging": false, "entities": [ "src/entity/**/*.ts" ], "migrations": [ "src/migration/**/*.ts" ], "subscribers": [ "src/subscriber/**/*.ts" ], "cli": { "entitiesDir": "src/entity", "migrationsDir": "src/migration", "subscribersDir": "src/subscriber" } }
定义实体和列
让我们在 src 目录中创建一个名为 Student 的新实体。实体和列相同。要生成主键列,我们使用 @PrimaryColumn 或
@PrimaryGeneratedColumn. 这可以定义为 @ObjectIdColumn. 简单示例如下所示 −
Student.ts
import {Entity, ObjectID, ObjectIdColumn, Column} from "typeorm"; @Entity() export class Student { @ObjectIdColumn() id: ObjectID; @Column() Name: string; @Column() Country: string; }
要保存此实体,请打开 index.ts 文件并添加以下更改 −
index.ts
import "reflect-metadata"; import {createConnection} from "typeorm"; import {Student} from "./entity/Student"; createConnection().then(async connection => { console.log("Inserting a new Student into the database..."); const std = new Student(); std.Name = "Student1"; std.Country = "India"; await connection.manager.save(std); console.log("Saved a new user with id: " + std.id); console.log("Loading users from the database..."); const stds = await connection.manager.find(Student); console.log("Loaded users: ", stds); console.log("TypeORM with MongoDB"); }).catch(error => console.log(error));
现在,启动您的服务器,您将获得以下响应 −
npm start
MongoDB EntityManager
我们还可以使用 EntityManager 来获取数据。简单示例如下所示 −
import {getManager} from "typeorm"; const manager = getManager(); const result = await manager.findOne(Student, { id:1 });
同样,我们也可以使用存储库来访问数据。
import {getMongoRepository} from "typeorm"; const studentRepository = getMongoRepository(Student); const result = await studentRepository.findOne({ id:1 });
如果您想使用 equal 选项过滤数据,如下所示 −
import {getMongoRepository} from "typeorm"; const studentRepository = getMongoRepository(Student); const result = await studentRepository.find({ where: { Name: {$eq: "Student1"}, } });
正如我们在本章中看到的,TypeORM 可以轻松地与 MongoDB 数据库引擎配合使用。
TypeORM 与 Express
Express 是用于创建 Web 应用程序的流行 JavaScript 框架之一。本章中,我们将学习如何将 TypeORM 与 express 框架结合使用。
创建一个简单的应用程序
TypeORM CLI 提供了一个简单的选项,可以创建一个与 TypeORM 集成的完整工作的 express Web 应用程序(Restful API 应用程序)。创建应用程序的 CLI 命令如下 −
cd /path/to/workspace typeorm init --express --name typeorm-express-sample --database mysql
上述命令将在 typeorm-express-sample 文件夹下创建一个新的 Web 应用程序。应用程序的结构如下 −
│ .gitignore │ ormconfig.json │ package.json │ README.md │ tsconfig.json │ └───src │ index.ts │ routes.ts │ ├───controller │ UserController.ts │ ├───entity │ User.ts │ └───migration
这里,
我们知道,ormconfig.json是TypeORM的配置文件,代码如下,
{ "type": "mysql", "host": "localhost", "port": 3306, "username": "test", "password": "test", "database": "test", "synchronize": true, "logging": false, "entities": [ "src/entity/**/*.ts" ], "migrations": [ "src/migration/**/*.ts" ], "subscribers": [ "src/subscriber/**/*.ts" ], "cli": { "entitiesDir": "src/entity", "migrationsDir": "src/migration", "subscribersDir": "src/subscriber" } }
在这里,更改数据库设置以匹配您的本地数据库设置。
package.json 文件是应用程序的主要配置。
tsconfig.json 文件包含与 TypeScript 相关的配置。
entity 文件夹包含 TypeORM 模型。CLI 将创建一个默认的 User 模型,如下所示 −
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm"; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column() firstName: string; @Column() lastName: string; @Column() age: number; }
controller文件夹包含 express 控制器。CLI 创建一个默认用户 API 控制器,用于添加/列出/删除用户详细信息。代码如下 −
import {getRepository} from "typeorm"; import {NextFunction, Request, Response} from "express"; import {User} from "../entity/User"; export class UserController { private userRepository = getRepository(User); async all(request: Request, response: Response, next: NextFunction) { return this.userRepository.find(); } async one(request: Request, response: Response, next: NextFunction) { return this.userRepository.findOne(request.params.id); } async save(request: Request, response: Response, next: NextFunction) { return this.userRepository.save(request.body); } async remove(request: Request, response: Response, next: NextFunction) { let userToRemove = await this.userRepository.findOne(request.params.id); await this.userRepository.remove(userToRemove); } }
此处,
all 方法用于从数据库获取所有用户。
one 方法用于使用 user id 从数据库获取单个用户。
save 方法用于将用户信息保存到数据库中。
delete 方法用于使用 user id 从数据库删除用户。routes.ts 文件将用户控制器方法映射到正确的 URL,代码如下 −
import {UserController} from "./controller/UserController"; export const Routes = [{ method: "get", route: "/users", controller: UserController, action: "all" }, { method: "get", route: "/users/:id", controller: UserController, action: "one" }, { method: "post", route: "/users", controller: UserController, action: "save" }, { method: "delete", route: "/users/:id", controller: UserController, action: "remove" }];
这里,
/users url 映射到用户控制器。每个动词 post、get 和 delete 都映射到不同的方法。
最后,index.ts 是我们的主要 Web 应用程序入口点。源代码如下 −
import "reflect-metadata"; import {createConnection} from "typeorm"; import * as express from "express"; import * as bodyParser from "body-parser"; import {Request, Response} from "express"; import {Routes} from "./routes"; import {User} from "./entity/User"; createConnection().then(async connection => { // create express app const app = express(); app.use(bodyParser.json()); // register express routes from defined application routes Routes.forEach(route => { (app as any)[route.method](route.route, (req: Request, res: Response, next: Function) => { const result = (new (route.controller as any))[route.action](req, res, next); if (result instanceof Promise) { result.then(result => result !== null && result !== undefined ? res.send(result) : undefined); } else if (result !== null && result !== undefined) { .json(result); } }); }); // setup express app here // ... // start express server app.listen(3000); // insert new users for test await connection.manager.save(connection.manager.create(User, { firstName: "Timber", lastName: "Saw", age: 27 })); await connection.manager.save(connection.manager.create(User, { firstName: "Phantom", lastName: "Assassin", age: 24 })); console.log("Express server has started on port 3000. Open http://localhost:3000/users to see results"); }).catch(error => console.log(error));
在这里,应用程序配置路由,插入两个用户,然后在端口 3000 启动 Web 应用程序。我们可以通过 http://localhost:3000 访问该应用程序
要运行该应用程序,请按照以下步骤操作 −
让我们使用以下命令安装必要的软件包 −
npm install
输出
npm notice created a lockfile as package-lock.json. You should commit this file. npm WARN typeorm-express-sample@0.0.1 No repository field. npm WARN typeorm-express-sample@0.0.1 No license field. added 176 packages from 472 contributors and audited 351 packages in 11.965s 3 packages are looking for funding run `npm fund` for details found 0 vulnerabilities
运行以下命令启动应用程序。
npm start
输出
> typeorm-express-sample@0.0.1 start /path/to/workspace/typeorm-express-sample > ts-node src/index.ts Express server has started on port 3000. Open http://localhost:3000/users to see results
让我们使用 curl 命令访问我们的 Web 应用程序 API,如下所示 −
curl http://localhost:3000/users
这里,
curl 是一个命令行应用程序,用于从命令提示符访问 Web 应用程序。它支持所有 HTTP 动词,例如 get、post、delete 等,
输出
[{"id":1,"firstName":"Timber","lastName":"Saw","age":27},{"id":2,"firstName":"Phantom","lastName":"Assassin","age":24}]
要获取第一条记录,我们可以使用以下命令 −
curl http://localhost:3000/users/1
输出
{"id":1,"firstName":"Timber","lastName":"Saw","age":27}
要删除用户记录,我们可以使用以下命令 −
curl -X DELETE http://localhost:3000/users/1
正如我们在本章中看到的,TypeORM可以轻松集成到 express 应用程序中。
TypeORM - 迁移
迁移就像数据库的版本控制。它用于修改和共享应用程序的数据库模式。本节介绍 TypeORM 中的迁移工作原理。
创建新迁移
要创建新迁移,首先我们需要在 ormconfig.json 中设置连接。它定义如下 −
ormconfig.json
"type": "mysql", "host": "localhost", "port": 8889, "username": "root", "password": "root", "database": "Library", "entities": ["entity/*.js"], "migrationsTableName": "student_migration_table", "migrations": ["migration/*.js"], "cli": { "migrationsDir": "migration" }
这里,
- migrationsTableName − 它指的是迁移表名称。
- migrations − TypeORM 从给定目录加载迁移。
- cli − 表示迁移将在特定目录内创建。
创建 Book 实体
让我们在 src/entity/Book.ts 中创建一个名为 Book 的实体,如下所示 −
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class Book { @PrimaryGeneratedColumn() id: number; @Column() title: string; @Column() text: string; }
执行 CLI 创建新迁移
现在,我们可以使用 CLI 执行新迁移,如下所示 −
语法
typeorm migration:create -n <migration-name>
示例
typeorm migration:create -n myMigration
执行上述命令后,您可以看到以下响应 −
Migration /path/to/project/src/migration/1587101104904-myMigration.ts has been generated successfully.
现在,移动到 src/migration/1587101104904-myMigration.ts 文件内部,如下所示。
import {MigrationInterface, QueryRunner} from "typeorm"; export class myMigration1587101104904 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise<any> { } public async down(queryRunner: QueryRunner): Promise<any> { } }
这里,
我们有两个方法 up 和 down。up 方法用于向迁移添加更改,down 方法用于还原迁移中的更改。
让我们在 myMigration.ts 文件中添加 up 方法,如下所示 −
import {MigrationInterface, QueryRunner} from "typeorm"; export class Book1587131893261 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise<any> { await queryRunner.query(`ALTER TABLE book ADD COLUMN price int`); } public async down(queryRunner: QueryRunner): Promise<any> { } }
这里,
我们在 book 表中添加了一个新列 price。现在,执行 CLI 以添加上述更改。
ts-node ./node_modules/typeorm/cli.js migration:run
上述命令执行迁移并按顺序运行它们。现在,您可以在屏幕上看到以下更改 −
输出
现在打开您的 mysql 服务器,新列已添加。如下所示 −
同样,我们可以将列标题数据类型修改为 varchar(30),如下所示,
import {MigrationInterface, QueryRunner} from "typeorm"; export class Book1587131893261 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise<any> { await queryRunner.query(`ALTER TABLE book MODIFY COLUMN title varchar(30)`); } public async down(queryRunner: QueryRunner): Promise<any> { } }
现在,执行相同的命令,您可以进行以下更改 −
ts-node ./node_modules/typeorm/cli.js migration:run
输出
Book 表修改为,
恢复迁移
让我们在 down 方法中添加以下代码来恢复迁移−
import {MigrationInterface, QueryRunner} from "typeorm"; export class Book1587131893261 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise<any> { } public async down(queryRunner: QueryRunner): Promise<any> { await queryRunner.query(`ALTER TABLE book drop column price`); // 恢复使用"up"方法所做的操作 } }
现在,执行以下命令以还原所有更改 −
ts-node ./node_modules/typeorm/cli.js migration:revert
您可以看到以下响应 −
输出
Book 表被修改为,
输出
正如我们在本章中看到的,TypeORM 使编写数据库迁移脚本变得容易。
TypeORM - 使用 CLI
本节详细介绍了 TypeORM CLI 命令。
创建 TypeORM 项目
typeorm init 是设置 TypeORM 项目最简单、最快捷的方法。您可以创建一个新项目,
typeorm init --name Demoproject --database mysql
执行命令后,您将在屏幕上看到以下输出 −
Project created inside /Users/workspace/TypeORM/Demoproject directory.
创建实体
要使用 CLI 创建新实体,请执行以下操作:
typeorm entity:create -n Person
现在,Person 实体已在项目 src 目录中创建。
Entity /Users/workspace/TypeORM/Demoproject/src/entity/Person.ts has been created successfully.
如果您有一个多模块项目结构,且多个实体位于不同的目录中,则可以使用以下命令,
typeorm entity:create -n Person -d src/Person/entity
创建新订阅者
使用 CLI 创建新订阅者,如下所示 −
typeorm subscription:create -n PersonSubscriber
您可以看到以下响应 −
Subscriber /path/to/TypeORM/Demoproject/src/subscriber/PersonSubscriber.ts has been created successfully.
创建迁移
您可以使用 CLI 创建新的迁移,如下所示 −
typeorm migration:create -n PersonMigration
上述命令在项目 src 中创建了一个迁移目录。迁移文件存储在其中。
Migration /path/to/TypeORM/Demoproject/src/migration/1587395030750-PersonMigration.ts has been generated successfully.
数据库模式
要同步数据库模式,请使用以下命令 −
typeorm schema:sync
要完全删除数据库模式,请使用以下命令 −
typeorm schema:drop
Sql 查询
如果您想执行任何 sql 查询,我们可以直接从这里执行。例如,要显示客户的所有记录,请使用以下查询 −
typeorm query "select * from customers"
如果您想清除缓存中存储的所有内容。您可以使用以下命令执行此操作 −
typeorm cache:clear
结论
TypeORM 是一个出色的开源 ORM 框架,可用于创建高质量且可扩展的应用程序,从小型应用程序到具有多个数据库的大型企业应用程序。