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
- 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
- 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
- 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
}
});
- 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
- Đồ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ảngUser.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!");
- Đồ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 createAt
và updateAt
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