TypeORM - 快速指南


TypeORM - 简介

TypeORM 框架是一个对象关系映射(ORM)框架。一般来说,对象部分是指应用程序中的域/模型,关系部分是指关系数据库管理系统(例如Oracle、MySQL、MS-SQL、PostgreSQL等)中表之间的关系,最后映射部分指连接模型和表格的Behave。

ORM 是一种将实体与数据库表映射的工具。ORM 通过自动执行对象到表和表到对象的转换来简化开发过程。一旦您可以在一处编写数据模型,更新、维护和重用代码就会变得更加容易。

由于该模型与应用程序的其余部分弱绑定,因此您可以更改它,而无需与应用程序的其他部分产生任何硬依赖,并且可以轻松地在应用程序内的任何位置使用它。TypeORM 非常灵活,将数据库系统从应用程序中抽象出来,使我们能够从 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、实体框架等。

TypeORM的特点

TypeORM 具有以下功能 -

  • 根据您的模型自动创建数据库表方案。
  • 轻松插入、更新和删除数据库中的对象。
  • 在表之间创建映射(一对一、一对多和多对多)。
  • 提供简单的 CLI 命令。

TypeORM 的好处

TypeORM 是易于使用的 ORM 框架,编码简单。它有以下好处 -

  • 高质量和松散耦合的应用程序。
  • 可扩展的应用程序。
  • 轻松与其他模块集成。
  • 完美适合从小型到企业应用程序的任何架构。

TypeORM - 安装

本章介绍如何在您的计算机上安装 TypeORM。在开始安装之前,请确保已安装 npm。要确认您有 npm,您可以在终端中运行以下命令。

npm -v

它显示版本。如果未安装,请下载最新版本并安装在您的计算机上。

安装类型ORM

让我们使用 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

现在,安装另一个可选包。使用以下命令进行节点类型 -

npm install @types/node --save

您可以看到以下响应 -

+ @types/node@12.12.31 

added 1 package and audited 1236 packages in 2.167s

安装数据库驱动

在本节中,让我们为数据库安装必要的节点包。

要安装MySQLMariaDB软件包,请使用以下命令 -

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是数据库名称。执行上述命令后,您可以看到以下响应:

Project created inside /path/to/TypeORM/FirstProject directory

现在,进入我们的项目目录并使用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和两个子目录entitymigration
  • index.ts - 应用程序的入口点。
  • 实体- 包含数据库模型。
  • 迁移- 包含数据库迁移代码。
  • 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
   }    
 }

这里,

typehostusernamepassworddatabase和 port 选项与数据库设置相关。mysql可以使用以下配置进行配置 -

{ 
   "type": "mysql", 
   "host": "localhost", 
   "port": 3306, 
   "username": "db_username", "password": "db_password", "database": "db_name" 
}
  • 实体- 指实体类的位置。
  • 迁移- 指迁移类的位置。
  • 订阅者- 指您的订阅者类别的位置。
  • 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 - 连接 API

为了与数据库交互,我们需要一个到数据库的连接对象。我们需要在执行数据库操作之前创建一个连接对象,并且在数据库操作完成后必须终止它。本节我们来了解一下TypeORM提供的Connection API。

创建新连接

在创建新连接之前,我们需要在ormconfig.json配置文件中配置数据库连接详细信息。连接详细信息示例如下所示 -

ormconfig.json

{ 
   name: "firstconnection", 
   type: "mysql", 
   host: "localhost", 
   port: 3306, 
   username: "root", 
   password: "root", 
   database: "firstDB" 
}

这里,

  • name - 数据库连接的名称。
  • type - 数据库类型。
  • host - 数据库服务器的主机名。
  • port - 数据库服务器端口。
  • username - 有权访问数据库的帐户名。
  • 密码- 上述帐户的密码。
  • 数据库- 要连接的数据库的名称。

创建连接

TypeORM 提供的CreateConnection方法用于创建新连接。它的定义如下,

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文件中指定的第一个连接的配置详细信息来创建连接对象。

TypeORM 还提供了另一个 API,createConnections 来一次性创建多个连接,然后在必要时使用它,如下所示 -

import { createConnections, Connection } from "typeorm"; 

const connections: Connection[] = await createConnections([ 

]);

这里,

连接将所有连接对象保存为数组。

连接管理器

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 并输入以下代码 -

学生.ts

import {Entity, PrimaryGeneratedColumn, Column} from "typeorm"; 

@Entity() 
export class Student {   

   @PrimaryGeneratedColumn() 
   id: number; 
   
   @Column() 
   Name: string; 
   
   @Column() 
   age: number; 
}

这里,

  • Entity()装饰器类用于表示Student类是一个实体。
  • PrimaryGenerateColumn()装饰器类用于表示 id 列是Student实体的主键列。
  • Column()装饰器类用于表示其他列,例如学生实体的姓名年龄

现在,实体类Student已创建。TypeORM 会自动生成一个与我们数据库中的Student实体对应的表,并将其命名为Student。现在,移至src/index.ts文件并添加以下代码 -

索引.ts

import "reflect-metadata"; 
import {createConnection} from "typeorm";
import {Student} from "./entity/Student"; //import Student entity

createConnection().then(async connection => { 

   console.log("Inserting a new record into the student database..."); 
   
   //create student object const stud = new Student(); 
   
   //Assign student name and age here 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 行导入相关类、createConnectionStudent
  • 第 5 行使用createConnection创建与数据库的新连接,如果建立连接,它将执行then块内的代码。
  • 第 10 行创建新的 Student 对象 Stud。
  • 第 13-14 行设置了新创建的 Stud 对象的属性。
  • 第 17 行使用connection.manager对象中可用的 save 方法将实体保存到数据库。
  • 第 23 行使用connection.manager对象中的 find 方法从数据库中获取学生详细信息。

启动 Mysql 服务器并运行您的应用程序

我们已经创建了 Student 实体并在 index.ts 中创建了连接。让我们启动 MySql 服务器和您的应用程序。

npm start

这将在屏幕上返回以下输出 -

输出

插入

打开mysql服务器,将学生表添加到数据库中。

学生桌

正如前面所了解的,实体实际上是属性的集合。作为实体对象指的是数据库表。它的属性/成员变量引用相应的数据库表的字段/列。TypeORM通过Column类支持所有类型的数据库字段。让我们在本章中了解 TypeORM 支持的不同类型的列。

@Column()装饰器类用于表示实体中的列及其类型。

例如,学生实体的年龄属性和年龄属性的类型可以定义如下 -

@Column("int") age: integer; // OR @Column({ type: "int" }) age: integer;

这里,

  • 年龄是实体的属性。换句话说,年龄是数据库中学生表中的一个字段/列。
  • int表示数据库中年龄列的类型。

TypeORM 支持流行数据库引擎中几乎所有可用的类型。实际上,TypeORM 为每个数据库引擎启用不同的类型集。我们可以使用我们的数据库引擎支持的任何数据库类型,没有任何问题。

例如,postgresql 数据库引擎的 TypeORM 支持的类型如下 -

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 提供了除类型之外的一组广泛的选项来描述列。例如,长度选项是指数据库字段的长度,可以指定如下 -

@Column("varchar", { length: 100 })

一些最常见的列选项如下 -

  • name - 数据库字段/列的名称。
  • length - 数据库字段/列的长度。
  • nullable - 指定数据库字段/列是否允许为空。
  • default - 数据库字段/列的默认值。
  • Primary - 指定数据库字段/列是否是表的主键。
  • unique - 指定数据库字段/列是否唯一
  • *精度** - 数据库字段/列的精度
  • 规模- 数据库字段/列的规模
  • comment - 数据库字段/列的注释或描述

@生成的装饰器

TypeORM 提供了额外的装饰器 @Generate 来自动生成列值。例如,通用唯一标识符(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; 
}

@PrimaryGenerateColumn()

@PrimaryGenerateColumn()字段用于指定主列以及在数据库中自动生成列值。如下所示 -

import {Entity, PrimaryGeneratedColumn} from "typeorm"; 

@Entity() 
export class Student {

   @PrimaryGeneratedColumn() 
   id: number;
}

这里,

您不必分配 id 值;它将由数据库表中的TypeORM自动生成。

@PrimaryGenerateColumn(“uuid”)

@PrimaryGenerateColumn 还接受一个参数来指定生成器的类型。主要用途之一是根据 UUID 生成唯一 id。

import {Entity, PrimaryGeneratedColumn} from "typeorm";

@Entity() 
export class Student {  
   @PrimaryGeneratedColumn("uuid") id: string; 
}

简单数组列类型

高级关系数据库支持数组数据类型。为了支持数组数据类型,TypeORM 提供了一种特殊的列类型“*simple-array”来存储原始数组值。使用它的示例代码如下 -

@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中定义为,

索引.ts

const stud = new Student(); 
stud.info = { firstName: "John", middleName: "peter", lastName: "Michael" };

专题栏目

TypeORM支持以下特殊列

  • @CreateDateColumn - 这是一个特殊的列,用于自动设置实体的插入日期。
  • @UpdateDateColumn - 用于自动设置实体的更新时间。
  • @VersionColumn - 自动设置实体的版本号。

实体继承

实体继承用于减少实体的重复。考虑以下实体 -

结果.ts

@Entity() 
export class Result {    

   @PrimaryGeneratedColumn() 
   id: number; 
   
   @Column() 
   title: string; 
   
   @Column() 
   description: string; 
   
   @Column() 
   eligible: string 
}

等级.ts

Grade.ts 的代码如下 -

@Entity() 
export class Grade {

   @PrimaryGeneratedColumn() 
   id: number; 
   
   @Column() 
   name: string; 
   
   @Column() 
   title: string; 
   
   @Column() 
   description: string;
   
   
   
   @Column() 
   grading : string; 
}

这里,

上面两个实体有 id、标题和描述列。使用实体继承,我们创建一个基类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 - 用于指定关系列是否可为空。

让我们详细了解不同类型的关系映射。

一对一

正如我们之前了解到的,一个表字段的实例包含另一个表字段的实例,反之亦然。让我们创建一个详细信息表 -

详情.ts

import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";

@Entity() 
export class Details {
   @PrimaryGeneratedColumn() 
   id: number; 
   
   @Column() 
   gender: string; 
   
   @Column() 
   country: string; 
}

让我们创建另一个实体 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详细信息表的映射。@JoinColumn()包含“关系 id”和客户表的外键。我们可以将关系保存在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; 
}

现在,我们创建学生实体如下 -

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; 
}

现在,我们创建学生实体如下 -

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可以用来查询student表

树存储库

用于树状结构实体,可以使用getTreeRepository()方法进行访问,如下所示 -

const studcaRepository = manager.getTreeRepository(Student);

Mongo存储库

在 mongoDB 操作实体内部使用,可以使用getMongoRepository()方法进行访问,如下所示 -

const detailsRepository = manager.getMongoRepository(Details);

自定义存储库

用于自定义存储库,可以使用getCustomRepository()方法进行访问,如下所示,

const myUserRepository = manager.getCustomRepository(UserRepository);

存储库API

让我们学习本章中EntityManager最重要的方法。

经理

我们可以使用管理器方法访问EntityManager,如下所示 -

const manager = repository.manager;

查询运行器

queryRunner方法返回自定义查询运行程序对象,并由存储库用于数据库操作。示例代码如下 -

const queryRunner = repository.queryRunner;

元数据

元数据返回存储库的元数据。示例代码如下 -

const metadata = repository.metadata;

询问

query方法执行 SQL 查询。简单的选择查询如下所示 -

const qur = await repository.query(`select * from students`);

插入

insert方法用于将新实体或实体数组插入到数据库中。示例代码如下 -

await repository.insert({ 
   Name: "Student3", 
   Age: 14 
});

上述查询相当于,

insert into student(Name,age) values("Student3",14)

更新

update用于更新数据库中现有的记录。

await repository.update(1, { Name: "Adam" });

该查询的工作原理与下面提到的类似 -

update student SET Name = "Adam" where id = 1

删除

delete方法将从表中删除指定的记录,

await repository.delete(Student, 1);

这将从学生表中删除 id 为1的学生。它相当于,

delete from student where id=1;

如果您想按名称删除,请使用以下查询,

await repository.delete({ Name: "Student1" });

此查询将删除所有名为Student1的学生

** 软删除和恢复 **

用于软删除数据,可以根据学生的id恢复记录。示例代码如下 -

await repository.softDelete(1);

您可以使用以下命令恢复学生记录 -

await repository.restore(1);

删除和恢复的另一种选择是使用softRemove恢复方法。示例代码如下 -

//find the entities const enty = await repository.find(); 

//soft removed entity const entySoftRemove = await repository.softRemove(enty);

并且,您可以使用下面指定的恢复方法来恢复它们,

await repository.recover(entySoftRemove);

节省

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用于从数据库中删除给定的实体。可以删除简单的学生实体,如下所示 -

await repository.remove(stud);

数数

count方法将返回表中可用记录的数量,您可以使用它进行分页目的。示例代码如下 -

const cnt = await repository.count(Student, { age: 12 });

寻找

find方法用于搜索目的。它从数据库中获取所有记录,如下所示 -

const result = await repository.find({ id: 1 });

找一个

与find方法类似,但返回第一个匹配的记录。示例代码如下 -

const result = await repository.findOne({ id: 1 });

清除

clear方法清除表中的所有数据。示例代码如下 -

await repository.clear();

TypeORM - 使用实体管理器

EntityManager类似于Repository,用于管理插入、更新、删除和加载数据等数据库操作。Repository处理单个实体,而EntityManager对所有实体都是通用的,并且能够对所有实体进行操作。

实体管理器API

我们可以使用getManager()方法访问EntityManager,如下所示 -

import { getManager } from "typeorm"; const entityManager = getManager();

让我们学习本章中EntityManager最重要的方法。

联系

连接方法返回到特定数据库的数据库 ORM 连接。示例代码如下 -

const connection = manager.connection;

查询运行器

queryRunner方法返回自定义查询运行程序对象,实体管理器将其用于数据库操作。示例代码如下 -

const queryRunner = manager.queryRunner;

交易

如果调用多个数据库请求,事务将在单个数据库事务中执行。获取交易的示例代码如下 -

await manager.transaction(async manager => { 
});

询问

query方法执行sql查询。简单的插入查询如下所示 -

const qur = await manager.query(`insert into student(name,age) values('stud2',13)`);

插入

insert方法用于将新实体或实体数组插入到数据库中。示例代码如下 -

await manager.insert(Student, { 
   Name: "Student3", 
   Age: 14 
});

更新

update用于更新数据库中现有的记录。

await manager.update(User, 1, { Name: "Adam" });

此查询的工作方式与下面的 SQL 查询类似,

UPDATE student SET Name = "Adam" WHERE id = 1

删除

delete方法将从表中删除指定的记录,

await manager.delete(Student, 1);

这将删除 id 为 1 的学生记录。

节省

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 connection.manager.save(stud); 
}

这会将新的学生记录添加到数据库中。如果数据库中不存在给定的学生,sa​​ve 方法将插入学生。否则,保存将更新数据库中现有的学生记录。

消除

remove用于从数据库中删除给定的实体。可以删除简单的学生实体,如下所示 -

await manager.remove(stud);

数数

count方法将返回表中可用记录的数量,您可以使用它进行分页目的。示例代码如下 -

const cnt = await manager.count(Student, { age: 12 });

寻找

find方法用于搜索目的。它从数据库中获取所有记录,如下所示 -

console.log("Loading users from the database..."); 
const students = await connection.manager.find(Student); console.log("Loaded users: ", students);

找一个

与find方法类似,但返回第一个匹配的记录。示例代码如下 -

const stud = await manager.findOne(Student, 1);

清除

clear 方法清除表中的所有数据。示例代码如下 -

await manager.clear(Student);

TypeORM - 查询生成器

查询构建器用于以简单的方式构建复杂的 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用于过滤条件符合的记录。

createQueryBuilder("student") .where("student.id = :id", { id: 1 })

这个查询相当于,

select * from students student where student.id=1;

我们还可以在里面使用 AND、OR、NOT、IN 条件。

拥有

简单的having表达式定义如下 -

createQueryBuilder("student") .having("student.id = :id", { id: 1 })

这个查询相当于,

select * from students student having student.id=1;

排序依据

orderby 用于根据字段对记录进行排序。

createQueryBuilder("student") .orderBy("student.name")

这个查询相当于,

select * from students student order by student.name;

通过...分组

它用于根据指定的列对记录进行分组。

createQueryBuilder("student") .groupBy("student.id")

这个查询相当于,

select * from students student group by student.id;

限制

它用于限制行的选择。下面的示例展示了如何在查询生成器中使用限制,

createQueryBuilder("student") .limit(5)

这个查询相当于,

select * from students student limit 5;

抵消

Offset 用于指定跳过结果的行数。它的定义如下 -

createQueryBuilder("student") .offset(5)

这个查询相当于,

select * from students student offset 5;

加入

join 子句用于根据相关列组合两个或多个表中的行。考虑两个实体 -

学生.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[]; 
}

项目.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'

同样,我们也可以尝试内连接。

无需选择即可加入

我们可以在不使用 select 的情况下连接数据。让我们尝试使用内连接的示例,如下所示 -

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; 
}

这里,

地址字段被标记为隐藏。我们可以使用addSelect方法从列中检索信息。它的定义如下,

const student = await connection.getRepository(Student) .createQueryBuilder() .select("student.id", "student")    .addSelect("student.address") .getMany();

获取Sql()

此方法用于获取查询生成器生成的 SQL 查询。它的定义如下 -

const sql = createQueryBuilder("student") .where("student.name = :name", { name: "Student1" })  .orWhere("student.age = :age", { age: 14 }) 
.getSql();

TypeORM - 查询操作

数据操作用于管理和查看数据。本节介绍如何使用 QueryBuilder 访问数据库查询,例如插入、更新、选择和删除查询。我们来一一详细了解一下。

构建插入查询

让我们创建一个客户实体,如下所示 -

客户.ts

import {Entity, PrimaryGeneratedColumn, Column} from "typeorm"; 
@Entity() 
export class Customer {       

   @PrimaryGeneratedColumn() 
   id: number; 
   
   @Column() 
   name: string; 
   
   @Column() 
   age: number; 
}

让我们在 index.ts 中添加以下更改,如下所示 -

索引.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表修改如下:

MySQL表

构建选择查询

select查询用于显示表中的记录。让我们在index.ts中添加以下代码,如下所示 -

索引.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 表达式来过滤客户。示例代码如下 -

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 记录。您可以在屏幕上看到以下输出,

第一个 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 表被修改如下 -

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"; 

// get a connection and create a new query runner 
const connection = getConnection(); const queryRunner = connection.createQueryRunner(); 

// establish real database connection using our new query runner 
await queryRunner.connect(); 

// now we can execute any queries on a query runner, for example: await queryRunner.query("SELECT * FROM students");

现在,使用以下语句启动事务 -

await queryRunner.startTransaction();

然后,使用以下语句提交和回滚事务,

try { 
   await queryRunner.commitTransaction(); 
}

如果有任何错误,则由catch()处理,

catch (err) { 

   // since we have errors lets rollback changes we made await queryRunner.rollbackTransaction(); 
}

现在,释放 queryRunner,如下所示 -

finally { 
   
   // you need to release query runner which is manually created: await queryRunner.release(); 
}

TypeORM - 索引

一般来说,索引是通过优化数据存储来优化数据库性能的过程。