主要遇到的問題

原專案是使用 Rails 開發,但因總總原因,決定改用 Node.js 開發,但在轉換時,遇到了一些問題,其中就有 Sequelize 的 Update 與 Set 的差異。

使用 Rails 所提供的 ORM 有跟很方便的方法,就是可以把 column 設定為 json 或是 text,在寫入與讀取時,ORM 會自動幫你處理,可以很方便的讀寫 json 裡的資料。但在 Node.js 使用 Sequelize 時,就沒有這麼方便了。

Sequelize 的做法,不管是讀取 json 或是 yaml 是必都必須有個 Parser 與 Loader 把這些資料轉成 Attribute。

範例而言:

(這只是範例,一般來說應該要照著 RDBMS 的規範來設計資料表)

const { Sequelize, DataTypes } = require("sequelize");
const sequelize = new Sequelize("sqlite::memory:");
const User = sequelize.define("User", {
  // JSON
  data: {
    type: DataTypes.JSON,
    allowNull: false,
    defaultValue: {},
  },
  age: {
    type: DataTypes.VIRTUAL,
    get() {
      return jsonLoader(this.getDataValue("data"), 'age');
    },
    set(value) {
      const data = this.getDataValue("data");
      jsonParser(data, { age: value });
    },
  },
  sex: {
    type: DataTypes.VIRTUAL,
    get() {
      return jsonLoader(this.getDataValue("data"), 'sex').
    },
    set(value) {
      const data = this.getDataValue("data");
      jsonParser(data, { sex: value });
    },
  }
});

在這個狀況下,如果使用 Update 進行更新某個欄位:

// 原本 User: { data: { age: 18, sex: 'male' } }

await User.update({ age: 20 }, { where: { id: 1 } });

最後在 DB 所看到的資料是:

{
  "age": 20
}

問題出在於 Update 沒有把整個資料拿出來,所以在更新時 this.getDataValue("data") 是空的。

Set 的做法是:

const user = await User.findByPk(1);
user.set({ age: 20 }).save();

因為已經把整個資料給讀取出來,所以 this.getDataValue("data") 是有資料的,這樣就可以正確的更新資料了。

結論

其實在 RDBMS 使用上,還是盡量依照規範來設計資料表,不要把 json 或是 yaml 放在一個欄位裡,這樣會讓資料難以維護,也不利於查詢。但如果真的需要使用 json 或是 yaml,就要注意在更新時,要把整個資料拿出來,不然會有資料遺但如果真的需要使用 json 或是 yaml,就要注意在更新時,要把整個資料拿出來,不然會有資料遺失的問題。