TypeORM是一個ORM框架,它可以運行在NodeJS、瀏覽器、Cordova、PhoneGap、Ionic、React Native、Expo和Electron平臺上,可以與TypeScript和JavaScript (ES5, ES6, ES7)一起使用。
它的目標是始終支持最新的JavaScript特性并提供額外的特性以幫助你開發任何使用數據庫的應用程序 —— 不管是只有幾張表的小型應用還是擁有多數據庫的大型企業應用。
不同于現有的所有其他JavaScript ORM框架,TypeORM支持Active Record和Data Mapper模式,這意味著你用最有效的方法編寫高質量的、松耦合的、可擴展的、可維護的應用程序。
TypeORM參考了很多其他優秀ORM的實現, 比如 Hibernate, Doctrine 和 Entity Framework.
TypeORM 的一些特性:
- 支持Active Record和Data Mapper(你可以自由選擇)
- 實體和列
- 數據庫特性列類型
- 實體管理
- 存儲庫和自定義存儲庫
- 清潔對象關系模型
- 關聯(關系)
- 貪婪和延遲關系
- 單向的,雙向的和自引用的關系
- 支持多重繼承模式
- 級聯
- 索引
- 事務
- 遷移和自動遷移
- 連接池
- 復制
- 使用多個數據庫連接
- 使用多個數據庫類型
- 跨數據庫和跨模式查詢
- 優雅的語法,靈活而強大的QueryBuilder
- 左聯接和內聯接
- 準確的分頁連接查詢
- 查詢緩存
- 原始結果流
- 日志
- 監聽者和訂閱者(鉤子)
- 支持閉包表模式
- 在模型或者分離的配置文件中聲明模式
- json / xml / yml / env格式的連接配置
- 支持 MySQL / MariaDB / Postgres / SQLite / Microsoft SQL Server / Oracle / sql.js
- 支持 MongoDB NoSQL 數據庫
- 在NodeJS / 瀏覽器 / Ionic / Cordova / React Native / Expo / Electron平臺上工作
- 支持 TypeScript 和 JavaScript
- 產生出高性能、靈活、清晰和可維護的代碼
- 遵循所有可能的最佳實踐
- 命令行工具
還有更多...
使用TypeORM你的模型是這樣的:
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
@Column()
age: number;
}
你的域邏輯是這樣的:
const user = new User();
user.firstName = "Timber";
user.lastName = "Saw";
user.age = 25;
await repository.save(user);
const allUsers = await repository.find();
const firstUser = await repository.findOne(1);
const timber = await repository.findOne({ firstName: "Timber", lastName: "Saw" });
await repository.remove(timber);
或者,你如果喜歡使用“ActiveRecord”實現,你也可以使用它:
import {Entity, PrimaryGeneratedColumn, Column, BaseEntity} from "typeorm";
@Entity()
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
@Column()
age: number;
}
你的域邏輯是這樣的:
const user = new User();
user.firstName = "Timber";
user.lastName = "Saw";
user.age = 25;
await user.save();
const allUsers = await User.find();
const firstUser = await User.findOne(1);
const timber = await User.findOne({ firstName: "Timber", lastName: "Saw" });
await timber.remove();
請注意
這個文檔可能不是最新的。
可以去官網查看最新的英文文檔。
非常歡迎你的貢獻。
安裝
-
安裝TypeORM:
npm install typeorm --save
-
需要安裝依賴模塊
reflect-metadata
:npm install reflect-metadata --save
在應用里全局引用一下:
- 比如在app.ts的入口處
require("reflect-metadata")
- 比如在app.ts的入口處
-
你可能需要安裝node類型:
npm install @types/node --save
-
安裝數據庫驅動:
-
MySQL 或 MariaDB
npm install mysql --save
-
PostgreSQL
npm install pg --save
-
SQLite
npm install sqlite3 --save
-
Microsoft SQL Server
npm install mssql --save
-
sql.js
npm install sql.js --save
-
Oracle (experimental)
npm install oracledb --save
可以根據你的數據庫選擇安裝上面的任意一個.
使用oracle驅動需要參考安裝說明:地址.
-
TypeScript配置
確保你的TypeScript編譯器的版本大于2.3,并且在tsconfig.json
開啟下面設置:
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
同時需要開啟編譯選項里的lib
下的es6
或者從@typings
安裝es6-shim
快速開始
開始使用TypeORM的最快方法是使用它的CLI命令生成一個初始項目。
快速開始只有在NodeJS應用程序中使用TypeORM才可以使用。
如果你正在使用其他平臺,請看分步指南。
首先全局安裝TypeORM:
npm install typeorm -g
然后轉到新項目的目錄并運行該命令:
typeorm init --name MyProject --database mysql
name
即項目的名稱,database
是你將使用的數據庫。數據庫可以是下列值之一:mysql
、mariadb
、postgres
、sqlite
、mssql
、oracle
、mongodb
、cordova
、react-native
、expo
。
該命令將在MyProject
目錄中生成一個新項目,其中包含以下文件:
MyProject
├── src // 放你的 TypeScript 代碼
│ ├── entity // 放實體(數據庫模型)的目錄
│ │ └── User.ts // 實體的案例
│ ├── migration // 遷移文件目錄
│ └── index.ts // 應用程序入口
├── .gitignore // 標準git忽略文件
├── ormconfig.json // ORM和數據連接配置
├── package.json // node模塊依賴
├── README.md // 簡單的說明文件
└── tsconfig.json // TypeScript編譯配置
你也可以在現有的node項目目錄執行
typeorm init
,但是一定要小心 - 它可能會覆蓋你已經有的一些文件。
下一步是安裝項目依賴
cd MyProject
npm install
在安裝過程中,修改 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"
]
}
通常來說,大多數時候你只需要配置host
,username
,password
,database
或者 port
選項。
配置和模塊安裝都完成之后,就可以運行應用程序了:
npm start
就是這樣,你的應用程序應該成功地運行并將一個新用戶插入到數據庫中。
你可以繼續這個項目,集成你需要的其他模塊,并創建更多的實體。
運行
typeorm init --name MyProject --database mysql --express
命令可以安裝express
,生成一個更高級的項目。
分步指南
你對ORM的期望是什么?
首先,你預期它將為你創建數據庫表,并查找/插入/更新/刪除你的數據,而不必編寫大量難以維護的SQL查詢。
本指南將向你展示如何從頭開始設置TypeORM,并讓它按照你所期望的ORM進行。
創建模型
與數據庫一起工作從創建表開始。
如何告訴TypeORM創建一個數據庫表?
答案是 - 通過模型。
你的應用程序中的模型就是你的數據庫中的表。
例如你有一個 Photo
模型:
export class Photo {
id: number;
name: string;
description: string;
filename: string;
views: number;
}
你想在你的數據庫中存儲照片。
要在數據庫中存儲東西,首先需要一個數據庫表,并從模型創建數據庫表。
不是所有的模型,而僅僅是那些你定義為實體。
創建實體
實體是你用 @Entity
裝飾的模型。
將為這些模型創建一個數據庫表。
使用TypeORM你將在任何地方使用實體。
你可以使用他們加載/插入/更新/刪除并執行其他操作。
讓我們把Photo
模型變成一個實體:
import {Entity} from "typeorm";
@Entity()
export class Photo {
id: number;
name: string;
description: string;
filename: string;
views: number;
isPublished: boolean;
}
現在,將會為 Photo
實體創建一個數據庫表,我們能夠在應用程序的任何地方使用它。
我們已經創建了一個數據庫表,然而沒有列的表示不存在的。
讓我們在數據庫表中創建一些列吧。
添加數據庫表列
要添加數據庫列,只需要將生成的實體的屬性用 @Column
裝飾。
import {Entity, Column} from "typeorm";
@Entity()
export class Photo {
@Column()
id: number;
@Column()
name: string;
@Column()
description: string;
@Column()
filename: string;
@Column()
views: number;
@Column()
isPublished: boolean;
}
現在 id
,name
,description
,filename
,views
和 isPublished
列將會被添加 photo
表。
數據庫中的列類型是從你使用的屬性類型推斷出來的,例如:number
將會被轉成 integer
,string
轉為 varchar
,boolean
轉為 bool
,等。
但是你可以通過隱式在 @Column
裝飾器傳入類型將列類型指定為任何你數據庫支持的類型。
我們生成了一個帶有列的數據庫表,但是還剩下一件事。
每個數據庫表必須有一個帶有主鍵的列。
創建一個主鍵列
每個表都必須至少有一個主鍵列。這是一個要求,你不能避免。要使列成為主鍵,你需要使用 @PrimaryColumn
修飾符。
import {Entity, Column, PrimaryColumn} from "typeorm";
@Entity()
export class Photo {
@PrimaryColumn()
id: number;
@Column()
name: string;
@Column()
description: string;
@Column()
filename: string;
@Column()
views: number;
@Column()
isPublished: boolean;
}
創建一個自動生成的列
現在,假設你希望將id列自動生成(這就是所謂的自動遞增/按順序/連續的/生成唯一標識列)。
要做到這一點,你需要將 @PrimaryColumn
修飾符更改為 @PrimaryGeneratedColumn
修飾符:
import {Entity, Column, PrimaryGeneratedColumn} from "typeorm";
@Entity()
export class Photo {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
description: string;
@Column()
filename: string;
@Column()
views: number;
@Column()
isPublished: boolean;
}
列數據類型
接下來,讓我們修復數據類型。默認情況下,字符串被映射到一個varchar(255)類型(取決于數據庫類型)。
數字被映射到一個integer類型(取決于數據庫類型)。
我們不希望所有的列都是有限的varchars或整數。
讓我們設置正確的數據類型:
import {Entity, Column, PrimaryGeneratedColumn} from "typeorm";
@Entity()
export class Photo {
@PrimaryGeneratedColumn()
id: number;
@Column({
length: 100
})
name: string;
@Column("text")
description: string;
@Column()
filename: string;
@Column("double")
views: number;
@Column()
isPublished: boolean;
}
列類型取決于數據庫支持的類型。
可以設置數據庫支持的任何列類型。
更多關于支持的列類型信息可以在這里找到這里。
創建數據庫連接
現在實體已經有了,讓我們新建一個 index.ts
(或 app.ts
不管你叫它什么)的文件,并配置數據庫連接:
import "reflect-metadata";
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
createConnection({
type: "mysql",
host: "localhost",
port: 3306,
username: "root",
password: "admin",
database: "test",
entities: [
Photo
],
synchronize: true,
logging: false
}).then(connection => {
// 這里可以寫實體操作相關的代碼
}).catch(error => console.log(error));
在例子里使用的是mysql,你也可以選擇其他數據庫,只需要簡單修改driver選項里的數據庫的類型就可以了,比如:mysql、mariadb、postgres、sqlite、mssql、oracle、cordova、react-native、expo或mongodb
同樣可以修改host, port, username, password 以及database等設置。
把Photo實體加到數據連接的實體列表中,所有需要在這個連接下使用的實體都必須加到這個列表中。
synchronize
選項可以在應用啟動時確保你的實體和數據庫保持同步。
引用目錄下的所有實體
接下來我們可能會創建更多的實體并把它們一一加到配置當中。
不過這樣會比較麻煩,好在可以直接寫上實體的目錄,這樣這個目錄下的所有實體都可以在當前連接中被使用:
import {createConnection} from "typeorm";
createConnection({
driver: {
type: "mysql",
host: "localhost",
port: 3306,
username: "root",
password: "admin",
database: "test"
},
entities: [
__dirname + "/entity/*.js"
],
synchronize: true,
}).then(connection => {
// here you can start to work with your entities
}).catch(error => console.log(error));
啟動應用
現在可以啟動app.ts
,啟動后可以發現數據庫自動被初始化,并且Photo這個表也會創建出來。
+-------------+--------------+----------------------------+
| photo |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(500) | |
| description | text | |
| filename | varchar(255) | |
| views | int(11) | |
| isPublished | boolean | |
+-------------+--------------+----------------------------+
添加和插入photo
現在創建一個新的photo然后存到數據庫:
import {createConnection} from "typeorm";
createConnection(/*...*/).then(connection => {
let photo = new Photo();
photo.name = "Me and Bears";
photo.description = "I am near polar bears";
photo.filename = "photo-with-bears.jpg";
photo.views = 1;
photo.isPublished = true;
connection.manager
.save(photo)
.then(photo => {
console.log("Photo has been saved");
});
}).catch(error => console.log(error));
使用async/await語法
現在利用TypeScript的async/await語法來實現同樣的功能:
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
createConnection(/*...*/).then(async connection => {
let photo = new Photo();
photo.name = "Me and Bears";
photo.description = "I am near polar bears";
photo.filename = "photo-with-bears.jpg";
photo.views = 1;
photo.isPublished = true;
await connection.manager.save(photo);
console.log("Photo has been saved");
}).catch(error => console.log(error));
使用EntityManager
剛剛我們創建了一個新的photo并且存進數據庫。使用EntityManager可以操作實體,現在用EntityManager
來把photo從數據庫中取出來。
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
createConnection(/*...*/).then(async connection => {
/*...*/
let savedPhotos = await connection.manager.find(Photo);
console.log("All photos from the db: ", savedPhotos);
}).catch(error => console.log(error));
savedPhotos 會從數據庫中取到的是一個Photo對象的數組
使用Repositories
現在重構下代碼,使用Repository
來代替EntityManage。每個實體都有自己的repository,可以對這個實體進行任何操作。
如果要對實體做很多操作,Repositories會比EntityManager更加方便。
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
createConnection(/*...*/).then(async connection => {
let photo = new Photo();
photo.name = "Me and Bears";
photo.description = "I am near polar bears";
photo.filename = "photo-with-bears.jpg";
photo.views = 1;
photo.isPublished = true;
let photoRepository = connection.getRepository(Photo);
await photoRepository.save(photo);
console.log("Photo has been saved");
let savedPhotos = await photoRepository.find();
console.log("All photos from the db: ", savedPhotos);
}).catch(error => console.log(error));
從數據庫中取photos
現在來嘗試用Repository做一些取數據方面的操作:
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
createConnection(/*...*/).then(async connection => {
/*...*/
let allPhotos = await photoRepository.find();
console.log("All photos from the db: ", allPhotos);
let firstPhoto = await photoRepository.findOne(1);
console.log("First photo from the db: ", firstPhoto);
let meAndBearsPhoto = await photoRepository.findOne({ name: "Me and Bears" });
console.log("Me and Bears photo from the db: ", meAndBearsPhoto);
let allViewedPhotos = await photoRepository.find({ views: 1 });
console.log("All viewed photos: ", allViewedPhotos);
let allPublishedPhotos = await photoRepository.find({ isPublished: true });
console.log("All published photos: ", allPublishedPhotos);
let [allPhotos, photosCount] = await photoRepository.findAndCount();
console.log("All photos: ", allPhotos);
console.log("Photos count: ", photosCount);
}).catch(error => console.log(error));
更新photo
現在來從數據庫中取出一個photo,修改并更新到數據庫。
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
createConnection(/*...*/).then(async connection => {
/*...*/
let photoToUpdate = await photoRepository.findOne(1);
photoToUpdate.name = "Me, my friends and polar bears";
await photoRepository.save(photoToUpdate);
}).catch(error => console.log(error));
這個id = 1
的photo在數據庫中就成功更新了.
刪除photo
再來,從數據庫中刪除我們的photo:
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
createConnection(/*...*/).then(async connection => {
/*...*/
let photoToRemove = await photoRepository.findOne(1);
await photoRepository.remove(photoToRemove);
}).catch(error => console.log(error));
這個id = 1
的photo就在數據庫中被移除了。
一對一關系
來創建與另一個類的一對一關系。
新建PhotoMetadata.ts用來存photo的元信息。
import {Entity, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn} from "typeorm";
import {Photo} from "./Photo";
@Entity()
export class PhotoMetadata {
@PrimaryGeneratedColumn()
id: number;
@Column("int")
height: number;
@Column("int")
width: number;
@Column()
orientation: string;
@Column()
compressed: boolean;
@Column()
comment: string;
@OneToOne(type => Photo)
@JoinColumn()
photo: Photo;
}
這里我們用到了一個新的裝飾器@OneToOne
,它可以用來在兩個實體之間創建一對一關系。
type => Photo
指示了我們想要連接的實體類名,這里因為TypeScript語言的支持原因不能直接用類名。
當然也可以使用() => Photo
,但是type => Photo
顯得更有可讀性。
Type變量本身并不包含任何東西。
我們同樣使用了@JoinColumn
裝飾器,這個裝飾器可以指定一對一關系的擁有者。
關系可以是單向的或雙向的,但是只有一方是擁有者,加個這個裝飾器就表示關系是給這個表服務的。
現在運行app,會新創建一個table,這個table有一個連接photo的外鍵:
+-------------+--------------+----------------------------+
| photo `譯者注:應該是PhotoMetadata` |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| height | int(11) | |
| width | int(11) | |
| comment | varchar(255) | |
| compressed | boolean | |
| orientation | varchar(255) | |
| photoId | int(11) | FOREIGN KEY |
+-------------+--------------+----------------------------+
存一個有一對一關系的對象
現在來創建一個photo,一個photo的元信息,并把它們已經連接起來。
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
import {PhotoMetadata} from "./entity/PhotoMetadata";
createConnection(/*...*/).then(async connection => {
// 創建一個photo
let photo = new Photo();
photo.name = "Me and Bears";
photo.description = "I am near polar bears";
photo.filename = "photo-with-bears.jpg"
photo.isPublished = true;
// 創建一個photo的元信息
let metadata = new PhotoMetadata();
metadata.height = 640;
metadata.width = 480;
metadata.compressed = true;
metadata.comment = "cybershoot";
metadata.orientation = "portait";
metadata.photo = photo; // 這里把兩者連起來
// 獲取實體repositories
let photoRepository = connection.getRepository(Photo);
let metadataRepository = connection.getRepository(PhotoMetadata);
// 先來把photo存到數據庫
await photoRepository.save(photo);
// photo存完了,再存下photo的元信息
await metadataRepository.save(metadata);
// 搞定
console.log("metadata is saved, and relation between metadata and photo is created in the database too");
}).catch(error => console.log(error));
雙向關系
關系可以是單向的或是雙向的.
現在PhotoMetadata和Photo的關系是單向的,關系擁有者是PhotoMetadata,Photo并不知道PhotoMetadata,這樣如果要想從Photo里得到PhotoMetadata的數據會比較麻煩。
現在來改變一下,把單向改成雙向:
import {Entity, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn} from "typeorm";
import {Photo} from "./Photo";
@Entity()
export class PhotoMetadata {
/* ... 其他列 */
@OneToOne(type => Photo, photo => photo.metadata)
@JoinColumn()
photo: Photo;
}
import {Entity, Column, PrimaryGeneratedColumn, OneToOne} from "typeorm";
import {PhotoMetadata} from "./PhotoMetadata";
@Entity()
export class Photo {
/* ... 其他列 */
@OneToOne(type => PhotoMetadata, photoMetadata => photoMetadata.photo)
metadata: PhotoMetadata;
}
photo => photo.metadata
是用來指定反向關系的字段名字,photo.metadata就指出了Photo里的metadata字段名字。
當然也可以使用@OneToOne('metadata')
來達到同樣的目的,不過這種對于以后的代碼重構不友好。
按上面說的,@JoinColumn
只能在關系的一邊使用來使這邊做為關系的擁有者,關系擁有者在數據庫里的表現就是擁有一個外鍵列。
取出關系對象的數據
現在來用一個查詢來取出photo以及它的元信息。
有兩種方式,一是用FindOptions
,另一個是使用QueryBuilder
。
先試下FindOptions
,通過指定FindOptions
接口作為參數來使用Repository.find
方法可以完成非常復雜的查詢。
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
import {PhotoMetadata} from "./entity/PhotoMetadata";
createConnection(/*...*/).then(async connection => {
/*...*/
let photoRepository = connection.getRepository(Photo);
let photos = await photoRepository.find({ relations: ["metadata"] });
}).catch(error => console.log(error));
返回的photos是從數據庫里取回的photo的數組,每個photo都包含它的元信息。
alias
是FindOptions的一個必需選項,這是你自己在select里定義的別名,然后需要用在接下來的 where, order by, group by, join 以及其他表達式.
這里還用到了innerJoinAndSelect
,表示內聯查詢photo.metadata的數據。
"photo.metadata"
里"photo"是一個別名,"metadata"則是你想查詢的那個對象的屬性名。
"metadata"
: 是內聯返回數據的新的別名.
下面來嘗試第二種方式:QueryBuilder
來達到同樣的目的. 使用QueryBuilder
可以優雅完成復雜的查詢:
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
import {PhotoMetadata} from "./entity/PhotoMetadata";
createConnection(/*...*/).then(async connection => {
/*...*/
let photos = await connection
.getRepository(Photo)
.createQueryBuilder("photo")
.innerJoinAndSelect("photo.metadata", "metadata")
.getMany();
}).catch(error => console.log(error));
使用 cascade 選項來自動保存關系著的對象
上面要保存關系對象需要一個一個來保存,略顯麻煩。
如果我們需要當關系對象中的一個被保存后,另一個也同樣被保存,則可以使用cascade
選項來做到。
稍微改下@OneToOne
裝飾:
export class Photo {
/// ... 其他列
@OneToOne(type => PhotoMetadata, metadata => metadata.photo, {
cascade: true,
})
metadata: PhotoMetadata;
}
使用cascade就可以不需要像上面那邊先存photo再存metadata了。
現在我們來單單存photo對象,由于cascade的作用,metadata也會自動存上。
createConnection(options).then(async connection => {
// 創建photo對象
let photo = new Photo();
photo.name = "Me and Bears";
photo.description = "I am near polar bears";
photo.filename = "photo-with-bears.jpg"
photo.isPublished = true;
// 創建photo metadata 對象
let metadata = new PhotoMetadata();
metadata.height = 640;
metadata.width = 480;
metadata.compressed = true;
metadata.comment = "cybershoot";
metadata.orientation = "portait";
photo.metadata = metadata; // 連接起來
// 得到repository
let photoRepository = connection.getRepository(Photo);
// 存photo
await photoRepository.save(photo);
// photo metadata也自動存上了
console.log("Photo is saved, photo metadata is saved too.")
}).catch(error => console.log(error));
多對一/一對多關系
接下來顯示多對一/一對多關系。
假設一個photo會有一個author,并且每個author可以有很多photo。
先創建Author實體:
import {Entity, Column, PrimaryGeneratedColumn, OneToMany, JoinColumn} from "typeorm";
import {Photo} from "./Photo";
@Entity()
export class Author {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@OneToMany(type => Photo, photo => photo.author) // 備注:下面會為Photo創建author屬性
photos: Photo[];
}
Author包含一個反向的關系,OneToMany
總是反向的,并且總是與ManyToOne
成對出現。
現在來為Photo加上關系擁有者。
import {Entity, Column, PrimaryGeneratedColumn, ManyToOne} from "typeorm";
import {PhotoMetadata} from "./PhotoMetadata";
import {Author} from "./Author";
@Entity()
export class Photo {
/* ... 其他列 */
@ManyToOne(type => Author, author => author.photos)
author: Author;
}
在ManyToOne/OneToMany
關系中,擁有者一邊總是ManyToOne
。譯者注:擁有外鍵者即關系擁有者
也就是ManyToOne
的那個字段存的是另一個對象的id。譯者注:也就是上面的author雖然屬性是Author,但在數據庫中類型是Author id的類型,存的也是id
執行上面的代碼將會自動創建author表,如下:
+-------------+--------------+----------------------------+
| author |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
+-------------+--------------+----------------------------+
因為photo表已經存在,所以不是增加而是修改photo表 - 添加一個新外鍵列author:
+-------------+--------------+----------------------------+
| photo |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
| description | varchar(255) | |
| filename | varchar(255) | |
| isPublished | boolean | |
| authorId | int(11) | FOREIGN KEY |
+-------------+--------------+----------------------------+
多對多關系
假設photo可以存在多個相冊中,并且相冊里可以包含多個photo。
先創建一個Album
類
import {Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable} from "typeorm";
@Entity()
export class Album {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@ManyToMany(type => Photo, photo => photo.albums)
@JoinTable()
photos: Photo[];
}
@JoinTable
多對多關系擁有者必須指定的。
接著給Photo
實體加個反向關系:
export class Photo {
/// ... 其他列
@ManyToMany(type => Album, album => album.photos)
albums: Album[];
}
執行上面的代碼后會自動創建一個叫 album_photos_photo_albums的聯接表:
+-------------+--------------+----------------------------+
| album_photos_photo_albums |
+-------------+--------------+----------------------------+
| album_id | int(11) | PRIMARY KEY FOREIGN KEY |
| photo_id | int(11) | PRIMARY KEY FOREIGN KEY |
+-------------+--------------+----------------------------+
記得把Album
實體加到ConnectionOptions中:
const options: ConnectionOptions = {
// ... 其他配置
entities: [Photo, PhotoMetadata, Author, Album]
};
現在來往數據庫里插入albums和photos
let connection = await createConnection(options);
// 創建幾張相冊
let album1 = new Album();
album1.name = "Bears";
await connection.manager.save(album1);
let album2 = new Album();
album2.name = "Me";
await connection.manager.save(album2);
// 創建幾個相片
let photo = new Photo();
photo.name = "Me and Bears";
photo.description = "I am near polar bears";
photo.filename = "photo-with-bears.jpg";
photo.albums = [album1, album2];
await connection.manager.save(photo);
// 現在我們的相片已經保存,并且添加到相冊里面了
// 讓我們開始加載它們:
const loadedPhoto = await connection
.getRepository(Photo)
.findOne(1, { relations: ["albums"] });
loadedPhoto
將是這樣的:
{
id: 1,
name: "Me and Bears",
description: "I am near polar bears",
filename: "photo-with-bears.jpg",
albums: [{
id: 1,
name: "Bears"
}, {
id: 2,
name: "Me"
}]
}
使用QueryBuilder
可以利用QueryBuilder來構建一個非常復雜的查詢,例如:
let photos = await connection
.getRepository(Photo)
.createQueryBuilder("photo") // first argument is an alias. Alias is what you are selecting - photos. You must specify it.
.innerJoinAndSelect("photo.metadata", "metadata")
.leftJoinAndSelect("photo.albums", "album")
.where("photo.isPublished = true")
.andWhere("(photo.name = :photoName OR photo.name = :bearName)")
.orderBy("photo.id", "DESC")
.skip(5)
.take(10)
.setParameters({ photoName: "My", bearName: "Mishka" })
.getMany();
這個查詢會查找已經published的,并且name是"My"或"Mishka",
得到的結果會從第5個開始(分頁偏移決定的),
并且只會得到10個結果(分頁每頁個數決定的),
所得結果是以id的倒序排序的,
Photo的albums是左聯接,photo的metadata是內聯接。
你將在應用程序中大量使用QueryBuilder。
了解更多QueryBuilder這里.
樣例
看看樣例里這些例子的用法
這些倉庫,你可以克隆下來幫助你開始:
- Example how to use TypeORM with TypeScript
- Example how to use TypeORM with JavaScript
- Example how to use TypeORM with JavaScript and Babel
- Example how to use TypeORM with TypeScript and SystemJS in Browser
- Example how to use Express and TypeORM
- Example how to use Koa and TypeORM
- Example how to use TypeORM with MongoDB
- Example how to use TypeORM in a Cordova/PhoneGap app
- Example how to use TypeORM with an Ionic app
- Example how to use TypeORM with React Native
- Example how to use TypeORM with Electron using JavaScript
- Example how to use TypeORM with Electron using TypeScript
擴展
這幾個擴展可以簡化TypeORM的使用,并將其與其他模塊集成:
- TypeORM + GraphQL framework
- TypeORM integration with TypeDI
- TypeORM integration with routing-controllers
- Models generation from existing database - typeorm-model-generator
轉載自:點擊查看文檔來源
留言
歡迎查看使用TypeORM 和 nestjs 實現的Mock Server 前端Mock接口和數據方案