Js全棧開發之Sequelize用法詳解(MySQL)

Sequelize用法詳解(MySQL)

接着上一篇《Js全棧開發之koa2中間件分離與數據層搭建》來繼續學習koa2,上一篇着重總結了數據庫的訪問及模型層的搭建,以及從koa2項目中拆分出中間件的具體步驟。還略微講解了ORM框架Sequelize的單表用法,但並沒有仔細的講解,本篇將重點彙總Sequelize框架的具體用法,總結一些平時比較常用的api及語法。

1. 安裝Sequelize及數據庫驅動包

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

2. 連接數據庫

 
var Sequelize = require('sequelize');

sequelize = new Sequelize({
    database:  'koa2learn',          //數據庫名稱
    username: 'root',        //數據庫用戶名
    password: 'xxxxxx',       //數據庫密碼
    host: 'xxx.xxx.xxx.xxx',         //數據庫主機IP  localhost
    dialect: 'mysql',         //數據庫類型   'mysql'|'mariadb'|'sqlite'|'postgres'|'mssql',
    pool: {              //連接池配置
        max: 5,        //最大連接數
        min: 0,         //最小連接數
        acquire: 500,     //請求超時時間
        idle: 10000          //斷開連接後,連接實例在連接池保持的時間
    },
    logging: console.log //輸出日誌信息  true or false
});
// 測試連接
module.exports = {
    testConn: async function(){
        sequelize.authenticate().then(() => {
            console.log("連接建立成功");
        })
        .catch(err => {
            throw new Error(`無法連接數據庫:${err.message}`);
        });
    },
  }

3. 定義數據模型

3.1 define方法定義

const { Sequelize, DataTypes } = require('sequelize');

const User = sequelize.define('User', {
    id:{
        type: DataTypes.UUID,
        defaultValue: Sequelize.UUID,
        primaryKey: true
    },
    firstName: {
      type: DataTypes.STRING,
      allowNull: false,
      defaultValue: "Dahlin"
    },
    lastName: {
      type: DataTypes.STRING
      // allowNull 默認爲 true
    }
  }, {
    tableName: 'User',
    timestamps: false
  });

3.2 擴展Model定義

const { Sequelize, DataTypes, Model } = require('sequelize');

class User extends Model {}
User.init({
  firstName: {
    type: DataTypes.STRING,
    allowNull: false
  },
  lastName: {
    type: DataTypes.STRING
    // allowNull 默認爲 true
  }
}, {
  // 這是其他模型參數
  sequelize, // 我們需要傳遞連接實例
  modelName: 'User' // 我們需要選擇模型名稱
});
// 定義的模型是類本身
console.log(User === sequelize.models.User); // true

3.3 同步數據

(async () => {
  await sequelize.sync({ force: true });
  console.log("所有模型均已成功同步.");
})();

3.4 模型實例

class User extends Model {
  static classLevelMethod() {
    return 'foo';
  }
  instanceLevelMethod() {
    return 'bar';
  }
  getFullname() {
    return [this.firstname, this.lastname].join(' ');
  }
}
User.init({
  firstname: DataTypes.STRING,
  lastname: DataTypes.STRING
}, { sequelize });

console.log(User.classLevelMethod()); // 'foo'
const user = User.build({ firstname: 'Jane', lastname: 'Doe' });
console.log(user.instanceLevelMethod()); // 'bar'
console.log(user.getFullname()); // 'Jane Doe'
// 創建實例
const dahlin = User.build({lastName:"badnothing"});
console.log(dahlin instanceof User);
console.log(dahlin.firstName);

await sequelize.sync({ force: true });
console.log("所有模型均已成功同步.");

const jane = await User.create({ firstName: "Jane" });
jane.lastName = "Ada";
// 實例json序列化
console.log(jane.toJSON()); // 這樣最好!
console.log(JSON.stringify(jane, null, 4)); // 這樣也不錯!  
// 保存實例
await jane.save();
// 保存實例字段
//await jane.save({ fields: ['lastName '] });
// 刪除實例
//await jane.destroy();
// 重新加載實例
//await jane.reload();
        

4. 增刪改查基礎

4.1 插入

const user = await User.create(
    { 
        firstName: "adaa", 
        lastName: "ffdf"
    },
    { fields: ['firstName','lastName'] }
    );
console.log("Jane's auto-generated ID:", user.id);
console.log(user.firstName); // 'alice123'
console.log(user.isAdmin); // false

// 批量插入
const user2 = await User.bulkCreate([
    { firstName: 'Jack', lastName:'Sparrow' },
    { firstName: 'Davy' , lastName:'Jones' }
    ]);
console.log(user2.length); // 2
console.log(user2[0] instanceof User); // true
console.log(user2[0].id); // 1 // (或另一個自動生成的值)

4.2 查詢

// 查詢所有用戶
const users = await User.findAll({
    attributes:  ['firstName','lastName'],
    where:
    {
        firstName: 'ada',
        lastName: 'fff'
    }
  });
console.log(users.every(user => user instanceof User)); // true
console.log("All users:", JSON.stringify(users, null, 4));

4.3 刪改

// 將所有沒有姓氏的人更改爲 "Doe"
await User.update({ lastName: "Doe" }, {
  where: {
    lastName: null
  }
});

// 刪除所有名爲 "Jane" 的人 
await User.destroy({
  where: {
    firstName: "Jane"
  }
});

// 截斷表格
await User.destroy({
  truncate: true
});

5. Finders模型查詢

  • findByPk 方法使用提供的主鍵從表中僅獲得一個條目.
  • findOne 方法獲得它找到的第一個條目(它可以滿足提供的可選查詢參數).
  • findOrCreate
    除非找到一個滿足查詢參數的結果,否則方法 findOrCreate 將在表中創建一個條目.
const [user, created] = await User.findOrCreate({
  where: { username: 'sdepold' },
  defaults: {
    job: 'Technical Lead JavaScript'
  }
});
console.log(user.username); // 'sdepold'
console.log(user.job); // 這可能是也可能不是 'Technical Lead JavaScript'
console.log(created); // 指示此實例是否剛剛創建的布爾值
if (created) {
  console.log(user.job); // 這裏肯定是 'Technical Lead JavaScript'
}
  • findAndCountAll
    結合了 findAll 和 count 的便捷方法. 在處理與分頁有關的查詢時非常有用,在分頁中,你想檢索帶有 limit 和 offset 的數據,但又需要知道與查詢匹配的記錄總數.findAndCountAll 方法返回一個具有兩個屬性的對象:
    count - 一個整數 - 符合查詢條件的記錄總數
    rows - 一個數組對象 - 獲得的記錄
const { count, rows } = await Project.findAndCountAll({
  where: {
    title: {
      [Op.like]: 'foo%'
    }
  },
  offset: 10,
  limit: 2
});
console.log(count);
console.log(rows);

6. 獲取器/設置器/虛擬字段

6.1 獲取器

獲取器是爲模型定義中的一列定義的 get() 函數

const User = sequelize.define('user', {
  // 假設我們想要以大寫形式查看每個用戶名,
  // 即使它們在數據庫本身中不一定是大寫的
  username: {
    type: DataTypes.STRING,
    get() {
      const rawValue = this.getDataValue(username);
      return rawValue ? rawValue.toUpperCase() : null;
    }
  }
});
// 使用
const user = User.build({ username: 'SuperUser123' });
console.log(user.username); // 'SUPERUSER123'
console.log(user.getDataValue(username)); // 'SuperUser123'

6.2 設置器

設置器是爲模型定義中的一列定義的 set() 函數. 它接收要設置的值

const User = sequelize.define('user', {
  username: DataTypes.STRING,
  password: {
    type: DataTypes.STRING,
    set(value) {
      // 在數據庫中以明文形式存儲密碼是很糟糕的.
      // 使用適當的哈希函數來加密哈希值更好.
      // 使用用戶名作爲鹽更好.
      this.setDataValue('password', hash(this.username + value));
    }
  }
});

// 使用
const user = User.build({ username: 'dahlin', password: '123456' });
console.log(user.password);
console.log(user.getDataValue(password)); 

6.3 虛擬字段

虛擬字段是 Sequelize 在後臺填充的字段,但實際上它們不存在於數據庫中.

const { DataTypes } = require("sequelize");

const User = sequelize.define('user', {
  firstName: DataTypes.STRING,
  lastName: DataTypes.STRING,
  fullName: {
    type: DataTypes.VIRTUAL,
    get() {
      return `${this.firstName} ${this.lastName}`;
    },
    set(value) {
      throw new Error('不要嘗試設置 `fullName` 的值!');
    }
  }
});

7. 原始查詢

// 刪改語句
const [results, metadata] = await sequelize
.query("UPDATE User SET firstName = :fistName WHERE lastName = :lastName",{
    replacements: { fistName: 'Jack1' ,lastName:'Sparrow'},
});
console.log(results); 
console.log(metadata);

// 查詢語句
const users = await sequelize.query("SELECT * FROM User where firstName=:firstName", 
{   type: QueryTypes.SELECT ,
    replacements: { firstName: 'Jack1' },
});
console.log(users); 

// 查詢並映射成對象實例
const users = await sequelize.query("SELECT * FROM User where firstName=:firstName", 
{   type: QueryTypes.SELECT ,
    replacements: { firstName: 'Jack1' },
    model: User,
    mapToModel: true // 如果你有任何映射字段,則在此處傳遞 true
});
console.log(users); 
console.log("All users:", JSON.stringify(users, null, 4));

8. 關聯查詢

8.1 默認設置

const Ship = sequelize.define('ship', {
  name: DataTypes.TEXT,
  crewCapacity: DataTypes.INTEGER,
  amountOfSails: DataTypes.INTEGER
}, { timestamps: false });

const Captain = sequelize.define('captain', {
  name: DataTypes.TEXT,
  skillLevel: {
    type: DataTypes.INTEGER,
    validate: { min: 1, max: 10 }
  }
}, { timestamps: false });

Captain.hasOne(Ship);
Ship.belongsTo(Captain); // 這將在 Ship 中創建 `captainId` 外鍵.
// 通過將模型傳遞給 `include` 來完成預先加載:
console.log((await Ship.findAll({ include: Captain })).toJSON());
// 或通過提供關聯的模型名稱:
console.log((await Ship.findAll({ include: 'captain' })).toJSON());

// 同樣,實例獲得用於延遲加載的 `getCaptain()` 方法:
const ship = Ship.findOne();
console.log((await ship.getCaptain()).toJSON());

8.2 直接提供外鍵名稱

Ship.belongsTo(Captain, { foreignKey: 'bossId' }); // 這將在 Ship 中創建 `bossId` 外鍵.

// 通過將模型傳遞給 `include` 來完成預先加載:
console.log((await Ship.findAll({ include: Captain })).toJSON());
// 或通過提供關聯的模型名稱:
console.log((await Ship.findAll({ include: 'Captain' })).toJSON());

// 同樣,實例獲得用於延遲加載的 `getCaptain()` 方法:
const ship = Ship.findOne();
console.log((await ship.getCaptain()).toJSON());

8.3 定義別名

Ship.belongsTo(Captain, { as: 'leader' }); // 這將在 Ship 中創建 `leaderId` 外鍵.
Ship.belongsTo(Captain, { as: 'leader', foreignKey: 'bossId' }); // 這將在 Ship 中創建 `bossId` 外鍵.


console.log((await Ship.findAll({ include: 'leader' })).toJSON());
// 或者,你可以傳遞一個指定模型和別名的對象:
console.log((await Ship.findAll({
  include: {
    model: Captain,
    as: 'leader'
  }
})).toJSON());

// 同樣,實例獲得用於延遲加載的 `getLeader()`方法:
const ship = Ship.findOne();
console.log((await ship.getLeader()).toJSON());

8.4 多對多關係

// 字符串創建方式
const Movie = sequelize.define('Movie', { name: DataTypes.STRING });
const Actor = sequelize.define('Actor', { name: DataTypes.STRING });
Movie.belongsToMany(Actor, { through: 'ActorMovies' });
Actor.belongsToMany(Movie, { through: 'ActorMovies' });

// 傳遞模型方式創建
const Movie = sequelize.define('Movie', { name: DataTypes.STRING });
const Actor = sequelize.define('Actor', { name: DataTypes.STRING });
const ActorMovies = sequelize.define('ActorMovies', {
  MovieId: {
    type: DataTypes.INTEGER,
    references: {
      model: Movie, // 'Movies' 也可以使用
      key: 'id'
    }
  },
  ActorId: {
    type: DataTypes.INTEGER,
    references: {
      model: Actor, // 'Actors' 也可以使用
      key: 'id'
    }
  }
});
Movie.belongsToMany(Actor, { through: 'ActorMovies' });
Actor.belongsToMany(Movie, { through: 'ActorMovies' });

8.5 延遲加載與預先加載

延遲加載是指僅在確實需要時才獲取關聯數據的技術. 另一方面,預先加載是指從一開始就通過較大的查詢一次獲取所有內容的技術.

  • 延遲加載示例
const awesomeCaptain = await Captain.findOne({
  where: {
    name: "Jack Sparrow"
  }
});
// 用獲取到的 captain 做點什麼
console.log('Name:', awesomeCaptain.name);
console.log('Skill Level:', awesomeCaptain.skillLevel);
// 現在我們需要有關他的 ship 的信息!
const hisShip = await awesomeCaptain.getShip();
// 用 ship 做點什麼
console.log('Ship Name:', hisShip.name);
console.log('Amount of Sails:', hisShip.amountOfSails);
  • 預先加載示例
const awesomeCaptain = await Captain.findOne({
  where: {
    name: "Jack Sparrow"
  },
  include: Ship
});
// 現在 ship 跟着一起來了
console.log('Name:', awesomeCaptain.name);
console.log('Skill Level:', awesomeCaptain.skillLevel);
console.log('Ship Name:', awesomeCaptain.ship.name);
console.log('Amount of Sails:', awesomeCaptain.ship.amountOfSails);

9. 官方中文文檔

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章