Sequelize是用于Postgres柔吼,MySQL,MariaDB丙唧,SQLite和Microsoft SQL Server的基于promise的Node.js ORM工具。它具有可靠的事務支持觅玻,關系想际,急切和延遲加載,讀取復制等功能溪厘。
Sequelize遵循語義版本控制胡本,并支持Node v10及更高版本。
sequelize的npm地址:
https://www.npmjs.com/package/sequelize
安裝:
npm i sequelize
其要和一個數(shù)據庫結合使用,這里我們選擇mysql, 而在我們的koa2工程中, 我們使用mysql2庫來操作數(shù)據庫
npm i mysql2
一. 連接MySQL數(shù)據庫
- 首先導入模塊
const { Sequelize } = require('sequelize');
- 用導入的Sequelize 實例化一個sequelize對象:
const sequelize = new Sequelize('db1', 'root', 'xxxxx', {
host: 'localhost', //數(shù)據庫地址
dialect: 'mysql', //指定連接的數(shù)據庫類型
pool: {
max: 5, //連接池最大連接數(shù)量
min: 0, //最小連接數(shù)量
idle: 10000, //如果一個線程 10秒內么有被使用過的話畸悬,就釋放
},
})
這樣就完成了數(shù)據庫鏈接了
我們可以測試一下:
sequelize對象提供了一個authenticate方法,可以測試數(shù)據庫鏈接
https://sequelize.org/master/class/lib/sequelize.js~Sequelize.html#instance-method-authenticate
我們可以看到它return 一個promise, 所以我們應該在一個async方法里處理這個promise
例如: 我們在index.js里寫一個訪問127.0.0.1就告訴我們數(shù)據庫連接是否成功的API,可以:
const router = require('koa-router')()
const { Sequelize } = require('sequelize');
const sequelize = new Sequelize('db1', 'root', 'xxxxxx', {
host: 'localhost', //數(shù)據庫地址
dialect: 'mysql', //指定連接的數(shù)據庫類型
pool: {
max: 5, //連接池最大連接數(shù)量
min: 0, //最小連接數(shù)量
idle: 10000, //如果一個線程 10秒內么有被使用過的話侧甫,就釋放
},
})
router.get('/', async (ctx, next) => {
let str1
str1 = await sequelize
.authenticate()
.then(() => {
return '連接數(shù)據庫成功'
})
.catch(err => {
return '連接數(shù)據庫失敗' + err
});
ctx.body = str1
})
module.exports = router
二. 創(chuàng)建模型:(獲取表)
https://sequelize.org/master/manual/model-basics.html
1. 創(chuàng)建模型
連接數(shù)據庫成功后, 怎么使用ORM的方式把數(shù)據庫用起來呢? 答: 創(chuàng)建模型
模型是Sequelize的本質。模型是代表數(shù)據庫中表的抽象蹋宦。
Sequelize中的模型有一個名稱披粟。此名稱不必與它在數(shù)據庫中表示的表的名稱相同。通常冷冗,模型具有單數(shù)名稱(例如User)守屉,而表具有復數(shù)名稱(例如Users),盡管這是完全可配置的蒿辙。
通過創(chuàng)建模型,我們將MySQL中的表和模型對應起來
class Student extends Model { }//創(chuàng)建一個類,繼承Model
Student.init({
id: { type:DataTypes.INTEGER, primaryKey: true },
name: DataTypes.STRING,
age: DataTypes.INTEGER,
score: DataTypes.FLOAT,
brithday: DataTypes.DATE,
}, { sequelize, modelName: 'Student' })
除了這種聲明方法,還有一種方法把一張表聲明為一個model,這種方法本質上還是在調用init
函數(shù), 用哪種隨你喜歡
const Student = sequelize.define('Student111', {
id: { type: DataTypes.INTEGER, primaryKey: true },
name: DataTypes.STRING,
age: DataTypes.INTEGER,
score: DataTypes.FLOAT,
brithday: DataTypes.DATE,
}, {sequelize})
2. 模型同步
定義模型時拇泛,您要告訴Sequelize有關數(shù)據庫中表的一些信息。但是思灌,如果該表實際上甚至不存在于數(shù)據庫中怎么辦俺叭?如果存在,但具有不同的列泰偿,較少的列或任何其他差異熄守,該怎么辦?
這就需要模型同步。
- User.sync() -如果表不存在柠横,則會創(chuàng)建表(如果已經存在窃款,則不執(zhí)行任何操作)
- User.sync({ force: true }) -這將創(chuàng)建表,如果該表已經存在牍氛,則將其首先刪除
- User.sync({ alter: true }) -這將檢查數(shù)據庫中表的當前狀態(tài)(它具有哪些列晨继,它們的數(shù)據類型是什么,等等)搬俊,然后在表中進行必要的更改以使其與模型匹配紊扬。
Student.sync({ alter: true })
3. 注意一個sequelize的默認行為:時間戳記
默認情況下,Sequelize使用數(shù)據類型自動將字段createdAt和添加updatedAt到每個模型中(DataTypes.DATE類型)唉擂。
這些字段也會自動進行管理-每當您使用Sequelize創(chuàng)建或更新內容時餐屎,這些字段都會被正確設置。該createdAt字段將包含代表創(chuàng)建時刻的時間戳玩祟,并且updatedAt將包含最新更新的時間戳腹缩。
注意:這是在Sequelize級別完成的(即,不使用SQL觸發(fā)器完成)空扎。使用者無需干預藏鹊。
但是通過不使用Sequelize的情況下執(zhí)行的查詢, 將不會導致這些字段自動更新。
可以使用以下timestamps: false選項為模型禁用此行為:
sequelize.define('User', {
// ... (attributes)
}, {
timestamps: false
});
上面我們得到了數(shù)據庫的映射對象sequelize,以及表的映射對象User, 接下來, 我們先嘗試操作表
三. 創(chuàng)建模型實例 (表中插入數(shù)據) // 增
數(shù)據就相當于''模型類''的實例化
3.1 build方法
盡管模型是類转锈,但是您不應new
直接使用運算符來創(chuàng)建實例盘寡。相反,應使用build
方法:
const ldy = Student.build({
name:"林黛玉",
age:17,
score:100,
brithday: new Date('2010-12-05')
})
console.log(ldy instanceof Student);
ldy.save()
注意:build根本不與數(shù)據庫通信(請注意: 它甚至不是異步的)
build完成后應該用save方法保存到數(shù)據庫中
3.2 create方法
Sequelize提供了create方法將以上的build和save方法組合為一個方法:
const yyh = await Student.create({
name:"楊玉環(huán)",
age:25,
score:95,
brithday: new Date('2009-12-05')
})
3.3 Sequelize提供了Model.bulkCreate
方法撮慨,該方法一次就可以創(chuàng)建多個記錄竿痰。
Model.bulkCreate與Model.create極為相似,方法接收對象數(shù)組而不是單個對象砌溺。
const captains = await Captain.bulkCreate([
{ name: 'Jack Sparrow' },
{ name: 'Davy Jones' }
]);
默認情況下影涉,bulkCreate不會在要創(chuàng)建的每個對象上運行驗證(create會驗證)。要bulkCreate運行驗證规伐,您必須傳遞validate: true選項常潮。但這會降低性能。
const Foo = sequelize.define('foo', {
bar: {
type: DataTypes.TEXT,
validate: {
len: [4, 6]
}
}
});
四. 查找模型中的數(shù)據 (查表) //查
4.1 查找模型中所有數(shù)據 findAll
相當于
SELECT * FROM ...
router.get('/', async (ctx, next) => {
const students = await Student.findAll();
console.log(students.every(student => student instanceof Student)); // true
ctx.body = "All students:" + JSON.stringify(students, null, 2)
})
4.1.1 findAll指定查詢的屬性
相當于: SELECT foo, bar FROM ...
router.get('/', async (ctx, next) => {
const students = await Student.findAll({ attributes: ['name', 'age'] });
console.log(students.every(student => student instanceof Student)); // true
ctx.body = "All students:" + JSON.stringify(students, null, 2)
})
4.1.2 取別名
相當于: SELECT foo, bar AS baz, qux FROM ...
Model.findAll({
attributes: ['foo', ['bar', 'baz'], 'qux']
});
4.2 按條件查找模型 //應用WHERE子句
4.2.1 按條件查找模型
相當于SELECT * FROM post WHERE authorId = 2
router.get('/', async (ctx, next) => {
const students = await Student.findAll({
where: { id: 2 }
});
ctx.body = "All students:" + JSON.stringify(students, null, 2)
})
如果有多個篩選條件,也是可以的
const students = await Student.findAll({
where: {
age: 17, score: 100
}
});
4.2.2 where and 條件
如果引入Op對象, 可以用and 篩選多個條件, 效果同上
const { Op } = require("sequelize");
router.get('/', async (ctx, next) => {
const students = await Student.findAll({
where: {
[Op.and]: [
{ age: 17 },
{ score: 100 }
]
}
});
ctx.body = "All students:" + JSON.stringify(students, null, 2)
})
4.2.2 where or 條件
如果需要OR限定, 也應該使用Op對象,對象的列表里寫的是同一個屬性
const students = await Student.findAll({
where: {
[Op.or]: [
{ age: 17 },
{ age: 25 }
]
}
});
還有一種寫法,功能是一樣的:
const students = await Student.findAll({
where: {
age: {
[Op.or]: [17, 25]
}
}
});
4.2.3 Op對象有好多種用法:
onst { Op } = require("sequelize");
Post.findAll({
where: {
[Op.and]: [{ a: 5 }, { b: 6 }], // (a = 5) AND (b = 6)
[Op.or]: [{ a: 5 }, { b: 6 }], // (a = 5) OR (b = 6)
someAttribute: {
// Basics
[Op.eq]: 3, // = 3
[Op.ne]: 20, // != 20
[Op.is]: null, // IS NULL
[Op.not]: true, // IS NOT TRUE
[Op.or]: [5, 6], // (someAttribute = 5) OR (someAttribute = 6)
// Using dialect specific column identifiers (PG in the following example):
[Op.col]: 'user.organization_id', // = "user"."organization_id"
// Number comparisons
[Op.gt]: 6, // > 6
[Op.gte]: 6, // >= 6
[Op.lt]: 10, // < 10
[Op.lte]: 10, // <= 10
[Op.between]: [6, 10], // BETWEEN 6 AND 10
[Op.notBetween]: [11, 15], // NOT BETWEEN 11 AND 15
// Other operators
[Op.all]: sequelize.literal('SELECT 1'), // > ALL (SELECT 1)
[Op.in]: [1, 2], // IN [1, 2]
[Op.notIn]: [1, 2], // NOT IN [1, 2]
[Op.like]: '%hat', // LIKE '%hat'
[Op.notLike]: '%hat', // NOT LIKE '%hat'
[Op.startsWith]: 'hat', // LIKE 'hat%'
[Op.endsWith]: 'hat', // LIKE '%hat'
[Op.substring]: 'hat', // LIKE '%hat%'
[Op.iLike]: '%hat', // ILIKE '%hat' (case insensitive) (PG only)
[Op.notILike]: '%hat', // NOT ILIKE '%hat' (PG only)
[Op.regexp]: '^[h|a|t]', // REGEXP/~ '^[h|a|t]' (MySQL/PG only)
[Op.notRegexp]: '^[h|a|t]', // NOT REGEXP/!~ '^[h|a|t]' (MySQL/PG only)
[Op.iRegexp]: '^[h|a|t]', // ~* '^[h|a|t]' (PG only)
[Op.notIRegexp]: '^[h|a|t]', // !~* '^[h|a|t]' (PG only)
[Op.any]: [2, 3], // ANY ARRAY[2, 3]::INTEGER (PG only)
// In Postgres, Op.like/Op.iLike/Op.notLike can be combined to Op.any:
[Op.like]: { [Op.any]: ['cat', 'hat'] } // LIKE ANY ARRAY['cat', 'hat']
// There are more postgres-only range operators, see below
}
}
});
4.2.4 Op.in可以簡寫
const students = await Student.findAll({
where: {
age: [17, 25]
}
});
五. 更多查詢方法
1. findByPk 按主鍵查詢
const student = await Student.findByPk(2)
2. findOne 找到第一個條目
const student = await Student.findOne({ where: { age: 17 } })
3. findOrCreate 查詢或創(chuàng)建
findOrCreate將在表中找滿足查詢選項的條目, 沒有則創(chuàng)建一個條目楷力。
在這兩種情況下喊式,它都會返回一個實例(找到的實例或創(chuàng)建的實例)和一個布爾值,指示該實例是已創(chuàng)建還是已經存在萧朝。
const [student, created] = await Student.findOrCreate({
where: { name: "范冰冰" },
defaults: {
age: 35, score: 100,
brithday: new Date('2020-03-05')
}
})
不知道是不是我用的不對, 創(chuàng)建時會在服務器控制臺報錯, 但仍舊能創(chuàng)建成功
3. findAndCountAll
findAndCountAll方法是組合findAll和的便捷方法count岔留。
該findAndCountAll方法返回一個具有兩個屬性的對象:
count -整數(shù)-符合查詢條件的記錄總數(shù)
rows -對象數(shù)組-獲得的記錄
router.get('/', async (ctx, next) => {
const {count, rows} = await Student.findAndCountAll({
where: {
age: {
[Op.lt]: 25
}
}
})
let str1 = ''
rows.forEach((row) => str1 += JSON.stringify(row, null, 2))
ctx.body = 'rows:\n' + str1 + '\ncount\n' + count
})
五. order排序查詢
order選項采用一項或一個sequelize方法進行排序。
例如:
const students = await Student.findAll({
order: ['age']
})
order有好多用法:
Subtask.findAll({
order: [
// Will escape title and validate DESC against a list of valid direction parameters
['title', 'DESC'],
// Will order by max(age)
sequelize.fn('max', sequelize.col('age')),
// Will order by max(age) DESC
[sequelize.fn('max', sequelize.col('age')), 'DESC'],
// Will order by otherfunction(`col1`, 12, 'lalala') DESC
[sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],
// Will order an associated model's createdAt using the model name as the association's name.
[Task, 'createdAt', 'DESC'],
// Will order through an associated model's createdAt using the model names as the associations' names.
[Task, Project, 'createdAt', 'DESC'],
// Will order by an associated model's createdAt using the name of the association.
['Task', 'createdAt', 'DESC'],
// Will order by a nested associated model's createdAt using the names of the associations.
['Task', 'Project', 'createdAt', 'DESC'],
// Will order by an associated model's createdAt using an association object. (preferred method)
[Subtask.associations.Task, 'createdAt', 'DESC'],
// Will order by a nested associated model's createdAt using association objects. (preferred method)
[Subtask.associations.Task, Task.associations.Project, 'createdAt', 'DESC'],
// Will order by an associated model's createdAt using a simple association object.
[{model: Task, as: 'Task'}, 'createdAt', 'DESC'],
// Will order by a nested associated model's createdAt simple association objects.
[{model: Task, as: 'Task'}, {model: Project, as: 'Project'}, 'createdAt', 'DESC']
],
// Will order by max age descending
order: sequelize.literal('max(age) DESC'),
// Will order by max age ascending assuming ascending is the default order when direction is omitted
order: sequelize.fn('max', sequelize.col('age')),
// Will order by age ascending assuming ascending is the default order when direction is omitted
order: sequelize.col('age'),
// Will order randomly based on the dialect (instead of fn('RAND') or fn('RANDOM'))
order: sequelize.random()
});
Foo.findOne({
order: [
// will return `name`
['name'],
// will return `username` DESC
['username', 'DESC'],
// will return max(`age`)
sequelize.fn('max', sequelize.col('age')),
// will return max(`age`) DESC
[sequelize.fn('max', sequelize.col('age')), 'DESC'],
// will return otherfunction(`col1`, 12, 'lalala') DESC
[sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],
// will return otherfunction(awesomefunction(`col`)) DESC, This nesting is potentially infinite!
[sequelize.fn('otherfunction', sequelize.fn('awesomefunction', sequelize.col('col'))), 'DESC']
]
});
六. 分組查詢
const students = await Student.findAll({
group: 'name'
})
七. 查詢的限制和分頁
在limit和offset選項允許您使用限制/分頁工作:
// Fetch 10 instances/rows
Project.findAll({ limit: 10 });
// Skip 8 instances/rows
Project.findAll({ offset: 8 });
// Skip 5 instances and fetch the 5 after that
Project.findAll({ offset: 5, limit: 5 });
通常检柬,這些order選項與選項一起使用献联。
八. 更新模型中的數(shù)據(更新表中數(shù)據)
8.1. 簡單的UPDATE
更新也接受where選項竖配,就像上面的讀取一樣。
await Student.update(
{ age: 23 },
{
where: {
name: '楊玉環(huán)'
}
}
)
九. 刪除表中數(shù)據
刪除也接受where選項里逆,就像上面的讀取一樣进胯。
await Student.destroy(
{
where: {
name: '楊玉環(huán)'
}
}
)
十. 使用原始SQL語句
在很多情況下執(zhí)行原始SQL查詢更加容易,此時可以使用sequelize.query
方法原押。
默認情況下胁镐,該函數(shù)將返回兩個參數(shù): -一個結果數(shù)組和一個包含元數(shù)據的對象(例如,受影響的行數(shù)等), 但對于MySQL诸衔,它們將是對同一對象的兩個引用盯漂。
const [results, metadata] = await sequelize.query("UPDATE users SET y = 42 WHERE x = 12");
關于原始查詢:詳見:
https://sequelize.org/master/manual/raw-queries.html
十一. 建立表間連接
表與表之間無外乎:
一對一 belongsto
外鍵一對一 hasone
一對多 hasmany
多對多 belongsToMany
Banner.belongsTo(Image, {
foreignKey: 'img_id',
targetKey: 'id'
})
今天我們熟悉了很多sequelize的特性和方法, 基本可以掌握sequelize的用法, 下一步: 我們將對sequelize進行封裝, 具體請參考下一篇文章.