Sequelize是一款基于 Nodejs 功能强大的异步 ORM 框架,同时支持 PostgreSQL, MySQL, SQLite and MSSQL 多种数据库,很适合作为 Nodejs 后端数据库的存储接口

0 说明

1 环境搭建

  • 创建文件夹sequelize,然后在文件夹中执行npm init,这样就得到一个基础的工程
  • 进入文件夹sequelize,执行命令yarn add sequelize mysql2,也可以使用npm命令
  • 创建数据库test_sequelize

    CREATE DATABASE test_sequelize DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
    
  • 创建以下文件夹

    • sequelize/src
    • sequelize/src/app
    • sequelize/src/db
    • sequelize/src/utils
  • 新增文件sequelize/src/db/index.js

    const Sequelize = require('sequelize');
    
    const dbConfig = {
      database: 'test_sequelize',
      username: 'root',
      password: '123456',
      host: '127.0.0.1',
      dialect: 'mysql', // 'mysql'|'sqlite'|'postgres'|'mssql'
    };
    
    const sequelize = new Sequelize(
      dbConfig.database,
      dbConfig.username,
      dbConfig.password,
      {
        host: dbConfig.host,
        dialect: dbConfig.dialect,
        operatorsAliases: false,
        // 设置时区
        timezone: '+08:00',
        pool: {
          max: 5,
          min: 0,
          acquire: 30000,
          idle: 10000,
        },
    
        // define: {
        // 全局设置引擎, 默认是 InnoDB
        //     engine: 'MYISAM',
        // SQLite only
        // storage: 'path/to/database.sqlite'
      }
    );
    
    module.exports = sequelize;
    
  • 新增文件sequelize/src/app/1-getstarted/index.js

    const db = require('../../db');
    
    db.authenticate()
      .then(() => {
        console.log('Connection has been established successfully.');
      })
      .catch(err => {
        console.error('Unable to connect to the database:', err);
      });
    
  • sequelize/src/app/1-getstarted目录下执行node index.js,看是否能够连接成功

    Executing (default): SELECT 1+1 AS result
    Connection has been established successfully.
    
  • 新增文件sequelize/src/utils/random.js,用于生成随机数

    const crypto = require('crypto');
    
    const getRandomString = (len = 16) => {
      const buf = crypto.randomBytes(len);
      return buf.toString('hex');
    };
    
    module.exports = {
      getRandomString,
    };
    
  • 至此环境搭建完毕

2 第一个示例

  • 新增文件sequelize/src/app/2-firstdemo/index.js

    const sequelize = require('sequelize');
    const db = require('../../db');
    const random = require('../../utils/random');
    
    // 定义一个 user model
    const {
        STRING
    } = sequelize;
    
    const User = db.define('user', {
        user_name: {
            type: STRING(32),
        },
        password: {
            type: STRING(32),
        }
    });
    
    const createTable = async () => {
        // 可以选择在 应用/服务器 启动的时候,把 User 映射到数据库中,比如这里会在数据库中自动创建一张表: users
        // 如果 force 为 true, 则每次都会重建表 users
        await User.sync({
            force: true,
        });
    }
    
    const createUser = async () => {
        // 创建几个用户
        for (let i = 0; i < 5; i++) {
            const userName = random.getRandomString(8);
            const password = random.getRandomString(16);
    
            const user = await User.create({
                user_name: userName,
                password
            });
            // sequelize 会默认加上 id, createdAt, updatedAt 字段
            console.log(user.id, user.createdAt, user.updatedAt);
        }
    }
    
    const queryAll = async () => {
        // 查询所有的结果
        const users = await User.findAll();
        users.forEach((user) => console.log('findAll', user.id, user.user_name));
    }
    
    (async () => {
        console.log('------------- createTable');
        await createTable();
        console.log('------------- createUser');
        await createUser();
        console.log('------------- queryAll');
        await queryAll();
    })();
    
  • sequelize/src/app/2-firstdemo下执行node index.js, 可以发现创建了表,创建了角色,并且全部搜索出来了
  • 需要注意的是, Sequelize使用的是异步,返回值是Promises, 比如const user = User.findOne(); console.log(user.id)输出的是undefined,看日志可以发现,在输出之后才执行的SELECT id, user_name, password, createdAt, updatedAt FROM users AS user LIMIT 1
  • 你需要使用如下方式来获取数据

    User.findOne().then(user => console.log(user.id));
    
  • 也可以使用本文的方式,使用async/await

    const user = User.findOne();
    console.log(user.id);
    

3 模型定义

  • 新增文件sequelize/src/app/3-model-define/index.js

    const sequelize = require('sequelize');
    const db = require('../../db');
    
    const { INTEGER, STRING, DATE, NOW, JSON } = sequelize;
    
    // 定义一个账号
    
    const Account = db.define(
      'account',
      {
        // id (实际上 Sequelize 默认会自动生成一个自增 id)
        id: {
          type: INTEGER,
          autoIncrement: true,
          // 做为主键
          primaryKey: true,
        },
        // 账号ID为一个整型,且不能为空
        accountId: {
          type: INTEGER.UNSIGNED,
          allowNull: false,
          // 账号id需要唯一性,这个是永远不能改变的值,特别是在分表的情况下,用自增ID不小心就会重复了
          unique: true,
        },
        // 账号名称,字符串,范围是 4~32
        accountName: {
          // 如果不指定 STRING 长度,则默认是 255
          type: STRING(32),
          validate: {
            // 限制长度范围
            min: 4,
            max: 32,
          },
          // 账号需要唯一性,登录时候使用
          unique: true,
        },
        // 密码
        password: {
          type: STRING(128),
          allowNull: false,
        },
        // 昵称
        nickName: {
          type: STRING(32),
          validate: {
            min: 4,
            max: 32,
          },
          // 假设昵称后要加上 id 值
          get() {
            const id = this.getDataValue('id');
            return this.getDataValue('nickName') + '-' + id;
          },
        },
        // 邮箱
        email: {
          type: STRING(64),
          allowNull: false,
          validate: {
            // 格式必须为 邮箱格式
            isEmail: true,
          },
          // set 假设数据库中存储的邮箱都要是大写的,可以在此处改写
          set(val) {
            this.setDataValue('email', val.toUpperCase());
          },
        },
        // 注册日期 (实际上 Sequelize 默认会自动生成一个 createdAt 字段)
        registerAt: {
          type: DATE,
          defaultValue: NOW,
        },
        // 属性,这里用 JSON 格式写入
        profile: {
          type: JSON,
        },
    
        // 还支持外键功能, 不过我不喜欢使用, 项目换人了,触发器, 存储过程, 外键 这些东西就比较麻烦了
      },
      {
        getterMethods: {
          // 自定义函数, brief 返回该账号的简要信息
          brief() {
            return `${this.accountId} ${this.accountName} ${this.nickName}`;
          },
        },
        setterMethods: {},
    
        // classMethods 和 instanceMethods 在 版本4 被移除了
        // 详见: http://docs.sequelizejs.com/manual/tutorial/upgrade-to-v4.html#config-options
        // 定义 类 级别的函数,可以用 Account 调用
        classMethods: {
          getCMethod() {
            return 'classMethods';
          },
        },
        // 定义 实例 级别的函数,用 account 调用
        instanceMethods: {
          getIMethod() {
            return 'instanceMethods';
          },
        },
        // 也可以在 src/db/index 中定义全局的函数
    
        // 设置为 false 之后,将不会自动加上 createdAt, updatedAt 这两个字段
        timestamps: true,
        // 假设我们需要 创建和更新 这两个字段,但不喜欢驼峰命名法
        // 设置为 true 之后,自动增加的字段就会用下划线命名: created_at, updated_at
        underscored: true,
        // 也可以分别设置 createdAt, updatedAt 是否需要
    
        // 假设我们喜欢 date 这个名字表示创建时间
        createdAt: 'date',
        // 不想要 updatedAt 这个字段
        updatedAt: false,
    
        // 设置为 true 之后,则不会真正的删除数据,而是设置 deletedAt
        paranoid: true,
        // 也可以重命名 deletedAt
        deletedAt: 'deleteTime',
    
        // 定义表名, 默认会生成一个 accounts 的表
        tableName: 'account',
    
        // 设置引擎格式,默认是 InnoDB 的,这是对单个表有效的
        // engine: 'MYISAM',
    
        // 写表注释
        comment: '账号表',
    
        // 可以在定义 字段的时候添加索引,不过在这个地方加索引看的会更清晰
        indexes: [
          // email 需要唯一
          {
            unique: true,
            fields: ['email'],
          },
          // 创建一个 符合索引
          {
            // 默认名字 [table]_[fields],巨丑
            name: 'select_index',
            fields: ['accountName', 'email', 'password'],
          },
        ],
      }
    );
    
    const createTable = async () => {
      await Account.sync({
        force: true,
      });
    };
    
    (async () => {
      console.log('------------- createTable');
      await createTable();
    })();
    
  • 字段的类型有很多种,比如常用的STRINGTEXTINTEGERFLOATDOUBLEDATE,更多的类型请见: DataTypes
  • 在定义模型的时候,可以直接在参数中直接写getset函数,这样可以在获取/设置 之前进行一些处理,例如nickName中,实际获取的nickName后面还加上了id值,在email中,邮箱全被设置为大写了
  • 我们也可以在新建更新保存的时候进行一些验证,在字段中的validate属性中指定,比如在email中会验证是否是isEmail,如果在这三个操作之前没有检测到,在操作时候会抛出异常。Sequelize使用了validator.js进行验证。需要注意的是,如果一个字段允许为NULL,即allowNull:true,当值为NULL的时候,validate不会进行验证

4 模型使用

  • 新增文件sequelize/src/app/4-model-usage/index.js

    const sequelize = require('sequelize');
    const db = require('../../db');
    
    const { INTEGER, STRING, Op } = sequelize;
    
    const Role = db.define(
      'role',
      {
        role_id: {
          type: INTEGER.UNSIGNED,
          allowNull: false,
        },
        role_name: {
          type: STRING(32),
          allowNull: false,
          unique: true,
          validate: {
            min: 4,
            max: 32,
          },
        },
        level: {
          type: INTEGER,
          defaultValue: 1,
        },
      },
      {
        underscored: true,
        paranoid: true,
      }
    );
    
    const createTable = async () => {
      await Role.sync({
        force: true,
      });
    };
    
    const createRoles = async () => {
      for (let i = 1; i <= 5; i++) {
        await Role.create({
          role_id: i,
          role_name: `name-${i}`,
        });
      }
    };
    
    const findUsage = async () => {
      // 根据 id 查找
      const role1 = await Role.findById(1);
      console.log(`findUsage/findById id: ${role1.id}, name: ${role1.role_name}`);
    
      // 根据条件查找一条数据, level=1 的角色有多个
      const role2 = await Role.findOne({
        where: {
          level: 1,
        },
      });
      console.log(`findUsage/findOne id: ${role2.id}, name: ${role2.role_name}`);
    
      const role3 = await Role.findOne({
        where: {
          level: 1,
        },
        attributes: ['id', 'role_id'],
      });
      // 结果中并不包含 level
      console.log(
        `findUsage/findOne id: ${role3.id}, role_id: ${role3.role_id}, level: ${
          role3.level
        }`
      );
    
      // 如果数据库中没有,则会按照 defaults 生成一条数据, 并且 created 字段为 true
      // 如果数据库中有该数据,则 created 字段为 false
      // 返回的结果是一个数组, 第一个元素为搜索结果,第二个元素为 boolean,表示是否是新建的数据
      const [role4, created] = await Role.findOrCreate({
        where: {
          role_name: 'alex',
        },
        defaults: {
          role_id: 5,
          role_name: 'alex',
          level: 15,
        },
      });
      console.log(
        `findUsage/findOrCreate created: ${created}, role4: ${JSON.stringify(
          role4
        )}`
      );
    
      // 查找全部数据,并获得数量
      // 返回值 count: 数据个数
      // 返回值 rows: 包含数据的集合
      const result5 = await Role.findAndCountAll({
        where: {
          level: 1,
        },
      });
      console.log(
        `findUsage/findAndCountAll count: ${
          result5.count
        }, rows: ${JSON.stringify(result5.rows)}`
      );
    };
    
    const findAllUsage = async () => {
      // 查找全部, 可以在其中放入查询条件和限制条件
      console.log('\r\n');
      await Role.findAll({
        where: {
          id: [1, 2, 3, 4, 5],
        },
        limit: 3,
      });
    
      // all() 是 findAll 的别名
    
      console.log('\r\n');
      // where 中可以灵活的设置各种条件
      await Role.all({
        where: {
          level: {
            [Op.gt]: 1,
    
            // [Op.and]: {a: 5},           // AND (a = 5)
            // [Op.or]: [{a: 5}, {a: 6}],  // (a = 5 OR a = 6)
            // [Op.gt]: 6,                // id > 6
            // [Op.gte]: 6,               // id >= 6
            // [Op.lt]: 10,               // id < 10
            // [Op.lte]: 10,              // id <= 10
            // [Op.ne]: 20,               // id != 20
            // [Op.between]: [6, 10],     // BETWEEN 6 AND 10
            // [Op.notBetween]: [11, 15], // NOT BETWEEN 11 AND 15
            // [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'
          },
        },
        limit: 3,
      });
    
      // 也可以设置为 OR/NOT 等条件
      console.log('\r\n');
      // ((`role`.`id` IN (1, 2, 3) OR `role`.`id` > 10) AND `role`.`level` = 1)
      await Role.findAll({
        where: {
          level: 1,
          [Op.or]: [
            {
              id: [1, 2, 3],
            },
            {
              id: {
                [Op.gt]: 10,
              },
            },
          ],
        },
      });
      console.log('\r\n');
    
      // (`role`.`id` IN (1, 2, 3) OR `role`.`id` > 12))
      await Role.findAll({
        where: {
          level: 1,
          id: {
            [Op.or]: [
              [1, 2, 3],
              {
                [Op.gt]: 12,
              },
            ],
          },
        },
      });
    
      console.log('\r\n');
      // order 排序
      await Role.findAll({
        limit: 2,
        order: [
          ['id'],
          ['role_id', 'ASC'],
          // [sequelize.fn('max', sequelize.col('level')), 'DESC'],
        ],
        // 注意 raw, 默认为 false, 这时候 Sequelize 会为搜索出的每一条数据生成一个 Role 实例,用于更新,删除等操作
        // 但当我们只想搜索出数据用于显示,并不想操作它,这个时候设置 raw: true 就会直接返回数据,而不会生成实例
        raw: true,
      });
    };
    
    const someUsage = async () => {
      console.log('\r\n');
    
      // count
      const c1 = await Role.count();
      console.log(`someUsage/count c1: ${c1}`);
    
      const c2 = await Role.count({
        where: {
          level: {
            [Op.gt]: 1,
          },
        },
      });
      console.log(`someUsage/count c2: ${c2}`);
    
      // max, min
      const m1 = await Role.max('level', {
        where: {
          id: {
            [Op.gt]: 5,
          },
        },
      });
      console.log(`someUsage/max m1: ${m1}`);
    
      // sum
      const s1 = await Role.sum('level');
      console.log(`someUsage/sum s1: ${s1}`);
    };
    
    (async () => {
      console.log('------------- createTable');
      await createTable();
      console.log('------------- createRoles');
      await createRoles();
      console.log('------------- findUsage');
      await findUsage();
      console.log('------------- findAllUsage');
      await findAllUsage();
      console.log('------------- someUsage');
      await someUsage();
    })();
    

5 查询相关

  • SELECT
    • 通过attributes可以指定搜索的字段
      Role.findAll({
      attributes: ['role_id', 'role_name'],
      });
      // SELECT `role_id`, `role_name`
      
  • AS
    • 也可以在``
      Role.findAll({
      attributes: ['role_id', ['role_name', 'name']],
      });
      // SELECT `role_id`, `role_name` AS `name` ...
      
  • COUNT,AVG,MAX,MIN,SUM 等函数

    • 可以使用sequelize.fn来执行这些函数
    Role.findAll({
      attributes: [[sequelize.fn('COUNT', sequelize.col('*')), 'total_count']],
    });
    // SELECT COUNT(*) AS `total_count` ...
    
    Role.findAll({
      attributes: [[sequelize.fn('COUNT', 1), 'total_count']],
    });
    // SELECT COUNT(1) AS `total_count` ...
    
  • WHERE

    • 默认是 AND
    Role.findAll({
      where: {
        id: 5,
        level: 1,
      },
    });
    // ... where `role`.`id` = 5 AND `role`.`level` = 1
    
    • OR 的写法
    Role.findAll({
      where: {
        [Op.or]: [{ id: 2 }, { id: 3 }],
      },
    });
    // ... where `role`.`id` = 2 OR `role`.`id` = 3
    
    // 另一种写法
    Role.findAll({
      where: {
        id: {
          [Op.or]: [2, 3],
        },
      },
    });
    // ... where `role`.`id` = 2 OR `role`.`id` = 3
    
    // 不同字段的写法
    Role.findAll({
      where: {
        [Op.or]: [{ id: 2 }, { level: 3 }],
      },
    });
    // ... where `role`.`id` = 2 OR `role`.`level` = 3
    
  • 复杂点的写法
    Role.findAll({
      where: {
        [Op.or]: [{ id: 2 }, { level: 3 }],
        [Op.and]: [{ role_id: { [Op.ne]: 10001 } }],
      },
    });
    // ... where (`role`.`id` = 2 OR `role`.`level` = 3) AND (`role`.`role_id` != 10001)
    

6 实例

  • 可以创建一个没有写入数据库的实例,当然,创建之后也可以用save命令存入数据库中

    const role = Role.build({
      role_id: 1,
      role_name: 'name-1',
    });
    console.log(JSON.stringify(role)); // 可以发现, role.id 的值是 NULL
    await role.save(); // 在需要的时候调用 save 命令,可以将数据存入数据库中
    console.log(JSON.stringify(role)); // 这时候,role.id 有值了
    
  • 也可以直接使用create命令直接创建一条写入数据库的数据,这在前文中有多处这样的写法

    const role = await Role.create({
      role_id: 2,
      role_name: 'name-2',
    });
    console.log(JSON.stringify(role));
    
  • 批量创建实例

    • 使用bulkCreate来批量创建

      const l = [];
      for (let i = 0; i < 5; i++) {
        l.push({
          role_id: 1000 + i,
          role_name: `bulkname-${i}`,
        });
      }
      
      const result = await Role.bulkCreate(l);
      console.log('result:', JSON.stringify(result));
      // 输出
      // Executing (default): INSERT INTO `roles` (`id`,`role_id`,`role_name`,`level`,`created_at`,`updated_at`) VALUES (NULL,1000,'bulkname-0',1,'2018-10-15 15:38:29','2018-10-15 15:38:29'),(NULL,1001,'bulkname-1',1,'2018-10-15 15:38:29','2018-10-15 15:38:29'),(NULL,1002,'bulkname-2',1,'2018-10-15 15:38:29','2018-10-15 15:38:29'),(NULL,1003,'bulkname-3',1,'2018-10-15 15:38:29','2018-10-15 15:38:29'),(NULL,1004,'bulkname-4',1,'2018-10-15 15:38:29','2018-10-15 15:38:29');
      // result: [{"id":1,"role_id":1000,"role_name":"bulkname-0","level":1,"created_at":"2018-10-15T07:38:29.902Z","updated_at":"2018-10-15T07:38:29.902Z"},{"id":2,"role_id":1001,"role_name":"bulkname-1","level":1,"created_at":"2018-10-15T07:38:29.902Z","updated_at":"2018-10-15T07:38:29.902Z"},{"id":3,"role_id":1002,"role_name":"bulkname-2","level":1,"created_at":"2018-10-15T07:38:29.902Z","updated_at":"2018-10-15T07:38:29.902Z"},{"id":4,"role_id":1003,"role_name":"bulkname-3","level":1,"created_at":"2018-10-15T07:38:29.902Z","updated_at":"2018-10-15T07:38:29.902Z"},{"id":5,"role_id":1004,"role_name":"bulkname-4","level":1,"created_at":"2018-10-15T07:38:29.902Z","updated_at":"2018-10-15T07:38:29.902Z"}]
      
    • 限制字段

      const l = [];
      for (let i = 0; i < 5; i++) {
        l.push({
          role_id: 1000 + i,
          role_name: `bulkname-${i}`,
          level: i + 5,
        });
      }
      
      const result = await Role.bulkCreate(l, {
        // 这样创建语句中只有 role_id 和 role_name,会忽略 level
        fields: ['role_id', 'role_name'],
      });
      
  • 更新

    • 更新表中符合条件的数据(批量更新)

      const result = await Role.update(
        {
          level: 4,
        },
        {
          where: {},
        }
      );
      console.log('result: ', JSON.stringify(result));
      // 输出
      // Executing (default): UPDATE `roles` SET `level`=4,`updated_at`='2018-10-15 06:51:07' WHERE (`deleted_at` > '2018-10-15 06:51:07' OR `deleted_at` IS NULL)
      // result:  [2]
      
    • 更新具体的实例

      const role = await Role.findOne({
        where: {
          id: 1,
        },
      });
      const result2 = await role.update({
        level: 5,
      });
      // 输出
      // Executing (default): SELECT `id`, `role_id`, `role_name`, `level`, `created_at`, `updated_at`, `deleted_at` FROM `roles` AS `role` WHERE ((`role`.`deleted_at` > '2018-10-15 06:51:07' OR `role`.`deleted_at` IS NULL) AND `role`.`id` = 1);
      // Executing (default): UPDATE `roles` SET `level`=5,`updated_at`='2018-10-15 06:51:07' WHERE `id` = 1
      // result2:  {"id":1,"role_id":1,"role_name":"name-1","level":5,"created_at":"2018-10-15T06:36:26.000Z","updated_at":"2018-10-15T06:51:07.935Z","deleted_at":null}
      
    • 更新未写入数据库的数据,相当于创建了一条新数据

      const role3 = Role.build({
        role_id: 3,
        role_name: 'name-3',
      });
      const result3 = await role3.update({
        role_id: 4,
        role_name: 'name-4',
        level: 4,
      });
      console.log('result3: ', JSON.stringify(result3));
      // 实际上执行的是: INSERT INTO `roles` (`role_id`,`role_name`,`level`,`updated_at`,`created_at`) VALUES (4,'name-4',4,'2018-10-15 06:57:28','2018-10-15 06:57:28')
      
  • 删除

    • 如果我们启用了软删除,即设置了paranoid:true(需要timestamps:true),则不会真正的将数据从数据库中删除,而是设置了deleted_at
    • 将表中符合条件的数据删除(批量删除)

      const result1 = await Role.destroy({
        where: {
          id: 1,
        },
      });
      console.log('result1:', result1);
      // 输出
      // Executing (default): UPDATE `roles` SET `deleted_at`='2018-10-15 15:02:50' WHERE `deleted_at` IS NULL AND `id` = 1
      // result1: 1
      
    • 将具体的实例进行删除,这里设置了force:true,进行硬删除,把数据从数据库中删除

      const role2 = await Role.findOne({
        where: {
          id: 2,
        },
      });
      // 可以设置 force: true, 使得数据真正从数据库中删除
      const result2 = await role2.destroy({
        force: true,
      });
      console.log('result2:', result2);
      // 输出
      // DELETE FROM `roles` WHERE `id` = 2 LIMIT 1
      
    • 删除未写入数据库的实例进行删除

      // 软删除
      Role.build({
        role_id: 5,
        role_name: 'name-5',
      }).destroy();
      // 输出
      // UPDATE `roles` SET `deleted_at`='2018-10-15 15:14:28' WHERE `id` IS NULL AND `deleted_at` IS NULL
      
      // 硬删除
      Role.build({
        role_id: 5,
        role_name: 'name-5',
      }).destroy({
        force: true,
      });
      // 输出
      // DELETE FROM `roles` WHERE `id` IS NULL LIMIT 1
      
  • 增减

    • 增加值使用increment, 减少值使用decrement,用法相同
    • level加 5,可以看到result中的level还是原来的数据

      const role = await Role.findById(1);
      const result = await role.increment('level', {
        by: 5,
      });
      console.log('result:', JSON.stringify(result));
      // 输出
      // Executing (default): UPDATE `roles` SET `level`=`level`+ 5,`updated_at`='2018-10-15 16:04:35' WHERE `id` = 1
      // result: {"id":1,"role_id":1000,"role_name":"bulkname-0","level":1,"created_at":"2018-10-15T07:52:06.000Z","updated_at":"2018-10-15T07:52:06.000Z","deleted_at":null}
      

7 事务

  • 事务分为两种:
    • 一种需要我们手动执行commitrollback命令进行提交和回滚
    • 另一种是自动管理的,只要返回值都是成功resolved,则会一个个执行下去,一旦出现一个rejected,就会自行回滚

8 钩子

  • 钩子列表

    (1)
      beforeBulkCreate(instances, options)
      beforeBulkDestroy(options)
      beforeBulkUpdate(options)
    (2)
      beforeValidate(instance, options)
    (-)
      validate
    (3)
      afterValidate(instance, options)
      - or -
      validationFailed(instance, options, error)
    (4)
      beforeCreate(instance, options)
      beforeDestroy(instance, options)
      beforeUpdate(instance, options)
      beforeSave(instance, options)
      beforeUpsert(values, options)
    (-)
      create
      destroy
      update
    (5)
      afterCreate(instance, options)
      afterDestroy(instance, options)
      afterUpdate(instance, options)
      afterSave(instance, options)
      afterUpsert(created, options)
    (6)
      afterBulkCreate(instances, options)
      afterBulkDestroy(options)
      afterBulkUpdate(options)
    
  • 完整列表见: Hooks file
  • 新增文件sequelize/src/app/7-hooks/index.js

    const sequelize = require('sequelize');
    const db = require('../../db');
    
    const { INTEGER, STRING } = sequelize;
    
    const Role = db.define(
      'role',
      {
        role_id: {
          type: INTEGER.UNSIGNED,
          allowNull: false,
        },
        role_name: {
          type: STRING(32),
          allowNull: false,
          unique: true,
          validate: {
            min: 4,
            max: 32,
          },
        },
        level: {
          type: INTEGER,
          defaultValue: 1,
        },
      },
      {
        underscored: true,
        paranoid: true,
        // 使用钩子方法1: 在定义的时候新增钩子
        hooks: {
          beforeCreate: (role, options) => {
            console.log('beforeCreate ...');
            role.level = 100;
          },
          // 可以写多个钩子
        },
      }
    );
    
    // 使用钩子方法2
    Role.hook('beforeValidate', (role, options) => {
      console.log('beforeValidate ...');
    });
    
    // 使用钩子方法3
    Role.afterCreate((role, options) => {
      console.log('afterCreate ...');
      console.log('role created success,', JSON.stringify(role));
    });
    
    const createTable = async () => {
      await Role.sync({
        force: true,
      });
    };
    
    const createRole = async () => {
      await Role.create({
        role_id: 1,
        role_name: 'name-1',
      });
    
      // 输出
      // beforeValidate ...
      // beforeCreate ...
      // Executing (default): INSERT INTO `roles` (`id`,`role_id`,`role_name`,`level`,`created_at`,`updated_at`) VALUES (DEFAULT,1,'name-1',100,'2018-10-15 17:08:52','2018-10-15 17:08:52');
      // afterCreate ...
      // role created success, {"level":100,"id":1,"role_id":1,"role_name":"name-1","updated_at":"2018-10-15T09:08:52.923Z","created_at":"2018-10-15T09:08:52.923Z"}
    };
    
    (async () => {
      console.log('------------- createTable');
      await createTable();
      console.log('------------- createRole');
      await createRole();
    })();
    

9 SQL 语句

  • sequelize中还可以直接使用sql语句,但在测试报错: TypeError: sequelize.query is not a function,因此后续还有个功能: 替换,用 ? 或者 :key 替换值 就没有继续研究了
  • 新增文件sequelize/src/app/8-sql/index.js

    const sequelize = require('sequelize');
    const db = require('../../db');
    
    const { INTEGER, STRING } = sequelize;
    
    const Role = db.define(
      'role',
      {
        role_id: {
          type: INTEGER.UNSIGNED,
          allowNull: false,
        },
        role_name: {
          type: STRING(32),
          allowNull: false,
          unique: true,
          validate: {
            min: 4,
            max: 32,
          },
        },
        level: {
          type: INTEGER,
          defaultValue: 1,
        },
      },
      {
        underscored: true,
        paranoid: true,
      }
    );
    
    const createTable = async () => {
      await Role.sync({
        force: true,
      });
    };
    
    const createRoles = async () => {
      const l = [];
      for (let i = 1; i <= 5; i++) {
        l.push({
          role_id: i,
          role_name: `name-${i}`,
          level: i + 5,
        });
      }
      await Role.bulkCreate(l);
    };
    
    const rawQueryUsage = async () => {
      // 实际上报错: 找不到 sequelize.query 这个函数
    
      const result1 = await sequelize.query('SELECT * FROM roles');
      console.log('result 1:', JSON.stringify(result1));
      console.log('\r\n');
      const result2 = await sequelize.query('SELECT * FROM roles', {
        type: sequelize.QueryTypes.SELECT,
      });
      console.log('result 2:', JSON.stringify(result2));
    };
    
    (async () => {
      console.log('------------- createTable');
      await createTable();
      console.log('------------- createRoles');
      await createRoles();
      console.log('------------- rawQueryUsage');
      await rawQueryUsage();
    })();