蔡比八寫後端(6) - TypeORM entity & column


Posted by TempuraEngineer on 2023-12-19

目錄


columns

entity的column對應的就是table的column,定義方式有裝飾器(下方使用)、schema 2種

  • entity column

    • @Column
    • 對應table中的一般column
  • primary columns

    • primary column是被設為primary key的column,每個entity都至少一個primary column

          -- 幫已存在的column新增約束
          -- uniqueLibraryName是約束的名稱,可隨意取
          ALTER TABLE libraries
          ADD CONSTRAINT uniqueLibraryName PRIMARY (name);
      
    • @PrimaryColumn()

      • primary key的column
    • @PrimaryGeneratedColumn()
      • primary key + auto-increment的column
  • special column

  • enum column

MySQL支援enum column,可以用在狀態、類型這類有選項的地方

當試著新增一筆資料時,若enum column輸入的值不在enum內,就會得到"Data truncated for column '欄位名城' at row n"的報錯

-- 修改已存在的column型別為enum
ALTER TABLE books
MODIFY status ENUM('ready', 'renting', 'missing');
  @Column("enum", {
    name: "status",
    nullable: true,
    enum: ["ready", "renting", "missing"],
  })
  status: "ready" | "renting" | "missing" | null;

其他還有spatial column(記錄位置資料)、set column(MySQL不支援)、simple-json(在前一篇示範過了,JSON column就是垃圾)之類的,但就不多介紹了


entity繼承 & 嵌入entity

這兩種方式都可以減少重複的column,但還是不一樣

繼承用在column名稱完全不一樣時(ex: name, phone),嵌入則用在前綴一樣時(ex: nameFirst, nameLast)


繼承

假設publisher、user都有phone、name這兩個欄位

先建立一個擁有共同的column的class

export abstract class FixedInfo {
  @Column("varchar", { name: "name", length: 100 })
  name: string;

  @Column("varchar", { name: "phone", nullable: true, length: 10 })
  phone: string | null;
}

然後extends

@Entity("publishers", { schema: "bookInfos" })
export class Publishers {
  @PrimaryGeneratedColumn({ type: "int", name: "id" })
  id: number;

  @OneToMany(() => Books, (books) => books.publisher)
  books: Books[];
}


export class Users extends FixedInfo {
  @PrimaryGeneratedColumn({ type: "int", name: "id" })
  id: number;

  @Column("int", { name: "neglect", default: () => "'0'" })
  neglect: number;

  @OneToMany(() => Books, (books) => books.rentTo2)
  books: Books[];
}


嵌入

先建立一個擁有共同的column的class

export abstract class FixedInfo {
  @Column("varchar", { name: "name", length: 100 })
  name: string;

  @Column("varchar", { name: "phone", nullable: true, length: 10 })
  phone: string | null;
}

然後connect

注意,如果column名完全不一樣,千萬不要用這個方式,不只得不到要的資料,還會把你的table搞得一團亂

得不到要的資料
table被搞得一團亂

如果只是前綴不一樣記得要設定 {prefix:false},設定完後這個column會被當成是底下有name和phone

import { FixedInfo } from "./FixedInfo"

export class Users {
  @PrimaryGeneratedColumn({ type: "int", name: "id" })
  id: number;

  // 有設定prefix: false的話,就會當成是name這個欄位底下有name和phone
  @Column(() => FixedInfo, {prefix:false})
  name: FixedInfo;

  @Column("int", { name: "neglect", default: () => "'0'" })
  neglect: number;

  @OneToMany(() => Books, (books) => books.rentTo2)
  books: Books[];
}


view & view entity

view

view是MySQL query的結果(檢視表),長得就像table,但真的存在於資料庫

真的存在資料庫的是Materialized View。它可以用來做暫存,但到目前的MySQL 8.0都不支援(雖然有方法可以做出類似的效果)

使用view的優點如下

  1. 簡化查詢複雜度,加快開發速度(ex: 每週都會撈每個圖書館的借閱統計,只要做好view就不用每次都打重複的sql語法了)

     CREATE VIEW view AS (SELECT books.id, books.name
     FROM books);
    
  2. 加強資料庫的安全性(可以根據不同權限給予看到的view)

  3. 資料表變更結構時只需更改 view 的語法

       ALTER VIEW view (id, bookName, price) AS 
       (SELECT id, name, price
       FROM books 
       ORDER BY price DESC);
    

但是view也是有缺點的

  1. 不是所有view都可以update。涉及多個table、有子查詢、有用到聚合函數、distinct、group、having、union的都無法update,因為它們屬於TEMPTABLE演算法
    • TEMPTABLE演算法是建立view時的三個可選演算法之一,預設是UNDEFINED。當設為UNDEFINED時MySQL會自己選要用TEMPTABLE或MERGE
  2. view本身沒有資料,因此所有對view進行資料操作都會體現在原table中,所以千萬不要隨意update view


view entity

view entity是一個class,它對應DB的view,當DB裏沒有對應的view,就會自己建立一個

可以直接寫SQL語法

@ViewEntity({
    expression: `
        SELECT "post"."id" AS "id", "post"."name" AS "name", "category"."name" AS "categoryName"
        FROM "post" "post"
        LEFT JOIN "category" "category" ON "post"."categoryId" = "category"."id"
    `
})

也可以使用TypeORM的抽象工具

@ViewEntity({
    expression: (dataSource: DataSource) => dataSource
        .createQueryBuilder()
        .select("post.id", "id")
        .addSelect("post.name", "name")
        .addSelect("category.name", "categoryName")
        .from(Post, "post")
        .leftJoin(Category, "category", "category.id = post.categoryId")
})

但是TypeORM是無法直接query view的

How to query a view instead of a table?


參考資料

Embedded Entities
Entity Inheritance
提升服務效能、減輕DB負擔!(2): Materialized View
View 視圖


#TypeORM #View #column #Entity







Related Posts

從 SessionStorage 開始一場 spec 之旅

從 SessionStorage 開始一場 spec 之旅

MTR04_0706

MTR04_0706

There's no hierarchy of pain

There's no hierarchy of pain


Comments