【譯】Nodejs最好的ORM - TypeORM

分類:IT技術 時間:2017-02-28

TypeORM github: https://github.com/typeorm/typeorm
這篇譯文是從TypeORM github上的使用說明上翻譯過來的,已經提交PR並merge到庫中了。


TypeORM是一個采用TypeScript編寫的用於Node.js的優秀ORM框架,支持使用TypeScript或Javascript(ES5, ES6, ES7)開發。
目標是保持支持最新的javascript特性來幫助開發各種用到數據庫的應用 - 不管是輕應用還是企業級的。

TypeORM可以做到:

  • 根據Models自動創建數據庫Table
  • 可以透明的insert/update/delete數據庫對象
  • 映射數據庫table到javascript對象,映射table column到javascript對象屬性
  • 提供表的一對一,多對一,一對多,多對多關系處理
  • 還有更多 ...

不同於其他的JavaScript ORM,TypeORM使用的是數據映射模式,可以很輕松的創建出松耦合、可伸縮、可維護的應用。

TypeORM可以幫助開發者專註於業務邏輯,而不用過於擔心數據存儲的問題。

TypeORM參考了很多其他優秀ORM的實現, 比如 Hibernate, Doctrine 和 Entity Framework.

安裝

  1. 安裝TypeORM:

    npm install typeorm --save

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

    npm install reflect-metadata --save

    在應用裏全局引用一下:

    • 比如在app.ts的入口處 require("reflect-metadata")
  3. 安裝數據庫驅動:

    • mysqlMariaDB

      npm install mysql --save

    • Postgres

      npm install pg --save

    • SQLite

      npm install sqlite3 --save

    • Microsoft SQL Server

      npm install mssql --save

    • Oracle (experimental)

      npm install oracledb --save

    可以根據你的數據庫選擇安裝上面的任意一個.

    使用oracle驅動需要參考安裝說明:地址.

TypeScript配置

確保你的TypeScript編譯器的版本大於2.1,並且在tsconfig.json開啟下面設置:

"emitDecoratorMetadata": true,
"experimentalDecorators": true,

同時需要開啟編譯選項裏的lib下的es6或者從@typings安裝es6-shim

Node.js 版本

TypeORM在Node.JS 4.0或以上版本上測試通過。
如果在應用啟動過程中出錯可以嘗試升級node.js到最新版本。

在瀏覽器中使用WebSQL (試用)

TypeORM可以在瀏覽器環境中工作,並且試驗性的支持WebSQL
如果在瀏覽器環境中使用TypeORM需要使用 npm i typeorm-browser 來替代 typeorm.
更多相關可以參考這裏和這個例子.

快速開始

在TypeORM中,數據庫table都是從實體中創建。
所謂實體其實就是用裝飾器@Table裝飾的一個model。
可以直接從數據庫中得到包含數據的實體對象,並且可以通過實體進行數據庫表的insert/update/remove。
來看看這個model entity/Photo.ts:

export class Photo {
    id: number;
    name: string;
    description: string;
    fileName: string;
    views: number;
}

創建實體

現在把Model變成實體:

import {Table} from "typeorm";

@Table()
export class Photo {
    id: number;
    name: string;
    description: string;
    fileName: string;
    views: number;
    isPublished: boolean;
}

添加table列

已經有了一個table,每個table都有column.
現在來添加列.
可以使用裝飾器@Column來把model的屬性變成列:

import {Table, Column} from "typeorm";

@Table()
export class Photo {

    @Column()
    id: number;

    @Column()
    name: string;

    @Column()
    description: string;

    @Column()
    fileName: string;

    @Column()
    views: number;

    @Column()
    isPublished: boolean;
}

創建一個主鍵列

很好, 現在ORM馬上就可以在數據庫中生成這個photo表,不過還漏了一個,每個table都必須要有主鍵列,所以要加上它。
可以用@PrimaryColumn裝飾器來標記一個主鍵列。

import {Table, Column, PrimaryColumn} from "typeorm";

@Table()
export class Photo {

    @PrimaryColumn()
    id: number;

    @Column()
    name: string;

    @Column()
    description: string;

    @Column()
    fileName: string;

    @Column()
    views: number;

    @Column()
    isPublished: boolean;
}

創建自增長/自生成/順序化的列

如果你想創建自增長/自生成/順序化的列,需要把column的type改成integer並且給主鍵列加上一個屬性{ generated: true }

import {Table, Column, PrimaryColumn} from "typeorm";

@Table()
export class Photo {

    @PrimaryColumn("int", { generated: true })
    id: number;

    @Column()
    name: string;

    @Column()
    description: string;

    @Column()
    fileName: string;

    @Column()
    views: number;

    @Column()
    isPublished: boolean;
}

使用 @PrimaryGeneratedColumn 裝飾器

現在photo表的id可能自動生成自動增長,不過還是有點麻煩,這個一個很常見的功能,所以有一個專門的裝飾器@PrimaryGeneratedColumn來實現相同的功能。

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

@Table()
export class Photo {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @Column()
    description: string;

    @Column()
    fileName: string;

    @Column()
    views: number;

    @Column()
    isPublished: boolean;
}

自定義列的數據類型

接下來讓我們改一下列的數據類型。默認情況下,string類型的屬性會映射到數據庫裏varchar(255)的數據類型,number則會映射到類似於float/double這樣的數據類型(取決到是什麽數據庫)。
但是我們不想所有的列被限制在varchar或float之類,下面來改進:

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

@Table()
export class Photo {

    @PrimaryGeneratedColumn()
    id: number;

    @Column({
        length: 500
    })
    name: string;

    @Column("text")
    description: string;

    @Column()
    fileName: string;

    @Column("int")
    views: number;

    @Column()
    isPublished: boolean;
}

創建數據庫連接

現在實體已經有了,接下來創建app.ts並配置數據庫連接:

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

createConnection({
    driver: {
        type: "mysql",
        host: "localhost",
        port: 3306,
        username: "root",
        password: "admin",
        database: "test"
    },
    entities: [
        Photo
    ],
    autoSchemaSync: true,
}).then(connection => {
    // 這裏可以寫實體操作相關的代碼 
}).catch(error => console.log(error));

在例子裏使用的是mysql,你也可以選擇其他數據庫,只需要簡單修改driver選項裏的數據庫的類型就可以了,比如:
mysql, mariadb, postgres, sqlite, mssql or oracle.
同樣可以修改host, port, username, password 以及database等設置.

把Photo實體加到數據連接的實體列表中,所有需要在這個連接下使用的實體都必須加到這個列表中。

autoSchemaSync選項可以在應用啟動時確保你的實體和數據庫保持同步。

引用目錄下的所有實體

接下來我們可能會創建更多的實體並把它們一一加到配置當中。
不過這樣會比較麻煩,好在可以直接寫上實體的目錄,這樣這個目錄下的所有實體都可以在當前連接中被使用:

import {createConnection} from "typeorm";

createConnection({
    driver: {
        type: "mysql",
        host: "localhost",
        port: 3306,
        username: "root",
        password: "admin",
        database: "test"
    },
    entities: [
        __dirname + "/entity/*.js"
    ],
    autoSchemaSync: 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.entityManager
            .persist(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.entityManager.persist(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.entityManager.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.persist(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.findOneById(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: ", allPublishedPhotos);
    console.log("Photos count: ", allPublishedPhotos);

}).catch(error => console.log(error));

更新photo

現在來從數據庫中取出一個photo,修改並更新到數據庫。

import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";

createConnection(/*...*/).then(async connection => {

    /*...*/
    let photoToUpdate = await photoRepository.findOneById(1);
    photoToUpdate.name = "Me, my friends and polar bears";
    await photoRepository.persist(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.findOneById(1);
    await photoRepository.remove(photoToRemove);

}).catch(error => console.log(error));

這個id = 1的photo就在數據庫中被移除了。

一對一關系

來創建與另一個類的一對一關系。
新建PhotoMetadata.ts用來存photo的元信息。

import {Table, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn} from "typeorm";
import {Photo} from "./Photo";

@Table()
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) |                            |
| photo       | 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 = http://www.cnblogs.com/brookshi/p/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.persist(photo);

    // photo存完了,再存下photo的元信息
    await metadataRepository.persist(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 {Table, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn} from "typeorm";
import {Photo} from "./Photo";

@Table()
export class PhotoMetadata {

    /* ... 其他列 */

    @OneToOne(type => Photo, photo => photo.metadata)
    @JoinColumn()
    photo: Photo;
}
import {Table, Column, PrimaryGeneratedColumn, OneToOne} from "typeorm";
import {PhotoMetadata} from "./PhotoMetadata";

@Table()
export class Photo {

    /* ... 其他列 */

    @OneToOne(type => PhotoMetadata, photoMetadata =http://www.cnblogs.com/brookshi/p/> 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({
        alias: "photo",
        innerJoinAndSelect: {
            "metadata": "photo.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 photoRepository = connection.getRepository(Photo);
    let photos = await photoRepository.createQueryBuilder("photo")
            .innerJoinAndSelect("photo.metadata", "metadata")
            .getMany();


}).catch(error => console.log(error));

使用 cascade 選項來自動保存關系著的對象

上面要保存關系對象需要一個一個來保存,略顯麻煩。
如果我們需要當關系對象中的一個被保存後,另一個也同樣被保存,則可以使用cascade選項來做到。
稍微改下@OneToOne裝飾:

export class Photo {
    /// ... 其他列

    @OneToOne(type => PhotoMetadata, metadata =http://www.cnblogs.com/brookshi/p/> metadata.photo, {
        cascadeInsert: true,
        cascadeUpdate: true,
        cascadeRemove: true
    })
    metadata: PhotoMetadata;
}
  • cascadeInsert - 如果表中沒有關系中的metadata,則自動insert,即我們不需要再手動insert一個新的photoMetadata對象。
  • cascadeUpdate - 如果metadata有變化,則自動update。
  • cascadeRemove - 如果把photo裏的metadata移除了,也就是為空,則會自動remove表中的這條metadata數據。

使用cascadeInsert就可以不需要像上面那邊先存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 = http://www.cnblogs.com/brookshi/p/new PhotoMetadata();
    metadata.height = 640;
    metadata.width = 480;
    metadata.compressed = true;
    metadata.comment ="cybershoot";
    metadata.orientation = "portait";
    
    photo.metadata = http://www.cnblogs.com/brookshi/p/metadata; // 連接起來

    // 得到repository
    let photoRepository = connection.getRepository(Photo);

    // 存photo
    await photoRepository.persist(photo);
    // photo metadata也自動存上了
    console.log("Photo is saved, photo metadata is saved too.")

}).catch(error => console.log(error));

多對一/一對多關系

接下來顯示多對一/一對多關系。
假設一個photo會有一個author,並且每個author可以有很多photo。
先創建Author實體:

import {Table, Column, PrimaryGeneratedColumn, OneToMany, JoinColumn} from "typeorm";
import {Photo} from "./Photo";

@Table()
export class Author {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @OneToMany(type => Photo, photo => photo.author) // 備註:下面會為Photo創建author屬性
    photos: Photo[];
}

Author包含一個反向的關系,OneToMany總是反向的,並且總是與ManyToOne成對出現。

現在來為Photo加上關系擁有者。

import {Table, Column, PrimaryGeneratedColumn, ManyToOne} from "typeorm";
import {PhotoMetadata} from "./PhotoMetadata";
import {Author} from "./Author";

@Table()
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      |                            |
| author      | int(11)      | FOREIGN KEY                |
+-------------+--------------+----------------------------+

多對多關系

假設photo可以存在多個相冊中,並且相冊裏可以包含多個photo。
先創建一個Album

import {Table, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable} from "typeorm";

@Table()
export class Album {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @ManyToMany(type => Photo, photo => photo.albums, {  // 備註: 會在下面的Photo類裏添加"albums"屬性
        cascadeInsert: true, // 在添加Album時,會自動添加相冊裏的Photo
        cascadeUpdate: true, // 在更新Album時,會自動更新相冊裏的Photo 
        cascadeRemove: true  // 在移除Album時,會自動移除相冊裏的Photo
    })
    @JoinTable()
    photos: Photo[] = []; // 初始化個Photo數組
}

@JoinTable多對多關系擁有者必須指定的。

接著給Photo實體加個反向關系:

export class Photo {
    /// ... 其他列

    @ManyToMany(type => Album, album => album.photos, {
        cascadeInsert: true, // 在添加Album時,會自動添加相冊裏的Photo
        cascadeUpdate: true, // 在更新Album時,會自動更新相冊裏的Photo 
        cascadeRemove: true  // 在移除Album時,會自動移除相冊裏的Photo
    })
    albums: Album[] = []; // 初始化個Album數組
}

執行上面的代碼後會自動創建一個叫 album_photos_photo_albums聯接表:

+-------------+--------------+----------------------------+
|                album_photos_photo_albums                |
+-------------+--------------+----------------------------+
| album_id_1  | int(11)      | PRIMARY KEY FOREIGN KEY    |
| photo_id_2  | int(11)      | PRIMARY KEY FOREIGN KEY    |
+-------------+--------------+----------------------------+

記得把Album實體加到ConnectionOptions中:

const options: CreateConnectionOptions = {
    // ... 其他配置
    entities: [Photo, PhotoMetadata, Author, Album]
};

現在來往數據庫裏插入albums和photos

let connection = await createConnection(options);

// 創建兩個albums
let album1 = new Album();
album1.name = "Bears";

let album2 = new Album();
album2.name = "Me";

// 創建兩個photos
let photo1 = new Photo();
photo1.name = "Me and Bears";
photo1.description = "I am near polar bears";
photo1.filename = "photo-with-bears.jpg";
photo1.albums.push(album1);

let photo2 = new Photo();
photo2.name = "Me and Bears";
photo2.description = "I am near polar bears";
photo2.filename = "photo-with-bears.jpg";
photo2.albums.push(album2);

// 獲取Photo的repository
let photoRepository = connection.getRepository(Photo);

// 依次存儲photos,由於cascade,albums也同樣會自動存起來
await photoRepository.persist(photo1);
await photoRepository.persist(photo2);

console.log("Both photos have been saved");

使用QueryBuilder

可以利用QueryBuilder來構建一個非常復雜的查詢,例如:

let photoRepository = connection.getRepository(Photo);
let photos = await photoRepository
    .createQueryBuilder("photo") // 別名,必填項,用來指定本次查詢
    .innerJoinAndSelect("photo.metadata", "metadata")
    .leftJoinAndSelect("photo.albums", "albums")
    .where("photo.isPublished=true")
    .andWhere("(photo.name=:photoName OR photo.name=:bearName)")
    .orderBy("photo.id", "DESC")
    .setFirstResult(5)
    .setMaxResults(10)
    .setParameters({ photoName: "My", bearName: "Mishka" })
    .getMany();

這個查詢會查找已經published的,並且name是"My"或"Mishka",
得到的結果會從第5個開始(分頁偏移決定的),
並且只會得到10個結果(分頁每頁個數決定的),
所得結果是以id的倒序排序的,
Photo的albums是左聯接,photo的metadata是內聯接。

更多關於QueryBuilder可以查看這裏.



Tags: javascript require insert update 數據庫

文章來源:


ads
ads

相關文章
ads

相關文章

ad