typeorm數據庫ORM框架中文文檔 js,node,typescript

TypeORM是一個ORM框架,它能夠運行在NodeJS、瀏覽器、Cordova、PhoneGap、Ionic、React Native、Expo和Electron平臺上,能夠與TypeScript和JavaScript (ES5, ES6, ES7)一塊兒使用。 它的目標是始終支持最新的JavaScript特性並提供額外的特性以幫助你開發任何使用數據庫的應用程序 —— 不論是隻有幾張表的小型應用仍是擁有多數據庫的大型企業應用。javascript

不一樣於現有的全部其餘JavaScript ORM框架,TypeORM支持Active Record和Data Mapper模式,這意味着你用最有效的方法編寫高質量的、鬆耦合的、可擴展的、可維護的應用程序。前端

TypeORM參考了不少其餘優秀ORM的實現, 好比 Hibernate, DoctrineEntity Framework.java

TypeORM 的一些特性:node

  • 支持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
  • 產生出高性能、靈活、清晰和可維護的代碼
  • 遵循全部可能的最佳實踐
  • 命令行工具

還有更多...mysql

使用TypeORM你的模型是這樣的:react

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

@Entity()
export class User {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    firstName: string;

    @Column()
    lastName: string;

    @Column()
    age: number;

}
複製代碼

你的域邏輯是這樣的:git

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」實現,你也可使用它:es6

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;

}
複製代碼

你的域邏輯是這樣的:github

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();
複製代碼

請注意

這個文檔可能不是最新的。 能夠去官網查看最新的英文文檔。 很是歡迎你的貢獻。sql

安裝

  1. 安裝TypeORM:

    npm install typeorm --save

  2. 須要安裝依賴模塊 reflect-metadata :

    npm install reflect-metadata --save

    在應用裏全局引用一下:

    • 好比在app.ts的入口處 require("reflect-metadata")
  3. 你可能須要安裝node類型:

    npm install @types/node --save

  4. 安裝數據庫驅動:

    • MySQLMariaDB

      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是你將使用的數據庫。數據庫能夠是下列值之一:mysqlmariadbpostgressqlitemssqloraclemongodbcordovareact-nativeexpo

該命令將在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"
   ]
}
複製代碼

一般來講,大多數時候你只須要配置hostusernamepassworddatabase 或者 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;
}
複製代碼

如今 idnamedescriptionfilenameviewsisPublished 列將會被添加 photo 表。 數據庫中的列類型是從你使用的屬性類型推斷出來的,例如:number 將會被轉成 integerstring 轉爲 varcharboolean 轉爲 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這裏.

樣例

看看樣例裏這些例子的用法

這些倉庫,你能夠克隆下來幫助你開始:

擴展

這幾個擴展能夠簡化TypeORM的使用,並將其與其餘模塊集成:

轉載自:點擊查看文檔來源

留言

歡迎查看使用TypeORM 和 nestjs 實現的Mock Server 前端Mock接口和數據方案

相關文章
相關標籤/搜索