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. 官方中文文档

Logo

更多推荐