Koa2使用sequelize操作Mysql(一) sequelize

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ù)據庫

  1. 首先導入模塊
const { Sequelize } = require('sequelize');
  1. 用導入的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進行封裝, 具體請參考下一篇文章.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市笨农,隨后出現(xiàn)的幾起案子就缆,更是在濱河造成了極大的恐慌,老刑警劉巖谒亦,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件竭宰,死亡現(xiàn)場離奇詭異,居然都是意外死亡份招,警方通過查閱死者的電腦和手機切揭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來脾还,“玉大人,你說我怎么就攤上這事入愧”陕” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵棺蛛,是天一觀的道長怔蚌。 經常有香客問我,道長旁赊,這世上最難降的妖魔是什么桦踊? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮终畅,結果婚禮上籍胯,老公的妹妹穿的比我還像新娘。我一直安慰自己离福,他們只是感情好杖狼,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著妖爷,像睡著了一般蝶涩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天绿聘,我揣著相機與錄音嗽上,去河邊找鬼。 笑死熄攘,一個胖子當著我的面吹牛兽愤,可吹牛的內容都是我干的。 我是一名探鬼主播鲜屏,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼烹看,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了洛史?” 一聲冷哼從身側響起惯殊,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎也殖,沒想到半個月后土思,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡忆嗜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年己儒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捆毫。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡闪湾,死狀恐怖,靈堂內的尸體忽然破棺而出绩卤,到底是詐尸還是另有隱情途样,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布濒憋,位于F島的核電站何暇,受9級特大地震影響,放射性物質發(fā)生泄漏凛驮。R本人自食惡果不足惜裆站,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望黔夭。 院中可真熱鬧宏胯,春花似錦、人聲如沸本姥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扣草。三九已至了牛,卻和暖如春颜屠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鹰祸。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工甫窟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蛙婴。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓粗井,卻偏偏與公主長得像,于是被迫代替她去往敵國和親街图。 傳聞我的和親對象是個殘疾皇子浇衬,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內容