# TypeORM
### 【官方】
中文官方(資料比英文官方舊):
https://typeorm.devjs.cn/
英文官方
https://typeorm.io/
參考文檔:
https://typeorm.biunav.com/en/connection-options.html#postgres-cockroachdb-connection-options
https://stackoverflow.com/questions/67659981/typeorm-create-method-is-extremely-slow
### 兩種資料對應資料庫模式【Active Record】&【Data Mapper】
#### Active Record 將方法寫在 entity內,適用於小、靈活的系統
#### Data Mapper 將方法寫在Repository,適用大型、維護量大的系統
https://hackmd.io/@LouisChen13/ByhUhn-1j
### 【Lock】
實作:
https://stackoverflow.com/questions/69012855/typeorm-database-lock-please-explain-how-to-use-database-lock-in-typeorm-using
### 【loadRelationIds】
遇過一種狀況是,message表與user表是多對一的關係,因此有設定關聯。當client端要修改某一條message時,我們使用messageID去message表去查找該筆資料,同時要確認該client是否為該message的擁有者:
`const checkUser = await userMessage.findOneBy({usermessageId})`
這樣找出來的資料會發現因為message entity中,message關聯到user是關聯整個user並不是手動記錄下userId在message表中,所以checkUser並不會response userId的欄位,那我們可能會想說把它relation關聯出來就有user的id了,那我們可以改寫成以下:
```
const checkUser = await userMessage.findOne({
relations:['User'],
where:{id:usermessageId}
})
```
這樣的結果是OK的,確實可以確認操作登入的client確實是該message所屬的user沒錯,但是如果為了要取得user的id而去把user關聯出來,且明明就是有設定關聯的,FK是存在於message表的,這樣不會太浪費效能?於是我們改寫成以下:
```
const toMessage = await this.userMessageRepository.findOne({
loadRelationIds: {
relations: ['toUser'],
disableMixedMap: true,
},
where: { id: userMessageId, toUser: { id: userId } },
});
```
loadRelationIds只會將關聯的userId load出來,且因為userId本來就是有FK是存在在userMessage的!
disableMixedMap的作用是會將撈回來的資料依照原定的階層排,若關掉的話,會直接將關聯的userId拉回跟usermessage同一個階層
### 【QueryRunner】
如果 RDBMS 支持連接池,則每個新的 QueryRunner 實例都從連接池中獲取一個連接。 對於不支持連接池的數據庫,跨數據源使用同一個連接。
連接池指就是,當我們連線資料庫時,都要耗費蠻多效能,連接池會將我們的連線先放在連接池中,需要用到時,會在從連接池中獲取連線,就不用再連接一次,QueryRunner的用法是每個queryRunner會建立獨立的連接實例,這樣就可以做到transaction?
### 【QueryBuilder】
TypeORM Repository已經寫好常用對資料庫新增、修改、搜尋(find說明文件)、刪除資料。
如果要更細部的建立Query,TypeORM提供Query Builder相關API,以API的方式去組SQL Query,比較彈性。
https://ithelp.ithome.com.tw/articles/10207509
QueryBuilder的一些用法
https://juejin.cn/post/7239135597478379576
### 【Unique column 和 @Unique的差別】
@Unique適用於整個entity,當然也可以多列的去約束其為unique。
https://stackoverflow.com/questions/63793417/typeorm-whats-difference-between-unique-decorator-and-unique-true-in-col
### 【Repository】
Repository是typeorm提供的已經幫我們包好的很多方法,點進去Repository就知道了
### 【關聯欄位】
關聯選項可參考:
https://orkhan.gitbook.io/typeorm/docs/relations
https://blog.errorbaker.tw/posts/minw/nest-js-migration/
#### [@ManyToOne()參數用法解說]
第一個參數是個函式,返回關聯的類
第二個參數創建雙向關係
https://blog.csdn.net/qq1195566313/article/details/128379605
#### [@ManyToMany()]
manyToMany的話
#### [@joinColumn用法]
@joinColumnu定義哪一側會顯示關聯欄位,所以正常狀況下,只有在ManyToOne那邊會有joinColumn,也可以自定義關聯欄位的名稱。
#### [Eager]
eager true會連同關聯的欄位的詳細資料一起找出來
### 【Entity】
設定entity時,正常會因為我們設定的型別,typeORM會去做隱式轉換,但因typeORM看不懂聯合型別,所以要特別使用decorator @Type(()=>Date)去轉換成正確的型別。
### 【Like】
Like 'asd%':查找以“asd”開頭的任何值
Like '%asd':查找以“asd”結尾的任何值
Like '%asd%':查找包含“asd”的任何值(在任何位置)
```
const posts = await postRepository.find({
where: { title: Like('%asd%') }
```
#### 【Find 選項】
查找的語法:
https://typeorm.biunav.com/zh/find-options.html#%E5%9F%BA%E7%A1%80%E9%80%89%E9%A1%B9
查找的選項設定:
https://hi-founder.com/p/typeorm-repository/
##### 【In、Not、MoreThanEual】
In用於查找是否在陣列裡,不是orm特有是SQL的東西,ORM可以照以下這樣寫:
const list = await this.listRepository.find({
where: { id: Array.isArray(id) ? In(id) : id },
relations,
});
Not用於查找不是在陣列裡的
FindOptionsRelations
### 【Migration】
使用cli去migration,要先編譯成js檔,可使用ts-node套件,直接使用ts檔(會先用你的tsconfig幫你編譯好,再去跑指令要跑的檔案)
若需要migration:run的話,需要透過以下指令告知datasource

https://typeorm.io/migrations#
以下方法會報錯
https://stackoverflow.com/questions/71625087/typeorm-migration-file-must-contain-a-typescript-javascript-code-and-export-a

### 【錦集】
Q1:使用create()資料庫沒東西?但有response回來,並且他有提示說create()這個方法不用await(他寫說'await' 對此運算式的類型沒有作用)?用save()資料庫就可成功寫入資料庫,不論是用new()或是直接存入的方法,都是create()不行,save()可以。
A1:create()的用法,是會自動幫你new一個實例出來,方便的寫法可以寫類似以下:
```
async creatUser(createUserDto:any){
const user = this.userRepository.create({createUserDto})
await this.userRepository.save(user)
return user //看要回傳什麼
}
```
Q2:updateListDTO中直接extends createListDTO的屬性,那我createListDTO的屬性不設定為唯獨(readonly)是可以的嗎?安全嗎?
A2:最好是不要,最好DTO就是指for前端送過來的東西,不要另外在controller裡修改,只能這樣的時候,也可以在API文件不要顯示此欄位。
Q3:通常DTO會把foreign key也寫進去嗎?(以前的作法有,但要思考一下真的要加嗎?可能要看整個schema?這次沒有加,因在body內不會接到currentUser的ID,我放在jwt裡面,因此input用到DTO的地方不需要)
A3:通常會由controller傳2個參數到service那邊,不用另外放在DTO裡面。
npx cli(創建migration)
https://stackoverflow.com/questions/72594414/npx-typeorm-migrationcreate-n-testdata-dosent-create-a-migration
使用migration方法
https://www.linkedin.com/pulse/nestjs-typeorm-migrations-nestor-iv%C3%A1n-scoles/
創建方法:
https://hackmd.io/@LouisChen13/ByhUhn-1j
Active Record VS Data Mapper
https://culttt.com/2014/06/18/whats-difference-active-record-data-mapper
Q:設定手動關聯(只記錄FK的ID)的缺點?
A:只手動記錄FK的話,對資料庫來說不算是真正的關聯,操作上,DB會去檢查新增資料時,若你新增的FK是沒有此FK的,就會出問題,在刪除欄位時,也會不能被刪除,也無法做到CASCADE。
ORM用法中,也會無法將關聯欄位搜尋出來(除非自己寫QueryBuilder 或執行RawSQL)
### 【問題】
Q1:另有看到有人使用mongoDB時,加密部分是寫在model裡面,這樣好處是? (0:17)
https://www.bilibili.com/video/BV1sJ411n7PE/?spm_id_from=333.337.search-card.all.click&vd_source=107ba761a291ed2746522372f562d92e
Q2:他還有一個蠻好的寫法是把password直接在model中寫selet:false就可以不管在‘取得單一使用者’‘取得所有使用者’都可以一起直接把password選擇不回傳?(可以對應到我的哪個部分嗎?)
Q4:直接從SQLite 看到的foreign key的欄位名稱跟return查出來的不一樣?(換成postgreSQL看還有沒有此問題)

