# 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://i.imgur.com/l0aP7fY.png) https://typeorm.io/migrations# 以下方法會報錯 https://stackoverflow.com/questions/71625087/typeorm-migration-file-must-contain-a-typescript-javascript-code-and-export-a ![](https://i.imgur.com/g4VQP0P.png) ### 【錦集】 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看還有沒有此問題) ![](https://i.imgur.com/Vdsktz3.png) ![](https://i.imgur.com/itlSHzR.png)