特性
- 默認(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 表了
特色
總結(jié)
migration 數(shù)據(jù)遷移
entity 實(shí)體
- 用類和對象操作數(shù)據(jù)表和數(shù)據(jù)行
connection 連接
- 一個數(shù)據(jù)庫連接,默認(rèn)最多 10 個連接
- 這種模式也叫做連接池最盅,可以參考這篇文章
manager / repo
- 兩種 API 封裝風(fēng)格突雪,用于操作 entity