玩轉Node.js-Sequelize基礎


Sequelize是什么?

logo-small-844fb9182c0fbf41931de2246fa9c496-1607689611218

Sequelize 是一個基於 promise 的 Node.js ORM, 目前支持 Postgres, MySQL, MariaDB, SQLite 以及 Microsoft SQL Server. 它具有強大的事務支持, 關聯關系, 預讀和延遲加載,讀取復制等功能。

Sequelize 遵從 語義版本控制。 支持 Node v10 及更高版本以便使用 ES6 功能。

說簡單點sequelize就是幫助我們操作數據庫的一個ORM(Object–relational mapping,對象關系映射)框架,我們使用mysql2操作數據庫,要使用各種原生的sql語句去操作,功能簡單還好說,功能復雜龐大就會顯得很麻煩,繁瑣,畢竟要寫大量的原生sql語句,sequelize的出現幫助我們只需要通過對象的方式就可以完成數據庫表創建、設計、數據的CRUD操作,簡化開發、提高效率。

中文文檔:https://www.sequelize.com.cn/

安裝

Sequelize 的使用可以通過 npm (或 yarn).

npm install --save sequelize

你還必須手動為所選數據庫安裝驅動程序:

# 選擇以下之一:
$ npm install --save pg pg-hstore # Postgres
$ npm install --save mysql2
$ npm install --save mariadb
$ npm install --save sqlite3
$ npm install --save tedious # Microsoft SQL Server

因為我們要操作的是mysql,所以這個地方我們選擇mysql2。

連接到數據庫

const Sequelize = require('sequelize');
// 創建Sequelize的實例對象並配置連接信息
const sequelize = new Sequelize({
  host: '127.0.0.1',
  username: 'root',
  password: 'root',
  database: 'todos',
  dialect: 'mysql',
  timezone: 'Asia/Shanghai' //當我們向數據庫中寫入時間的時候,默認會根據系統當前所在時區進行設置
})
// 測試連接
sequelize.authenticate().then(_=>{
  console.log("數據庫連接成功!");
}).catch(err=>{
  console.log("數據庫連接失敗! ", err);
})

new Sequelize options:
host:主機,默認localhost
port:端口,默認3306
dialect:數據庫類型,默認mysql,必填
timezone:時區,影響數據庫日期時間格式的值,格式:+08:00 或 字符串格式

什么是模型?

用來表述(描述)數據庫表字段信息的對象,每一個模型對象表示數據庫中的一個表,后續對數據庫的操作都是通過對應的模型對象來完成的。

模型對象

創建模型對象

public define(modelName: String, attributes: Object, options: Object): Model

  • modelName:模型名稱,自定義
  • attributes:模型中包含的數據,每一個數據映射對應表中的每一個字段
  • options:模型(表)的設置
  /**
   * 數據庫連接完成以后,需要確定操作的表
   * 使用ORM,不需要通過sql來操作表,而是通過對象來操作
   * 給每一個要操作的表定義一個對象 - 模型 Model
   */
  const UserModel = sequelize.define('User', {
    // 描述表中對應的字段信息
    // 對象的key默認對應這表的column字段
    id: {
      // 每一個字段的信息
      type: Sequelize.INTEGER(10), // 整數類型
      allowNull: false, // 不允許為空
      primaryKey: true, // 設置為主鍵
      autoIncrement: true // 允許自增
    },
    username: {
      type: Sequelize.STRING(255),
      allowNull: false,
      defaultValue: 0
    },
    age: {
      type: Sequelize.TINYINT,
      allowNull: false,
      defaultValue: 0
    },
    gender: {
      type: Sequelize.ENUM(['男', '女', '默認']), // 注意枚舉類型為數組
      allowNull: false,
      defaultValue: '男'
    }
  }, {
    // 用來設置字段以外的其他信息
    timestamps: false,
    paranoid: false,
    freezeTableName: true,
    tableName: 'user',
    indexes: [{
      name: 'uname',
      fields: ['username']
    }, {
      name: 'age',
      fields: ['age']
    }]
  })

attributes 設置

attributes是一個對象,里面包含了對應的表中的字段信息,key表示字段名稱,值是字段的具體描述。

字段值描述

type:			字段類型,String|DataTypes
allowNull:		是否允許為空,默認為true
defaultValue:	默認值,默認為null
unique:			值唯一,默認為false
primaryKey:		是否為主鍵,默認為false
field:		    數據庫中字段的實際名稱
autoIncrement:	是否自增,默認false
get:		   字段的getter,函數
set:            字段的setter,函數
validate:       對象,對當前字段值發生改變的時候進行驗證

模型額外配置

timestamps:是否給每條記錄添加 createdAt 和 updatedAt 字段,並在添加新數據和更新數據的時候自動設置這兩個字段的值,默認為true

paranoid:設置 deletedAt 字段,當刪除一條記錄的時候,並不是真的銷毀記錄,而是通過該字段來標識,即保留數據,進行假刪除,默認為false

freezeTableName:禁用修改表名; 默認情況下,sequelize將自動將所有傳遞的模型名稱(define的第一個參數)轉換為復數。 默認為false

tableName:手動設置表的實際名稱

定義表的索引

indexes:Array

每個索引對象可以設置的值:
	name:索引名稱,默認模型名稱+字段
	fields: Array<string>,索引字段
	unique:唯一索引,默認false

增加索引的好處就是讓數據庫查詢數據的時候變得更快。

模型實例對象

模型實例對象的概念很好理解,模型對象的作用是操作某張數據表,模型實例對象的作用是操作這張數據表中的數據。

一個模型類對應一個表,一個模型實例對象就是一條對應的表記錄,通過操作這個對象來關聯操作對應的表中的數據,操作模型類就是操作表,操作模型類對象就是操作該表中的某條記錄。

模型類——表

模型實例——記錄

注意在數據庫中,我們通常會把記錄稱為數據,也會把數據稱為記錄,概念是一樣的。

創建模型實例對象

public static build(options: Object): Model | Model[]

options:一個對象,對應的就是表中的字段(模型中定義的屬性),需要注意的是對該對象的操作不會立即反應到實際的數據庫中,需要通過后續的操作進行同步
  /**
   * 模型類 -> 表
   * 模型創建出來的對象 -> 表中某條記錄
   */
  // let Kimoo = new UserModel();    //創建了一個User的記錄

  let Kimoo = UserModel.build({    //和上面的new是一樣的
      // 字段對應的值
      username: 'Kimoo',
      age: 30,
      gender: '男'
  });  

  // 通過new或者build出來的對象不會立即同步到數據庫中,需要使用后續的一些方法來同步

  await Kimoo.save();

模型實例對象CRUD操作

模型實例對象.get(key: String):獲取某個屬性(字段)的值。

模型對象.set(key: String, value: any):設置某個屬性(字段)的值。

模型對象.save():驗證該實例,如果通過驗證,則持久化到數據庫中。

模型對象.update(updates: Object):updates:要更新的字段,調用該方法等同於調用.set()然后.save()。

模型對象.destroy():銷毀該實例(假刪除或真刪除)。

  // 創建數據模型實例對象 c4az6
  // let c4az6 = UserModel.build({
  //   username: 'c4az6',
  //   age: 20,
  //   gender: '男'
  // })

  // 獲取屬性
  /* console.log(`
  username: ${c4az6.get('username')}
  age: ${c4az6.get('age')}
  gender: ${c4az6.get('gender')}
  `); */

  // 設置屬性
  // let res = c4az6.set('age', 21);
  // console.log(res.dataValues);

  // 通過save方法同步數據到數據庫中
  // await c4az6.save();

  // 更新字段數據
  let res = await UserModel.findById(17);
  console.log(res.dataValues);
  res.update({username: 'Alex'});

  let res2 = await UserModel.findById(18);
  res2.update({username: 'Elon', age: 40});
  console.log(res2.dataValues);
  // 銷毀記錄
  res2.destroy();

除了通過模型創建出來的實例對單條數據進行操作,也可以通過模型類對整個對應的表進行操作。

模型.findById(id: Number | String | Buffer):根據主鍵搜索單條記錄,注意是根據主鍵。

findById這個API在6.x的版本中已經被替換為findByPk了

模型.findOne(options: Object):根據條件搜索一條記錄 options.where:搜索條件 Op操作。

模型.findOrCreate(options: Object):搜索特定記錄或創建它(如果沒有對應記錄)options.where:搜索條件。

模型.findAll(findOptions: Object):在數據庫中搜索多個記錄,返回所有數據。

  • findOptions.where:搜索條件
  • findOptions.limit:記錄條數限制
  • findOptions.offset:記錄偏移
  • findOptions.order:記錄排序方式

模型.findAndCountAll(findOptions: Object):與findAll類似,但是返回值包含 count 屬性,返回數據與總計數。

  // 數據模型實例對象查詢相關操作
  // findById
  // let user = await UserModel.findById(17);
  // console.log(user.dataValues);

  // findOne 返回一個對象
  // let res = await UserModel.findOne({
  //   where: {
  //     id: 17
  //   }
  // })
  // console.log(res.dataValues);
  
  // 搜索或創建特定記錄,如果不存在則創建,返回數組
  // let res = await UserModel.findOrCreate({
  //   where: {
  //     id: 30,
  //     username: 'test'
  //   }
  // })
  // console.log(res[0].dataValues);

  // 搜索多個記錄,返回數據和總記錄數, 返回數組
  // 搜索年齡大於30的所有記錄,這種對象嵌套對象的寫法真惡心,一旦條件變多代碼可讀性會非常差
  // let res = await UserModel.findAll({
  //   where: {
  //     age: {
  //       [Op.gt]: 30
  //     }
  //   }
  // })
  // console.log(res.length);
  // res.map(item=>{console.log(item.dataValues)});

  // 與findAll一樣,但是在返回所有數據的基礎上添加了count統計總記錄數的字段, 返回數組
  let res = await UserModel.findAndCountAll()
  console.log(res.count)
  res.rows.map(item=>{console.log(item.dataValues)});

過濾查詢(Sequelize.Op)

更多Op規則參考sequelize API文檔:https://sequelize.org/master/variable/index.html#static-variable-Op

  // 過濾查詢
  // let res = await UserModel.findAll({
  //   where: {
  //     // 單條件過濾
  //     // username: 'Alex',
  //     // 多條件 要么年齡大於30,要么性別為女
  //     [Sequelize.Op.or]: [
  //       {
  //         age: {
  //           [Sequelize.Op.gt]: 30
  //         }
  //       },
  //       {
  //         gender: '女'
  //       }
  //     ]
  //   }
  // })
  // res.map(item=>{console.log(item.dataValues)});

  // limit 限制記錄查詢
  // let res = await UserModel.findAll({
  //   limit: 5
  // });
  // for(let i=0; i<res.length; i++) {
  //   console.log(res[i].dataValues);
  // }

  // 分頁查詢 limit配合offset一起使用
  // let res = await UserModel.findAll({
  //   limit: 5,
  //   offset: 5
  // });
  // console.log(res);
  // res.some(item=>console.log(item.dataValues))

  // 排序查詢
  // 年齡按照降序規則來排序
  // let res = await UserModel.findAll({
  //   order: [['age', 'desc']]
  // });
  // res.forEach(item=>{console.log(item.dataValues)});

  // 返回總記錄數
  // let res = await UserModel.count();
  // console.log(res);

  // 返回2條記錄數和總記錄數
  // let res = await UserModel.findAndCountAll({
  //   limit: 2
  // });
  // console.log(res.count);
  // res.rows.forEach(item=>{console.log(item.dataValues)});

  // 計算gender字段為男的年齡總和
  // let res = await UserModel.sum('age', {
  //   where: {
  //     gender: '男'
  //   }
  // });
  // console.log(res);

關聯查詢

1.首先給關聯的字段定義外鍵關系

references: {
	model: 關聯的外鍵表,如User
	key: 關聯的外鍵表的字段,如id
}
  1. 在調用hasOne或hasMany等方法的時候,通過第二個參數設置對象:{foreignKey: 當前關聯表的字段,如uid}
  2. 在查詢中使用 include 去設置關聯的外鍵表模型,如:include: [MessageModel]
  // 關聯查詢
  // 注意:創建模型的前提是你已經有建好了這張表
  const MessageModel = sequelize.define('message', {
    id: {
      type: Sequelize.INTEGER(10),
      primaryKey: true,
      allowNull: true,
      autoIncrement: true
    },
    uid: { // 關聯其他表的字段,把當前字段定義為外鍵
      type: Sequelize.INTEGER(10),
      defaultValue: 0,
      references: {
        model: UserModel,
        key: 'id'
      }
    },
    content: {
      type: Sequelize.STRING(255),
      allowNull: true,
      defaultValue: ''
    }
  }, {
    timestamps: false,
    freezeTableName: true, // 凍結表名稱
    tableName: 'message'
  })

  // Object.assign(data, {
  //   id: message.get('id'),
  //   uid: message.get('uid'),
  //   username: user.get('username'),
  //   age: user.get('age'),
  //   gender: user.get('gender'),
  //   content: message.get('content')
  // });
  // console.log(data);

  // MessageModel屬於UserModel模型對象
  // MessageModel.belongsTo(UserModel, {
  // // 關聯外鍵
  //   foreignKey: 'uid'
  // });

  // let data2 = await MessageModel.findById(1, {
  //  // 設置查詢出來的數據包含UserModel數據
  //   include: [UserModel]
  // });
  // // console.log(data2);

  // console.log(`
  //   留言id:${data2.get('id')}
  //   留言人名稱:${data2.User.username}
  //   留言內容:${data2.get('content')}
  // `);

  // 關聯查詢與預加載
  // 首先給關聯的字段定義外鍵關系
  // UserModel包含MessageModel對象,hasMany表示包含多個
  UserModel.hasMany(MessageModel, {
    foreignKey: 'uid'
  });

  // 在查詢中使用include去設置關聯的外鍵表模型,如:include:[MessageModel]
  let data3 = await UserModel.findById(5, {
    include: [MessageModel]
  });

  data3.messages.map(item => console.log(item.dataValues));


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM