0

Lý thuyết Sequelize (Phần 1)

Trước khi đọc bài viết thì mình muốn mọi người biết rằng đây chỉ là bài dịch ngẫu hứng trong thời gian rảnh. Nếu các bạn thấy đọc khó hiểu thì hãy vào bài viết gốc của docs trên trang sequelize ở đường link này. Phiên bản mình dịch là Sequelize V6 ]

Lý thuyết Sequelize (phần I)

Lý thuyết cơ bản về model

I/ Khái niệm

Model chính là class đại diện cho bảng trong database. Để khai báo được bảng thì cần có những KIỂU DỮ LIỆU. Trong javascript thì không có những thứ đó nên Sequelize có hỗ trợ cho nhiều loại kiểu dữ liệu.

Sequelize hỗ trợ tạo sẵn bảng với dạng số nhiều của tên model. Ví dụ Model tên là User thì bảng trong database sẽ tên là Users. Và nếu bảng là Person thì Sequelize cũng sẽ tự hiểu dạng số nhiều là People nhờ vào thư viện inflection

Tên Model và tên bảng đều có thể thay đổi được.

II/ Cách khai báo Model

Sau đây là code mẫu sử dụng cả 2 cách sequelize và thừa hưởng class

  1. Sử dụng sequelize.define:
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:');

const User = sequelize.define('User', {
  // Model attributes are defined here
  firstName: {
    type: DataTypes.STRING,
    allowNull: false
  },
  lastName: {
    type: DataTypes.STRING
    // allowNull defaults to true
  }
}, {
  // Other model options go here
});

// `sequelize.define` also returns the model
console.log(User === sequelize.models.User); // true
  1. Sử dụng thừa hưởng class
const { Sequelize, DataTypes, Model } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:');

class User extends Model {}

User.init({
  // Model attributes are defined here
  firstName: {
    type: DataTypes.STRING,
    allowNull: false
  },
  lastName: {
    type: DataTypes.STRING
    // allowNull defaults to true
  }
}, {
  // Other model options go here
  sequelize, // We need to pass the connection instance
  modelName: 'User' // We need to choose the model name
});

// the defined model is the class itself
console.log(User === sequelize.models.User); // true

III/ Cẩn thận code shadowing

Nếu như sử dụng phương pháp thứ 2 là thừa hưởng class thì không nên khai báo trùng thuộc tính trong class mà model thừa hưởng.

Ví dụ không nên: (class Model đã có thuộc tính id, class User khai báo thêm thuộc tính id sẽ bị viết đè lên và mất setter getter)

// Invalid
class User extends Model {
  id; // this field will shadow sequelize's getter & setter. It should be removed.
  otherPublicField; // this field does not shadow anything. It is fine.
}

User.init({
  id: {
    type: DataTypes.INTEGER,
    autoIncrement: true,
    primaryKey: true
  }
}, { sequelize });

const user = new User({ id: 1 });
user.id; // undefined

Ví dụ nên:

// Valid
class User extends Model {
  otherPublicField;
}

User.init({
  id: {
    type: DataTypes.INTEGER,
    autoIncrement: true,
    primaryKey: true
  }
}, { sequelize });

const user = new User({ id: 1 });
user.id; // 1

IV/ Thay đổi tên bảng

  1. Bắt buộc tên bảng y chang tên model:

Bằng cách sử dụng option freezeTableName: true

Ví dụ:

sequelize.define('User', {
  // ... (attributes)
}, {
  freezeTableName: true
});

Hoặc khai báo lựa chọn này khi khai báo sequelize ở đầu file

const sequelize = new Sequelize('sqlite::memory:', {
  define: {
    freezeTableName: true
  }
});
  1. Cung cấp tên bảng trực tiếp

Cung cấp tên bảng khi khai báo model:

sequelize.define('User', {
  // ... (attributes)
}, {
  tableName: 'Employees'
});

V/ Đồng bộ model

  1. Đồng bộ từng cái

Khi định nghĩa một model, tức là bạn đang nói cho Sequelize một vài điều về bảng của nó trong cơ sở dữ liệu. Tuy nhiên, nếu bảng thực sự không tồn tại ? Nếu nó tồn tại, nhưng nó có các cột khác nhau, ít cột hơn hoặc bất kỳ sự khác biệt nào?

Đây là lúc đồng bộ hóa model bước vào. Một model có thể được đồng bộ hóa với cơ sở dữ liệu bằng cách gọi model.sync(options), một hàm không đồng bộ (trả về một Promise ). Với lệnh gọi này, Sequelize sẽ tự động thực hiện truy vấn SQL tới cơ sở dữ liệu. Lưu ý rằng điều này chỉ thay đổi bảng trong cơ sở dữ liệu, không thay đổi model ở phía JavaScript.

  • User.sync() - Tạo bảng nếu nó không tồn tại (và không làm gì nếu nó đã tồn tại)
  • User.sync({ force: true }) - Tạo bảng, xóa bảng nếu bảng đã tồn tại rồi tạo bảng
  • User.sync({ alter: true }) - Kiểm tra trạng thái hiện tại của bảng trong cơ sở dữ liệu (cột nào tồn tại, kiểu dữ liệu của chúng, v.v.), sau đó thực hiện các thay đổi cần thiết trong bảng để khiến bảng phù hợp với model.

Ví dụ:

await User.sync({ force: true });
console.log("The table for the User model was just (re)created!");
  1. Đồng bộ tất cả

Bằng cách sử dụng lệnh sequelize.sync() để tự động đồng bộ tất cả model. Ví dụ:

await sequelize.sync({ force: true });
console.log("All models were synchronized successfully.");

VI/ Drop bảng

Để có thể drop bảng có liên hệ với lại một model ta chạy lệnh .drop():

await User.drop();
console.log("User table dropped!");

hoặc drop toàn bộ bảng ta chạy:

await sequelize.drop();
console.log("All tables dropped!");

Tuy nhiên drop bảng hoặc đồng bộ bằng phương pháp force, alter là KHÔNG KHUYẾN CÁO trong PRODUCTION.

VII/ Timestamps

Mặc định, sequelize sẽ thêm vào hai cột createAtupdateAt cho từng model với loại dữ liệu là DataTypes.DATE.

Chú ý: Những field này được cập nhật tự động bởi Sequelize. Do đó tất cả những cập nhật sử dụng Queries thường sẽ không kích hoạt tác dụng này. Mà phải cập nhật sử dụng hàm có sẵn của Sequelize.

Hành vi này có thể được tắt đi bởi lệnh:

sequelize.define('User', {
  // ... (attributes)
}, {
  timestamps: false
});

Hoặc là ta cũng có thể enable thằng nào mà mình thích bằng lệnh:

class Foo extends Model {}
Foo.init({ /* attributes */ }, {
  sequelize,

  // don't forget to enable timestamps!
  timestamps: true,

  // I don't want createdAt
  createdAt: false,

  // I want updatedAt to actually be called updateTimestamp
  updatedAt: 'updateTimestamp'
});

VIII/ Cú pháp ngắn để định nghĩa cột

Nếu như chỉ muốn định nghĩa type của một cột thì ta có thể viết:

// This:
sequelize.define('User', {
  name: {
    type: DataTypes.STRING
  }
});

// Can be simplified to:
sequelize.define('User', { name: DataTypes.STRING });

IX/ Giá trị mặc định của một cột

Mặc định sequelize sẽ coi giá trị mặc định là NULL nhưng ta có thể cấu hình giá trị mặc định:

sequelize.define('User', {
  name: {
    type: DataTypes.STRING,
    defaultValue: "John Doe"
  }
});

X/ Các kiểu dữ liệu

Sequelize hỗ trợ nhiều kiểu dữ liệu xài chung lẫn độc quyền cho từng database. Các kiểu dữ liệu độc quyền xem tại đây.

Ngoài ra các kiểu dữ liệu xài chung:

String

DataTypes.STRING             // VARCHAR(255)
DataTypes.STRING(1234)       // VARCHAR(1234)
DataTypes.STRING.BINARY      // VARCHAR BINARY
DataTypes.TEXT               // TEXT
DataTypes.TEXT('tiny')       // TINYTEXT
DataTypes.CITEXT             // CITEXT          PostgreSQL and SQLite only.
DataTypes.TSVECTOR           // TSVECTOR        PostgreSQL only.

Boolean

DataTypes.BOOLEAN            // TINYINT(1)

Numbers

DataTypes.INTEGER            // INTEGER
DataTypes.BIGINT             // BIGINT
DataTypes.BIGINT(11)         // BIGINT(11)

DataTypes.FLOAT              // FLOAT
DataTypes.FLOAT(11)          // FLOAT(11)
DataTypes.FLOAT(11, 10)      // FLOAT(11,10)

DataTypes.REAL               // REAL            PostgreSQL only.
DataTypes.REAL(11)           // REAL(11)        PostgreSQL only.
DataTypes.REAL(11, 12)       // REAL(11,12)     PostgreSQL only.

DataTypes.DOUBLE             // DOUBLE
DataTypes.DOUBLE(11)         // DOUBLE(11)
DataTypes.DOUBLE(11, 10)     // DOUBLE(11,10)

DataTypes.DECIMAL            // DECIMAL
DataTypes.DECIMAL(10, 2)     // DECIMAL(10,2)

DataTypes.INTEGER.UNSIGNED
DataTypes.INTEGER.ZEROFILL
DataTypes.INTEGER.UNSIGNED.ZEROFILL
// You can also specify the size i.e. INTEGER(10) instead of simply INTEGER
// Same for BIGINT, FLOAT and DOUBLE

Dates

DataTypes.DATE       // DATETIME for mysql / sqlite, TIMESTAMP WITH TIME ZONE for postgres
DataTypes.DATE(6)    // DATETIME(6) for mysql 5.6.4+. Fractional seconds support with up to 6 digits of precision
DataTypes.DATEONLY   // DATE without time

UUIDs

{
  type: DataTypes.UUID,
  defaultValue: DataTypes.UUIDV4 // Or DataTypes.UUIDV1
}

XI/ Các options khác của cột

const { Model, DataTypes, Deferrable } = require("sequelize");

class Foo extends Model {}
Foo.init({
  // instantiating will automatically set the flag to true if not set
  flag: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true },

  // default values for dates => current time
  myDate: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },

  // setting allowNull to false will add NOT NULL to the column, which means an error will be
  // thrown from the DB when the query is executed if the column is null. If you want to check that a value
  // is not null before querying the DB, look at the validations section below.
  title: { type: DataTypes.STRING, allowNull: false },

  // Creating two objects with the same value will throw an error. The unique property can be either a
  // boolean, or a string. If you provide the same string for multiple columns, they will form a
  // composite unique key.
  uniqueOne: { type: DataTypes.STRING,  unique: 'compositeIndex' },
  uniqueTwo: { type: DataTypes.INTEGER, unique: 'compositeIndex' },

  // The unique property is simply a shorthand to create a unique constraint.
  someUnique: { type: DataTypes.STRING, unique: true },

  // Go on reading for further information about primary keys
  identifier: { type: DataTypes.STRING, primaryKey: true },

  // autoIncrement can be used to create auto_incrementing integer columns
  incrementMe: { type: DataTypes.INTEGER, autoIncrement: true },

  // You can specify a custom column name via the 'field' attribute:
  fieldWithUnderscores: { type: DataTypes.STRING, field: 'field_with_underscores' },

  // It is possible to create foreign keys:
  bar_id: {
    type: DataTypes.INTEGER,

    references: {
      // This is a reference to another model
      model: Bar,

      // This is the column name of the referenced model
      key: 'id',

      // With PostgreSQL, it is optionally possible to declare when to check the foreign key constraint, passing the Deferrable type.
      deferrable: Deferrable.INITIALLY_IMMEDIATE
      // Options:
      // - `Deferrable.INITIALLY_IMMEDIATE` - Immediately check the foreign key constraints
      // - `Deferrable.INITIALLY_DEFERRED` - Defer all foreign key constraint check to the end of a transaction
      // - `Deferrable.NOT` - Don't defer the checks at all (default) - This won't allow you to dynamically change the rule in a transaction
    }
  },

  // Comments can only be added to columns in MySQL, MariaDB, PostgreSQL and MSSQL
  commentMe: {
    type: DataTypes.INTEGER,
    comment: 'This is a column name that has a comment'
  }
}, {
  sequelize,
  modelName: 'foo',

  // Using `unique: true` in an attribute above is exactly the same as creating the index in the model's options:
  indexes: [{ unique: true, fields: ['someUnique'] }]
});

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí