使用 TypeORM

特性

  • 默認(rèn)支持 TypeScript
  • 我們來打算用 Sequelize.js峭咒,發(fā)現(xiàn)他 對 TS 支持不夠好
  • 支持關(guān)聯(lián)(Associations)
  • 支持事務(wù)(Transaction)
  • 支持?jǐn)?shù)據(jù)庫遷移(Migration)

啟動數(shù)據(jù)庫 postgresql

新版 docker(額外)

  • 在項(xiàng)目目錄中創(chuàng)建 blog-data 目錄
  • .gitignore 里添加 /blog-data/

啟動 PostgreSQL

  • 一句話啟動
  • 新版: docker run -v "$PWD/blog-data":/var/lib/postgresql/data -p 5432:5432 -e POSTGRES_USER=blog -e POSTGRES_HOST_AUTH_METHOD=trust -d postgres:12.2
  • 舊版: docker run -v "blog-data":/var/lib/postgresql/data -p 5432:5432 -e POSTGRES_USER=blog -e POSTGRES_HOST_AUTH_METHOD=trust -d postgres:12.2
  • docker ps -a 這句話可以查看容器的運(yùn)行狀態(tài)
  • docker logs 容器id 這句話可以查看啟動日志

驗(yàn)證 pg

進(jìn)入 docker 容器

  • docker exec -it 容器id bash

進(jìn)入 pg 命令行

  • psql -U blog -W
  • 由于上面沒有設(shè)置密碼赘方,所以直接回車即可
  • 如果需要密碼快集,可在docker run 選項(xiàng)里的 -e POSTGRES_HOST_AUTH_METHOD=trust 替換成 -e POSTGRES_PASSWORD=123456

一些簡單的命令

  • \l 用于 list databases每瞒,目前有一個 blog 數(shù)據(jù)庫
  • \c 用于 connect to a database
  • \d 用于 display
  • \dt 用于 display tables孤澎,目前還沒有

創(chuàng)建數(shù)據(jù)庫

用 SQL 來創(chuàng)建數(shù)據(jù)庫

  • CREATE DATABASE xxx ENCODING 'UTF8' LC_COLLATE 'en_US.utf8' LC_CTYPE 'en_US.utf8';
  • 因?yàn)?Type ORM 沒有提供單純創(chuàng)建數(shù)據(jù)庫的 API
  • 創(chuàng)建三個數(shù)據(jù)庫:開發(fā)、測試条摸、生產(chǎn)
  • 對應(yīng)英文 blog_development悦污、blog_test、 blog_production
  • 得到三個數(shù)據(jù)庫

安裝 TypeORM

  • 打開官網(wǎng)屈溉,點(diǎn)擊 Getting Started
  • 安裝該安裝的依賴(typeorm reflect-metadata @types/node pg)
  • 不要使用 Quick Start 里面的 typeorm init 命令,因?yàn)樗麄兏膶懩愕默F(xiàn)有項(xiàng)目的文件(后面自己改)
  • 在 tsconfig.json 中加入 "emitDecoratorMetadata": true, "experimentalDecorators": true, 并更改成 "module": "commonjs"
  • 創(chuàng)建 ormconfig.json抬探,并加入內(nèi)容(官網(wǎng)上有)

運(yùn)行 TypeORM

吐槽

  • Next.js 默認(rèn)使用 babel 來將 TS 編譯為JS(內(nèi)置功能)
  • TypeORM 推薦使用 ts-node 來編譯(沒有內(nèi)置)
  • babel 和 ts-node 對 TS 的支持并非完全一致
  • 所以我們必須進(jìn)行統(tǒng)一子巾,全部都用 babel

安裝 babel

  • 安裝 @babel/cli
  • 創(chuàng)建 src/index.ts
// index.ts
import "reflect-metadata";
import {createConnection} from 'typeorm';

createConnection().then(async connection => {
  console.log(connection)
  await connection.close()
}).catch(error => console.log(error));
  • npx babel ./src --out-dir dist --extensions ".ts,.tsx"
  • node dist/index.js
  • 控制臺成功打印出 connection 對象,連接數(shù)據(jù)庫成功小压!

此時項(xiàng)目運(yùn)行流程

  • 統(tǒng)一讓 Next.js 和 TypeORM 使用 babel 翻譯 TS
  • 每次修改 src 的 TS 代碼后线梗,翻譯為 dist 里的 JS
  • 使用 node 運(yùn)行 dist 里的 JS,執(zhí)行 TypeORM 任務(wù)
  • 也可使用 Next.js 執(zhí)行 TypeORM 任務(wù)(后面弄)

重要配置:禁用 sync

ormconfig

  • "synchronize": true => false
  • 如果為 true怠益,那么在連接數(shù)據(jù)庫時仪搔,typeorm 會自動根據(jù) entity 目錄來修改數(shù)據(jù)表
  • 假設(shè) entity 里面有 User,就會自動創(chuàng)建 User 表

看起來很方便蜻牢,為什么要禁用

  • 因?yàn)?sync 功能可能會在我們修改User 時直接刪除數(shù)據(jù)
  • 假設(shè)你把 user 表中的 name 字段改為了 nickname烤咧,他可能會誤解你刪除了 name偏陪,并新增了 nickname,此時表中 name 數(shù)據(jù)已經(jīng)丟失
  • 這種行為絕對不能發(fā)生在生產(chǎn)環(huán)境
  • 所以我們要一開始就杜絕 sync 功能

創(chuàng)建表

posts表

"cli": {
  "migrationsDir": "src/migration"
}
  • npx typeorm migration:create -n CreatePosts
  • 在新生成的文件中 up 方法代表升級數(shù)據(jù)庫笛谦,down 代表降級數(shù)據(jù)庫
import {MigrationInterface, QueryRunner, Table} from 'typeorm';

export class CreatePosts1595341120888 implements MigrationInterface {

    public async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.createTable(new Table({
            name: 'posts',
            columns: [
                {name: 'id', isGenerated: true, type: 'int', isPrimary: true, generationStrategy: "increment"},
                {name: 'title', type: 'varchar'},
                {name: 'content', type: 'text'},
                {name: 'author_id', type:'int'}
            ]
        }))
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.dropTable('posts')
    }

}

  • npx babel ./src --out-dir dist --extensions ".ts,.tsx"
  • 由于我們使用 babel,與 TypeORM 官方建議用的 ts-node 不一樣昌阿,所以我們還需要修改 ormconfig.json 文件饥脑,把 entities、migrations懦冰、subscribers灶轰,里面的路徑都替換為 dist/xxx/*/.js
  "entities": [
    "dist/entity/**/*.js"
  ],
  "migrations": [
    "dist/migration/**/*.js"
  ],
  "subscribers": [
    "dist/subscriber/**/*.js"
  ],
  • npx typeorm migration:run
  • 運(yùn)行成功
  • 我們就可以看到數(shù)據(jù)庫中已經(jīng)有 posts 表了

每次都要運(yùn)行 babel 不傻嗎?

  • npx babel --help 可以看到有 -w 選項(xiàng)
  • 這樣我們每次更改文件 babel 就會自動編譯
  • 但是此時我們需要開三個窗口來運(yùn)行我們的項(xiàng)目刷钢,第一跑 next dev笋颤,第二個跑 babel,第三個輸入當(dāng)前命令
  • 所以有沒有辦法讓第一個窗口和第二個窗口合并呢闯捎?
  • Linux / Mac 用戶直接使用 & 即可椰弊, next dev & babel -w ....
  • 但是 Windows 不支持(&& 的意思是如果前一個命令成功了,就執(zhí)行下一個命令瓤鼻,此時不適用)
  • 通過搜索關(guān)鍵詞 npm run tasks in paraller
  • 發(fā)現(xiàn) concurrently 可以代替 & 操作秉版,安裝根據(jù)文檔操作即可

數(shù)據(jù)映射到實(shí)體

背景

  • 剛剛只是在數(shù)據(jù)庫里創(chuàng)建了 posts,代碼如何讀寫 posts 呢茬祷?
  • 答案:將數(shù)據(jù)映射到 Entity(實(shí)體)
  • 和 migration 一樣首先在 ormconfig 中加入以下代碼清焕,控制文件生成的目錄
"cli": {
  "entitiesDir": "src/entity",
}
  • npx typeorm entity:create -n Post
import {Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn} from 'typeorm';

@Entity('posts')
export class Post {
  @PrimaryGeneratedColumn('increment')
  id: number;
  @Column('varchar')
  title: string;
  @Column('text')
  content: string;
  @Column('int')
  authorId: number;
  @CreateDateColumn()
  createdAt: Date;
  @UpdateDateColumn()
  updatedAt: Date;
}

  • 編譯時遇到一個報錯 syntax 'decorators-legacy'
  • 搜索以后,安裝 yarn add -D @babel/plugin-proposal-decorators
  • 根據(jù) Next.js 的要求祭犯,新建 .babelrc 文件秸妥,并加入上面安裝的插件
{
  "presets": [
    "next/babel"
  ],
  "plugins": [
    [
      "@babel/plugin-proposal-decorators",
      {
        "legacy": true
      }
    ]
  ]
}

知識點(diǎn)

  • @PrimaryGeneratedColumn('increment') // 自增主鍵
  • @Column('varchar') // varchar 類型
  • @Column('text') // text 類型

如何使用實(shí)體

EntityManager API

舉例

  • await manager.find(Post, { title: '第一篇博客' })
  • await manager.create(Post, { title: '.....' })
  • await manager.save(post1)
  • await manager.save([post1, post2, post3])
  • await manager.remove(post1)
  • await manager.update(Post, 1, { title: '修改后的標(biāo)題' })
  • await manager.delete(Post, 1)
  • await manager.findOne(Post, 1)

封裝思路

  • 把所有操作都放在 manager 上
  • 把 Post 類沃粗、post1 對象和其他參數(shù)傳給 manager

Repository API

舉例

  • const postRepository = getRepository(Post)
  • await postRepository .findOne(1)
  • await postRepository .save(post)

封裝思路

  • 先通過Post 構(gòu)造一個 repo 對象
  • 這個 repo 對象就只操作 posts 表了

特色

  • TreeRepository 和 MongoRepository
  • 目前用不到這兩個功能粥惧,所以就先不用Repo API

總結(jié)

migration 數(shù)據(jù)遷移

  • 用來對數(shù)據(jù)庫升級和降級

entity 實(shí)體

  • 用類和對象操作數(shù)據(jù)表和數(shù)據(jù)行

connection 連接

  • 一個數(shù)據(jù)庫連接,默認(rèn)最多 10 個連接
  • 這種模式也叫做連接池最盅,可以參考這篇文章

manager / repo

  • 兩種 API 封裝風(fēng)格突雪,用于操作 entity
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市涡贱,隨后出現(xiàn)的幾起案子咏删,更是在濱河造成了極大的恐慌,老刑警劉巖问词,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件督函,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)辰狡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門锋叨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人搓译,你說我怎么就攤上這事悲柱。” “怎么了些己?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵豌鸡,是天一觀的道長。 經(jīng)常有香客問我段标,道長涯冠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任逼庞,我火速辦了婚禮蛇更,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赛糟。我一直安慰自己派任,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布璧南。 她就那樣靜靜地躺著掌逛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪司倚。 梳的紋絲不亂的頭發(fā)上豆混,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天,我揣著相機(jī)與錄音动知,去河邊找鬼皿伺。 笑死,一個胖子當(dāng)著我的面吹牛盒粮,可吹牛的內(nèi)容都是我干的鸵鸥。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼丹皱,長吁一口氣:“原來是場噩夢啊……” “哼妒穴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起种呐,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤宰翅,失蹤者是張志新(化名)和其女友劉穎弃甥,沒想到半個月后爽室,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年阔墩,在試婚紗的時候發(fā)現(xiàn)自己被綠了嘿架。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡啸箫,死狀恐怖耸彪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情忘苛,我是刑警寧澤蝉娜,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站扎唾,受9級特大地震影響召川,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜胸遇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一荧呐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纸镊,春花似錦倍阐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至庵楷,卻和暖如春罢艾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背尽纽。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工咐蚯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人弄贿。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓春锋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親差凹。 傳聞我的和親對象是個殘疾皇子期奔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348