# 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看還有沒有此問題)  
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.