TypeORM 快速指南



TypeORM - 簡介

TypeORM 框架是一個物件關係對映 (ORM) 框架。一般來說,物件部分指的是應用程式中的域/模型,關係部分指的是關係資料庫管理系統 (例如 Oracle、MySQL、MS-SQL、PostgreSQL 等) 中表之間的關係,最後對映部分指的是連線模型和表的動作。

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、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

安裝資料庫驅動程式

在本節中,讓我們為我們的資料庫安裝必要的節點包。

要安裝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 - 應用程式的入口點。
  • 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
   }    
 }

這裡,

typehostusernamepassworddatabase 和 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 - 連線 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 - 擁有資料庫訪問許可權的帳戶名。
  • 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 還提供了一個名為 connectionManager 的 API 來建立連線。其定義如下:

import {getConnectionManager, ConnectionManager, Connection} from "typeorm"; 

const connectionManager = getConnectionManager(); 

const connection = connectionManager.create({ 

}); 
await connection.connect();

TypeORM 優先使用createConnection 而不是ConnectionManager 來建立連線物件。

TypeORM - 實體

實體是欄位和相關資料庫操作的集合。它用於將資料庫表及其欄位與實體及其屬性對映。本章詳細介紹了 TypeORM 實體。

介紹

讓我們在程式碼中建立一個簡單的 Entity 類。移動到您的專案根目錄,進入 src 資料夾並移動到 entity 資料夾。現在,建立一個 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 實體的其他列,例如NameAge

現在,已建立 Entity 類Student。TypeORM 將自動生成一個與Student 實體對應的表到我們的資料庫中,並且它將被命名為student。現在,移動到src/index.ts 檔案並新增以下程式碼:

index.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

這將在您的螢幕上顯示以下輸出 -

輸出

Inserting

開啟 **mysql** 伺服器,以下 **student** 表已新增到您的資料庫中。

Student Table

如前所述,實體實際上是屬性的集合。實體物件引用資料庫表。其屬性/成員變數引用相應的資料庫表的欄位/列。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; 
}

simple-array 列型別

高階關係資料庫支援陣列資料型別。為了支援陣列資料型別,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 中定義為:

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

現在啟動您的伺服器,您將看到以下響應:

TS Node

現在開啟您的 mysql 伺服器並轉到您的資料庫,您將看到以下表格:

Mysql Server

Grade 表

Grade table

Result 表

Result table

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

一對多和多對一

正如我們之前瞭解到的,它指的是第一個表字段的例項包含第二個表字段的多個例項,稱為 **一對多** 對映;而第一個表的多個例項只包含第二個表的一個例項,稱為 **多對一** 對映。

考慮 **Student** 和 **project** 實體的示例,其中學生可以參與多個專案,但每個專案都只由一名學生負責。

讓我們建立一個名為 **Project** 的實體,如下所示:

Project

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**。但是,**@OneToMany** 無法在沒有 **@ManyToOne** 的情況下存在,並且 **@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);

多對多

正如我們之前瞭解到的,它指的是一個表中的多條記錄與另一個表中的多條記錄相關聯。例如,大學學生可以同時選修多門課程,這意味著學生每學期可能選修四五門課程,而一門課程可以有多名學生。

我們可以簡單地得出結論,一個學生可以選修多門課程,而一門課程可以有多名學生。讓我們為 **Classes** 建立一個實體,如下所示:

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 - 使用 Repository

Repository 專用於某個實體。換句話說,每個實體都有自己的內建儲存庫,並且可以使用 connection 物件的 getRepository() 方法訪問它,如下所示:

const studRepository = manager.getRepository(Student);

建立 student 儲存庫物件後,就可以使用它來執行 student 物件的所有資料庫操作。

Repository 型別

**Repository** 分為四類。它們如下:

Repository

實體的預設儲存庫,可以使用 **getRepository()** 方法訪問它,如下所示:

const studRepository = manager.getRepository(Student);

現在,**studRepository** 可用於查詢 student 表

TreeRepository

用於樹狀結構實體,可以使用 **getTreeRepository()** 方法訪問它,如下所示:

const studcaRepository = manager.getTreeRepository(Student);

MongoRepository

用於 MongoDB 操作實體,可以使用 **getMongoRepository()** 方法訪問它,如下所示:

const detailsRepository = manager.getMongoRepository(Details);

CustomRepository

用於自定義儲存庫,可以使用 **getCustomRepository()** 方法訪問它,如下所示:

const myUserRepository = manager.getCustomRepository(UserRepository);

Repository API

讓我們在本節中學習 EntityManager 中最重要的方法。

manager

我們可以使用 manager 方法訪問 **EntityManager**,如下所示:

const manager = repository.manager;

queryRunner

**queryRunner** 方法返回自定義查詢執行器物件,儲存庫使用它進行資料庫操作。示例程式碼如下:

const queryRunner = repository.queryRunner;

metadata

**metadata** 返回儲存庫的元資料。示例程式碼如下:

const metadata = repository.metadata;

query

**query** 方法執行 SQL 查詢。簡單的 select 查詢如下所示:

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

insert

**insert** 方法用於將新實體或實體陣列插入資料庫。示例程式碼如下:

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

以上查詢等價於:

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

update

**update** 用於更新資料庫中現有的記錄。

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

此查詢的工作方式類似於下面提到的查詢:

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

delete

**delete** 方法將從表中刪除指定的記錄,

await repository.delete(Student, 1);

這將從 **student** 表中刪除 id 為 **1** 的學生。它等價於:

delete from student where id=1;

如果要按名稱刪除,請使用以下查詢:

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

此查詢將刪除所有名稱為 **Student1** 的學生

** softDelete 和 restore **

它用於軟刪除資料,您可以根據學生的 ID 恢復記錄。示例程式碼如下:

await repository.softDelete(1);

您可以使用以下命令恢復學生記錄:

await repository.restore(1);

刪除和恢復的另一種選擇是使用 **softRemove** 和 **recover** 方法。示例程式碼如下:

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

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

並且,您可以使用如下所示的 recover 方法恢復它們:

await repository.recover(entySoftRemove);

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 repository.save(stud);

這將在資料庫中新增新的學生記錄。

remove

**remove** 用於從資料庫中刪除給定實體。簡單的 Student 實體可以如下刪除:

await repository.remove(stud);

count

**count** 方法將返回表中可用的記錄數,您可以將其用於分頁目的。示例程式碼如下:

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

find

find 方法用於搜尋目的。它從資料庫中獲取所有記錄,如下所示:

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

findOne

類似於find 方法,但返回第一個匹配的記錄。示例程式碼如下:

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

clear

clear 方法清除表中的所有資料。示例程式碼如下:

await repository.clear();

TypeORM - 使用 Entity Manager

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** 用於從資料庫中刪除給定實體。簡單的 Student 實體可以如下刪除:

await manager.remove(stud);

count

**count** 方法將返回表中可用的記錄數,您可以將其用於分頁目的。示例程式碼如下:

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

find

find 方法用於搜尋目的。它從資料庫中獲取所有記錄,如下所示:

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

findOne

類似於find 方法,但返回第一個匹配的記錄。示例程式碼如下:

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

clear

clear 方法清除表中的所有資料。示例程式碼如下:

await manager.clear(Student);

TypeORM - 查詢構建器

查詢生成器用於以簡單的方式構建複雜的 SQL 查詢。它從 Connection 方法和 QueryRunner 物件初始化。

我們可以透過三種方式建立 QueryBuilder。

Connection

考慮一個使用 connection 方法使用 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();

Repository

我們可以使用儲存庫來建立查詢生成器。如下所述,

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

它用於限制行的選擇。下面,示例顯示瞭如何在查詢生成器中使用 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'

類似地,我們也可以嘗試內部連線。

不帶選擇的連線

我們可以不使用 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; 
}

這裡,

address 欄位標記為隱藏。我們可以使用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

輸出

您可以在螢幕上看到以下輸出:

Data Inserted

現在開啟您的 mysql 伺服器,表插入了兩個欄位,如下所示:

Table Inserted

構建更新查詢

上一節,我們插入了兩行資料。讓我們檢查更新查詢如何工作。在 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

您可以在螢幕上看到以下輸出:

Data Updated

Mysql 表修改如下所示:

Mysql Table

構建選擇查詢

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

您可以在螢幕上看到以下輸出:

Output

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 記錄。您可以在螢幕上看到以下輸出,

First Id Records

類似地,您也可以嘗試其他表示式。

構建刪除查詢

上一節,我們已經插入、更新和選擇資料。讓我們檢查刪除查詢如何工作。在 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));

您可以在螢幕上看到以下輸出:

Output on Screen

您的 mysql 表修改如下:

Mysql Table is Modified

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 - 索引

通常,索引是一個透過最佳化資料儲存來最佳化資料庫效能的過程。它用於快速定位和訪問資料庫中的資料。本節說明如何在 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: 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 - 刪除實體後,將呼叫它。

訂閱者

訂閱者用於偵聽特定實體事件。它從EntitySubscriberInterface實現。讓我們瞭解一個關於如何在訂閱者中使用實體偵聽器的簡單示例。考慮以下所示的學生實體:

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 檔案。您可以看到以下響應,

Subscriber /Users/workspace/TypeORM/FirstProject/src/subscriber/StudentSubscriber.ts has been created successfully.

現在轉到檔案,您可以看到以下程式碼:

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

執行應用程式後,您可以在螢幕上看到以下輸出,

Application

日誌記錄

資料庫日誌記錄是您的高可用資料庫解決方案設計的重要組成部分,因為資料庫日誌使從故障中恢復成為可能,並且使同步主資料庫和輔助資料庫成為可能。

所有資料庫都與其關聯的日誌相關聯。這些日誌記錄資料庫更改的記錄。如果需要將資料庫恢復到最後一個完整離線備份之前的某個點,則需要日誌才能將資料向前滾動到故障點。

日誌記錄選項

透過在資料庫連線中新增 {logging: true} 來啟用日誌記錄。日誌記錄選項分為不同型別。它們如下:

query - 返回所有日誌查詢。它定義如下:

{ 
   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 { 
   
   // implement all methods from logger class 
}

連線選項在 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 支援 JavaScript 的 es5 和 es6 版本。

在本章中,讓我們學習如何在 JavaScript ES5(ECMAScript 5)中編寫 TypeORM 應用程式。

開啟命令提示符並轉到您的工作區。

cd /path/to/workspace/

執行以下命令建立 TypeORM 專案。

typeorm init --name typeorm-javascript-student-app --database mysql

開啟 package.json 檔案以刪除 typescript 引用。

原始

{ 
   "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" } 
}

更新

{ 
   "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
Start Server

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

如果要使用以下等號選項過濾資料:

import {getMongoRepository} from "typeorm"; 

const studentRepository = getMongoRepository(Student); 
const result = await studentRepository.find({ 
   where: { 
      Name: {$eq: "Student1"}, 
   } 
});

正如我們在本章中看到的,TypeORM 使使用 MongoDB 資料庫引擎變得容易。

TypeORM 與 Express

Express 是一個流行的 JavaScript 框架,用於建立 Web 應用程式。讓我們在本章中學習如何在 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.jsonTypeORM 配置檔案。程式碼如下:

{ 
   "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 方法用於使用 使用者 ID 從資料庫中獲取單個使用者。

save 方法用於將使用者資訊儲存到資料庫中。

delete 方法用於使用 使用者 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 https://:3000/users to see results"); 
}).catch(error => console.log(error));

在這裡,應用程式配置路由,插入兩個使用者,然後在埠 3000 上啟動 Web 應用程式。我們可以在 https://: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 https://:3000/users to see results

讓我們使用 curl 命令訪問我們的 Web 應用程式 API,如下所示:

curl https://: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 https://:3000/users/1

輸出

{"id":1,"firstName":"Timber","lastName":"Saw","age":27}

要刪除使用者記錄,我們可以使用以下命令:

curl -X DELETE https://: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> { 
   } 
}

這裡,

我們有兩個方法 updownup 方法用於將更改新增到遷移,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

上述命令執行遷移並按順序執行它們。現在,您可以在螢幕上看到以下更改:

輸出

Executes Migrations

現在開啟您的 mysql 伺服器,已新增新列。如下所示:

Open Mysql Server

類似地,我們可以將列標題資料型別修改為 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

輸出

Command

Book 表已修改為:

Book Table

撤消遷移

讓我們在 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`); // reverts things made in "up" method
   } 
}

現在,執行以下命令以撤消所有更改:

ts-node ./node_modules/typeorm/cli.js migration:revert

您可以看到以下響應:

輸出

Response

Book 表已修改為:

輸出

Database Migration Script

正如我們在本章中看到的,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 subscriber: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 框架,用於從小型應用程式到具有多個數據庫的大型企業應用程式建立高質量且可擴充套件的應用程式。

廣告
© . All rights reserved.