欢迎来到我的博客

vuePress-theme-reco hbbaly    2021
欢迎来到我的博客

Choose mode

  • dark
  • auto
  • light
首页
时间轴
标签
分类
  • 前端
GitHub
author-avatar

hbbaly

31

Article

18

Tag

首页
时间轴
标签
分类
  • 前端
GitHub
  • 【Node】sequelize

    • 安装
      • 建立连接
        • 连接池 (生产环境)
          • 表建模
            • 自定义模型参数
              • 将模型与数据库同步
                • 扩展模型
                  • 数据类型
                    • Getters & setters
                      • 用于 getter 和 setter 定义内部的 Helper 方法
                    • 验证
                      • 属性验证器
                    • 配置
                      • 数据检索
                        • find
                        • findOrCreate
                        • findAndCountAll
                        • findAll
                        • 复合过滤 / OR / NOT 查询
                        • 用限制,偏移,顺序和分组操作数据集
                        • count
                        • max
                        • min
                        • sum
                      • Hooks - 钩子
                        • 声明 Hook
                        • 移除 Hook
                        • 全局 / 通用 Hook
                        • 实例 Hook
                        • 模型 Hook
                      • Querying - 查询
                        • 属性
                        • Where
                        • 操作符
                      • Instances - 实例
                        • 构建非持久性实例
                        • 创建持久性实例
                        • 更新 / 保存 / 持久化一个实例
                        • 销毁 / 删除持久性实例
                        • 恢复软删除的实例
                        • 一个实例的值
                        • 重载实例
                        • 递增
                        • 递减
                      • 批量操作(一次性创建,更新和销毁多行)

                      【Node】sequelize

                      vuePress-theme-reco hbbaly    2021

                      【Node】sequelize


                      hbbaly 2019-08-15 19:00:00 NodeSequelize

                      # sequelize

                      参考资料

                      # 安装

                      npm install --save sequelize
                      

                      使用mysql所以还要安装

                      npm install --save mysql2
                      

                      # 建立连接

                      const Sequelize = require('sequelize');
                      
                      const sequelize = new Sequelize('database', 'username', 'password', {
                        host: 'localhost',
                        port: 3306, //端口号,默认3306
                        dialect: /* 'mysql' | 'mariadb' | 'postgres' | 'mssql' 之一 */
                      });
                      
                      const sequelize = new Sequelize('postgres://user:pass@example.com:port/dbname');
                      

                      # 连接池 (生产环境)

                      const sequelize = new Sequelize(/* ... */, {
                        // ...
                        pool: {
                          max: 5,
                          min: 0,
                          acquire: 30000,
                          idle: 10000
                        }
                      });
                      

                      如果你希望最大连接池大小为 90 并且你有三个进程,则每个进程的 Sequelize 实例的最大连接池大小应为 30.

                      # 表建模

                      有两种方式,不过不推荐使用 define ,使用类方法建模.

                      const { Model } = Sequelize;
                      class User extends Model {}
                      User.init({
                        // 属性
                        firstName: {
                          type: Sequelize.STRING,
                          allowNull: false
                        },
                        lastName: {
                          type: Sequelize.STRING
                          // allowNull 默认为 true
                        }
                      }, {
                        sequelize,
                        modelName: 'user'
                        // 参数
                      });
                      

                      Sequelize 还默认为每个模型定义了字段id(主键),createdAt和updatedAt

                      你也可以添加很多参数

                      User.init({
                       // 如果未赋值,则自动设置值为 TRUE
                       flag: { type: Sequelize.BOOLEAN, allowNull: false, defaultValue: true},
                      
                       // 设置默认时间为当前时间
                       myDate: { type: Sequelize.DATE, defaultValue: Sequelize.NOW },
                      
                       // 将allowNull设置为false会将NOT NULL添加到列中,
                       // 这意味着当列为空时执行查询时将从DB抛出错误. 
                       // 如果要在查询DB之前检查值不为空,请查看下面的验证部分.
                       title: { type: Sequelize.STRING, allowNull: false},
                      
                       // 创建具有相同值的两个对象将抛出一个错误. 唯一属性可以是布尔值或字符串.
                       // 如果为多个列提供相同的字符串,则它们将形成复合唯一键.
                       uniqueOne: { type: Sequelize.STRING,  unique: 'compositeIndex'},
                       uniqueTwo: { type: Sequelize.INTEGER, unique: 'compositeIndex'},
                      
                       // unique属性用来创建一个唯一约束.
                       someUnique: {type: Sequelize.STRING, unique: true},
                       
                       // 这与在模型选项中创建索引完全相同.
                       {someUnique: {type: Sequelize.STRING}},
                       {indexes: [{unique: true, fields: ['someUnique']}]},
                      
                       // primaryKey用于定义主键.
                       identifier: { type: Sequelize.STRING, primaryKey: true},
                      
                       // autoIncrement可用于创建自增的整数列
                       incrementMe: { type: Sequelize.INTEGER, autoIncrement: true },
                      
                       // 你可以通过'field'属性指定自定义列名称:
                       fieldWithUnderscores: { type: Sequelize.STRING, field: 'field_with_underscores' },
                      
                       // 这可以创建一个外键:
                       bar_id: {
                         type: Sequelize.INTEGER,
                      
                         references: {
                           // 这是引用另一个模型
                           model: Bar,
                      
                           // 这是引用模型的列名称
                           key: 'id',
                      
                           // 这声明什么时候检查外键约束. 仅限PostgreSQL.
                           deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE
                         }
                       },
                      
                       // 仅可以为 MySQL,PostgreSQL 和 MSSQL 的列添加注释
                       commentMe: {
                         type: Sequelize.INTEGER,
                      
                         comment: '这是一个包含注释的列名'
                       }
                      }, {
                        sequelize,
                        modelName: 'user'
                      });
                      

                      # 自定义模型参数

                      const sequelize = new Sequelize(/* ... */, {
                        // ...
                      define: {
                          // 显示 create_time, update_time
                          timestamps: true,
                          // delete_time
                          paranoid: true,
                          createdAt: 'created_at',
                          updatedAt: 'updated_at',
                          deletedAt: 'deleted_at',
                          underscored: true ,  // 吧所有驼峰转化为下划线
                          scopes:{
                            'bh':{
                              attributes: {
                                exclude: ['updated_at', 'deleted_at','created_at']
                              },
                            }
                          }
                        }
                      })
                      

                      # 将模型与数据库同步

                      // 创建表:
                      Project.sync()
                      Task.sync()
                      
                      // 强制创建!
                      Project.sync({force: true}) // 这将先丢弃表,然后重新创建它
                      
                      // 删除表:
                      Project.drop()
                      Task.drop()
                      
                      // 事件处理:
                      Project.[sync|drop]().then(() => {
                        // 好吧...一切都很好!
                      }).catch(error => {
                        // oooh,你输入了错误的数据库凭据?
                      })
                      

                      因为同步和删除所有的表可能要写很多行,你也可以让Sequelize来为做这些

                      // 同步所有尚未在数据库中的模型
                      sequelize.sync()
                      
                      // 强制同步所有模型
                      sequelize.sync({force: true})
                      
                      // 删除所有表
                      sequelize.drop()
                      
                      // 广播处理:
                      sequelize.[sync|drop]().then(() => {
                        // woot woot
                      }).catch(error => {
                        // whooops
                      })
                      

                      因为.sync({ force: true })是具有破坏性的操作,可以使用match参数作为附加的安全检查.

                      match参数可以通知Sequelize,以便在同步之前匹配正则表达式与数据库名称 - 在测试中使用force:true但不使用实时代码的情况下的安全检查.

                      // 只有当数据库名称以'_test'结尾时,才会运行.sync()
                      sequelize.sync({ force: true, match: /_test$/ });
                      

                      # 扩展模型

                      Sequelize 模型是ES6类. 你可以轻松添加自定义实例或类级别的方法

                      class User extends Model {
                        // 添加一个类级别的方法
                        static classLevelMethod() {
                          return 'foo';
                        }
                      
                        // 添加实例级别方法
                        instanceLevelMethod() {
                          return 'bar';
                        }
                      }
                      User.init({ firstname: Sequelize.STRING }, { sequelize });
                      

                      当然,你还可以访问实例的数据并生成虚拟的getter:

                      class User extends Model {
                        getFullname() {
                          return [this.firstname, this.lastname].join(' ');
                        }
                      }
                      User.init({ firstname: Sequelize.STRING, lastname: Sequelize.STRING }, { sequelize });
                      
                      // 示例:
                      User.build({ firstname: 'foo', lastname: 'bar' }).getFullname() // 'foo bar'
                      

                      # 数据类型

                      Sequelize.STRING                      // VARCHAR(255)
                      Sequelize.STRING(1234)                // VARCHAR(1234)
                      Sequelize.STRING.BINARY               // VARCHAR BINARY
                      Sequelize.TEXT                        // TEXT
                      Sequelize.TEXT('tiny')                // TINYTEXT
                      Sequelize.CITEXT                      // CITEXT      仅 PostgreSQL 和 SQLite.
                      
                      Sequelize.INTEGER                     // INTEGER
                      Sequelize.BIGINT                      // BIGINT
                      Sequelize.BIGINT(11)                  // BIGINT(11)
                      
                      Sequelize.FLOAT                       // FLOAT
                      Sequelize.FLOAT(11)                   // FLOAT(11)
                      Sequelize.FLOAT(11, 10)               // FLOAT(11,10)
                      
                      Sequelize.REAL                        // REAL        仅 PostgreSQL.
                      Sequelize.REAL(11)                    // REAL(11)    仅 PostgreSQL.
                      Sequelize.REAL(11, 12)                // REAL(11,12) 仅 PostgreSQL.
                      
                      Sequelize.DOUBLE                      // DOUBLE
                      Sequelize.DOUBLE(11)                  // DOUBLE(11)
                      Sequelize.DOUBLE(11, 10)              // DOUBLE(11,10)
                      
                      Sequelize.DECIMAL                     // DECIMAL
                      Sequelize.DECIMAL(10, 2)              // DECIMAL(10,2)
                      
                      Sequelize.DATE                        // mysql / sqlite 为 DATETIME, postgres 为带时区的 TIMESTAMP
                      Sequelize.DATE(6)                     // DATETIME(6) 适用 mysql 5.6.4+. 小数秒支持最多6位精度
                      Sequelize.DATEONLY                    // DATE 不带时间.
                      Sequelize.BOOLEAN                     // TINYINT(1)
                      
                      Sequelize.ENUM('value 1', 'value 2')  // 一个允许值为'value 1'和'value 2'的ENUM
                      Sequelize.ARRAY(Sequelize.TEXT)       // 定义一个数组. 仅 PostgreSQL.
                      Sequelize.ARRAY(Sequelize.ENUM)       // 定义一个ENUM数组. 仅 PostgreSQL.
                      
                      Sequelize.JSON                        // JSON 列. 仅 PostgreSQL, SQLite 和 MySQL.
                      Sequelize.JSONB                       // JSONB 列. 仅 PostgreSQL.
                      
                      Sequelize.BLOB                        // BLOB (PostgreSQL 为 bytea)
                      Sequelize.BLOB('tiny')                // TINYBLOB (PostgreSQL 为 bytea. 其余参数是 medium 和 long)
                      
                      Sequelize.UUID                        // PostgreSQL 和 SQLite 的 UUID 数据类型,MySQL 的 CHAR(36) BINARY(使用defaultValue:Sequelize.UUIDV1 或 Sequelize.UUIDV4 来让 sequelize 自动生成 id).
                      
                      Sequelize.CIDR                        // PostgreSQL 的 CIDR 数据类型
                      Sequelize.INET                        // PostgreSQL 的 INET 数据类型
                      Sequelize.MACADDR                     // PostgreSQL 的 MACADDR 数据类型
                      
                      Sequelize.RANGE(Sequelize.INTEGER)    // 定义 int4range 范围. 仅 PostgreSQL.
                      Sequelize.RANGE(Sequelize.BIGINT)     // 定义 int8range 范围. 仅 PostgreSQL.
                      Sequelize.RANGE(Sequelize.DATE)       // 定义 tstzrange 范围. 仅 PostgreSQL.
                      Sequelize.RANGE(Sequelize.DATEONLY)   // 定义 daterange 范围. 仅 PostgreSQL.
                      Sequelize.RANGE(Sequelize.DECIMAL)    // 定义 numrange 范围. 仅 PostgreSQL.
                      
                      Sequelize.ARRAY(Sequelize.RANGE(Sequelize.DATE)) // 定义 tstzrange 范围的数组. 仅 PostgreSQL.
                      
                      Sequelize.GEOMETRY                    // Spatial 列. 仅 PostgreSQL (带有 PostGIS) 或 MySQL.
                      Sequelize.GEOMETRY('POINT')           // 带有 geometry 类型的 spatial 列. 仅 PostgreSQL (带有 PostGIS) 或 MySQL.
                      Sequelize.GEOMETRY('POINT', 4326)     // 具有 geometry 类型和 SRID 的 spatial 列. 仅 PostgreSQL (带有 PostGIS) 或 MySQL.
                      
                      

                      # Getters & setters

                      对象属性getter和setter函数,这些可以用于映射到数据库字段的“保护”属性,也可以用于定义“伪”属性.

                      User.init({
                        name: {
                          type: Sequelize.STRING,
                          allowNull: false,
                          get() {
                            const title = this.getDataValue('title');
                            // 'this' 允许你访问实例的属性
                            return this.getDataValue('name') + ' (' + title + ')';
                          },
                        },
                        title: {
                          type: Sequelize.STRING,
                          allowNull: false,
                          set(val) {
                            this.setDataValue('title', val.toUpperCase());
                          }
                        }
                      }, { sequelize, modelName: 'employee' });
                      
                      User
                        .create({ name: 'John Doe', title: 'senior engineer' })
                        .then(user => {
                          console.log(user.get('name')); // John Doe (SENIOR ENGINEER)
                          console.log(user.get('title')); // SENIOR ENGINEER
                        })
                      

                      # 用于 getter 和 setter 定义内部的 Helper 方法

                      检索底层属性值 - 总是使用 this.getDataValue()

                      /* 一个用于 'title' 属性的 getter */
                      get() {
                        return this.getDataValue('title')
                      }
                      

                      设置基础属性值 - 总是使用 this.setDataValue()

                      /* 一个用于 'title' 属性的 setter */
                      set(title) {
                        this.setDataValue('title', title.toString().toLowerCase());
                      }
                      

                      # 验证

                      模型验证允许你为模型的每个属性指定格式/内容/继承验证

                      验证会自动运行在 create , update 和 save 上. 你也可以调用 validate() 手动验证一个实例

                      # 属性验证器

                      你可以自定义验证器或使用由validator.js实现的几个内置验证器

                      class ValidateMe extends Model {}
                      ValidateMe.init({
                        bar: {
                          type: Sequelize.STRING,
                          validate: {
                            is: ["^[a-z]+$",'i'],     // 只允许字母
                            is: /^[a-z]+$/i,          // 与上一个示例相同,使用了真正的正则表达式
                            not: ["[a-z]",'i'],       // 不允许字母
                            isEmail: true,            // 检查邮件格式 (foo@bar.com)
                            isUrl: true,              // 检查连接格式 (http://foo.com)
                            isIP: true,               // 检查 IPv4 (129.89.23.1) 或 IPv6 格式
                            isIPv4: true,             // 检查 IPv4 (129.89.23.1) 格式
                            isIPv6: true,             // 检查 IPv6 格式
                            isAlpha: true,            // 只允许字母
                            isAlphanumeric: true,     // 只允许使用字母数字
                            isNumeric: true,          // 只允许数字
                            isInt: true,              // 检查是否为有效整数
                            isFloat: true,            // 检查是否为有效浮点数
                            isDecimal: true,          // 检查是否为任意数字
                            isLowercase: true,        // 检查是否为小写
                            isUppercase: true,        // 检查是否为大写
                            notNull: true,            // 不允许为空
                            isNull: true,             // 只允许为空
                            notEmpty: true,           // 不允许空字符串
                            equals: 'specific value', // 只允许一个特定值
                            contains: 'foo',          // 检查是否包含特定的子字符串
                            notIn: [['foo', 'bar']],  // 检查是否值不是其中之一
                            isIn: [['foo', 'bar']],   // 检查是否值是其中之一
                            notContains: 'bar',       // 不允许包含特定的子字符串
                            len: [2,10],              // 只允许长度在2到10之间的值
                            isUUID: 4,                // 只允许uuids
                            isDate: true,             // 只允许日期字符串
                            isAfter: "2011-11-05",    // 只允许在特定日期之后的日期字符串
                            isBefore: "2011-11-05",   // 只允许在特定日期之前的日期字符串
                            max: 23,                  // 只允许值 <= 23
                            min: 23,                  // 只允许值 >= 23
                            isCreditCard: true,       // 检查有效的信用卡号码
                      
                            // 自定义验证器的示例:
                            isEven(value) {
                              if (parseInt(value) % 2 !== 0) {
                                throw new Error('Only even values are allowed!');
                              }
                            }
                            isGreaterThanOtherField(value) {
                              if (parseInt(value) <= parseInt(this.otherField)) {
                                throw new Error('Bar must be greater than otherField.');
                              }
                            }
                          }
                        }
                      }, { sequelize });
                      

                      # 配置

                      class User extends Model {}
                      User.init({ /* bla */ }, {
                        // 模型的名称. 该模型将以此名称存储在`sequelize.models`中.
                        // 在这种情况下,默认为类名,即Bar. 
                        // 这将控制自动生成的foreignKey和关联命名的名称
                        modelName: 'bar',
                        // 不添加时间戳属性 (updatedAt, createdAt)
                        timestamps: false,
                      
                        // 不删除数据库条目,但将新添加的属性deletedAt设置为当前日期(删除完成时). 
                        // paranoid 只有在启用时间戳时才能工作
                        paranoid: true,
                      
                        // 将自动设置所有属性的字段参数为下划线命名方式.
                        // 不会覆盖已经定义的字段选项
                        underscored: true,
                      
                        // 禁用修改表名; 默认情况下,sequelize将自动将所有传递的模型名称(define的第一个参数)转换为复数. 如果你不想这样,请设置以下内容
                        freezeTableName: true,
                      
                        // 定义表的名称
                        tableName: 'my_very_custom_table_name',
                      
                        // 启用乐观锁定. 启用时,sequelize将向模型添加版本计数属性,
                        // 并在保存过时的实例时引发OptimisticLockingError错误.
                        // 设置为true或具有要用于启用的属性名称的字符串.
                        version: true,
                      
                        // Sequelize 实例
                        sequelize,
                      })
                      

                      如果你希望sequelize处理时间戳,但只想要其中一部分,或者希望你的时间戳被称为别的东西,则可以单独覆盖每个列:

                      class User extends Model {}
                      User.init({ /* bla */ }, {
                        // 不要忘记启用时间戳!
                        timestamps: true,
                      
                        // 我不想要 createdAt
                        createdAt: false,
                      
                        // 我想 updateAt 实际上被称为 updateTimestamp
                        updatedAt: 'updateTimestamp',
                      
                        // 并且希望 deletedA t被称为 destroyTime(请记住启用paranoid以使其工作)
                        deletedAt: 'destroyTime',
                        paranoid: true,
                      
                        sequelize,
                      })
                      

                      # 数据检索

                      # find

                      • 搜索数据库中的一个特定元素
                      // 搜索已知的ids
                      Project.findByPk(123).then(project => {
                        // project 将是 Project的一个实例,并具有在表中存为 id 123 条目的内容.
                        // 如果没有定义这样的条目,你将获得null
                      })
                      
                      // 搜索属性
                      Project.findOne({ where: {title: 'aProject'} }).then(project => {
                        // project 将是 Projects 表中 title 为 'aProject'  的第一个条目 || null
                      })
                      
                      
                      Project.findOne({
                        where: {title: 'aProject'},
                        attributes: ['id', ['name', 'title']]
                      }).then(project => {
                        // project 将是 Projects 表中 title 为 'aProject'  的第一个条目 || null
                        // project.get('title') 将包含 project 的 name
                      })
                      
                      

                      # findOrCreate

                      • 搜索特定元素或创建它

                      方法 findOrCreate 可用于检查数据库中是否已存在某个元素. 如果是这种情况,则该方法将生成相应的实例. 如果元素不存在,将会被创建.

                      如果是这种情况,则该方法将导致相应的实例. 如果元素不存在,将会被创建.

                      有一个空的数据库,一个 User 模型有一个 username 和 job

                      User
                        .findOrCreate({where: {username: 'sdepold'}, defaults: {job: 'Technical Lead JavaScript'}})
                        . then(([user, created]) => {
                          console.log(user.get({
                            plain: true
                          }))
                          console.log(created)
                      
                          /*
                          findOrCreate 返回一个包含已找到或创建的对象的数组,找到或创建的对象和一个布尔值,如果创建一个新对象将为true,否则为false,像这样:
                      
                          [ {
                              username: 'sdepold',
                              job: 'Technical Lead JavaScript',
                              id: 1,
                              createdAt: Fri Mar 22 2013 21: 28: 34 GMT + 0100(CET),
                              updatedAt: Fri Mar 22 2013 21: 28: 34 GMT + 0100(CET)
                            },
                            true ]
                      
                      在上面的例子中,第三行的数组将分成2部分,并将它们作为参数传递给回调函数,在这种情况下将它们视为 "user" 和 "created" .(所以“user”将是返回数组的索引0的对象,并且 "created" 将等于 "true".)
                          */
                        })
                      

                      # findAndCountAll

                      • 在数据库中搜索多个元素,返回数据和总计数

                      这是一个方便的方法,它结合了 findAll 和 count(见下文),当处理与分页相关的查询时,这是有用的,你想用 limit 和 offset 检索数据,但也需要知道总数与查询匹配的记录数

                      处理程序成功将始终接收具有两个属性的对象:

                      • count - 一个整数,总数记录匹配where语句和关联的其它过滤器

                      • rows - 一个数组对象,记录在limit和offset范围内匹配where语句和关联的其它过滤器

                      Project
                        .findAndCountAll({
                           where: {
                              title: {
                                [Op.like]: 'foo%'
                              }
                           },
                           offset: 10,
                           limit: 2
                        })
                        .then(result => {
                          console.log(result.count);
                          console.log(result.rows);
                        });
                      

                      # findAll

                      • 搜索数据库中的多个元素
                      // 找到多个条目
                      Project.findAll().then(projects => {
                        // projects 将是所有 Project 实例的数组
                      })
                      
                      // 搜索特定属性 - 使用哈希
                      Project.findAll({ where: { name: 'A Project' } }).then(projects => {
                        // projects将是一个具有指定 name 的 Project 实例数组
                      })
                      
                      // 在特定范围内进行搜索
                      Project.findAll({ where: { id: [1,2,3] } }).then(projects => {
                        // projects将是一系列具有 id 1,2 或 3 的项目
                        // 这实际上是在做一个 IN 查询
                      })
                      
                      Project.findAll({
                        where: {
                          id: {
                            [Op.and]: {a: 5},           // 且 (a = 5)
                            [Op.or]: [{a: 5}, {a: 6}],  // (a = 5 或 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],     // 在 6 和 10 之间
                            [Op.notBetween]: [11, 15], // 不在 11 和 15 之间
                            [Op.in]: [1, 2],           // 在 [1, 2] 之中
                            [Op.notIn]: [1, 2],        // 不在 [1, 2] 之中
                            [Op.like]: '%hat',         // 包含 '%hat'
                            [Op.notLike]: '%hat',       // 不包含 '%hat'
                            [Op.iLike]: '%hat',         // 包含 '%hat' (不区分大小写)  (仅限 PG)
                            [Op.notILike]: '%hat',      // 不包含 '%hat'  (仅限 PG)
                            [Op.overlap]: [1, 2],       // && [1, 2] (PG数组重叠运算符)
                            [Op.contains]: [1, 2],      // @> [1, 2] (PG数组包含运算符)
                            [Op.contained]: [1, 2],     // <@ [1, 2] (PG数组包含于运算符)
                            [Op.any]: [2,3],            // 任何数组[2, 3]::INTEGER (仅限 PG)
                          },
                          status: {
                            [Op.not]: false,           // status 不为 FALSE
                          }
                        }
                      })
                      

                      # 复合过滤 / OR / NOT 查询

                      你可以使用多层嵌套的 AND,OR 和 NOT 条件进行一个复合的 where 查询.

                      Project.findOne({
                        where: {
                          name: 'a project',
                          [Op.or]: [
                            { id: [1,2,3] },
                            { id: { [Op.gt]: 10 } }
                          ]
                        }
                      })
                      
                      Project.findOne({
                        where: {
                          name: 'a project',
                          id: {
                            [Op.or]: [
                              [1,2,3],
                              { [Op.gt]: 10 }
                            ]
                          }
                        }
                      })
                      

                      # 用限制,偏移,顺序和分组操作数据集

                      // 限制查询的结果
                      Project.findAll({ limit: 10 })
                      
                      // 跳过前10个元素
                      Project.findAll({ offset: 10 })
                      
                      // 跳过前10个元素,并获取2个
                      Project.findAll({ offset: 10, limit: 2 })
                      
                      Project.findAll({order: [['title', 'DESC']]})
                      // 生成 ORDER BY title DESC
                      
                      Project.findAll({group: 'name'})
                      // 生成 GROUP BY name
                      
                      something.findOne({
                        order: [
                          // 将返回 `name`
                          ['name'],
                          // 将返回 `username` DESC
                          ['username', 'DESC'],
                          // 将返回 max(`age`)
                          sequelize.fn('max', sequelize.col('age')),
                          // 将返回 max(`age`) DESC
                          [sequelize.fn('max', sequelize.col('age')), 'DESC'],
                          // 将返回 otherfunction(`col1`, 12, 'lalala') DESC
                          [sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],
                          // 将返回 otherfunction(awesomefunction(`col`)) DESC,这个嵌套是可以无限的!
                          [sequelize.fn('otherfunction', sequelize.fn('awesomefunction', sequelize.col('col'))), 'DESC']
                        ]
                      })
                      

                      # count

                      • 计算数据库中元素的出现次数
                      Project.count().then(c => {
                        console.log("There are " + c + " projects!")
                      })
                      
                      Project.count({ where: {'id': {[Op.gt]: 25}} }).then(c => {
                        console.log("There are " + c + " projects with an id greater than 25.")
                      })
                      

                      # max

                      • 获取特定表中特定属性的最大值
                      /*
                         我们假设3个具有属性年龄的对象.
                         第一个是10岁,
                         第二个是5岁,
                         第三个是40岁.
                      */
                      Project.max('age').then(max => {
                        // 将返回 40
                      })
                      
                      Project.max('age', { where: { age: { [Op.lt]: 20 } } }).then(max => {
                        // 将会是 10
                      })
                      

                      # min

                      • 获取特定表中特定属性的最小值
                      /*
                         我们假设3个具有属性年龄的对象.
                         第一个是10岁,
                         第二个是5岁,
                         第三个是40岁.
                      */
                      Project.min('age').then(min => {
                        // 将返回 5
                      })
                      
                      Project.min('age', { where: { age: { [Op.gt]: 5 } } }).then(min => {
                        // 将会是 10
                      })
                      

                      # sum

                      • 特定属性的值求和
                      /*
                         我们假设3个具有属性年龄的对象.
                         第一个是10岁,
                         第二个是5岁,
                         第三个是40岁.
                      */
                      Project.sum('age').then(sum => {
                        // 将返回 55
                      })
                      
                      Project.sum('age', { where: { age: { [Op.gt]: 5 } } }).then(sum => {
                        // 将会是 50
                      })
                      

                      # Hooks - 钩子

                      Hook(也称为生命周期事件)是执行 sequelize 调用之前和之后调用的函数

                      const hookTypes = {
                        beforeValidate: { params: 2 },
                        afterValidate: { params: 2 },
                        validationFailed: { params: 3 },
                        beforeCreate: { params: 2 },
                        afterCreate: { params: 2 },
                        beforeDestroy: { params: 2 },
                        afterDestroy: { params: 2 },
                        beforeRestore: { params: 2 },
                        afterRestore: { params: 2 },
                        beforeUpdate: { params: 2 },
                        afterUpdate: { params: 2 },
                        beforeSave: { params: 2, proxies: ['beforeUpdate', 'beforeCreate'] },
                        afterSave: { params: 2, proxies: ['afterUpdate', 'afterCreate'] },
                        beforeUpsert: { params: 2 },
                        afterUpsert: { params: 2 },
                        beforeBulkCreate: { params: 2 },
                        afterBulkCreate: { params: 2 },
                        beforeBulkDestroy: { params: 1 },
                        afterBulkDestroy: { params: 1 },
                        beforeBulkRestore: { params: 1 },
                        afterBulkRestore: { params: 1 },
                        beforeBulkUpdate: { params: 1 },
                        afterBulkUpdate: { params: 1 },
                        beforeFind: { params: 1 },
                        beforeFindAfterExpandIncludeAll: { params: 1 },
                        beforeFindAfterOptions: { params: 1 },
                        afterFind: { params: 2 },
                        beforeCount: { params: 1 },
                        beforeDefine: { params: 2, sync: true, noModel: true },
                        afterDefine: { params: 1, sync: true, noModel: true },
                        beforeInit: { params: 2, sync: true, noModel: true },
                        afterInit: { params: 1, sync: true, noModel: true },
                        beforeAssociate: { params: 2, sync: true },
                        afterAssociate: { params: 2, sync: true },
                        beforeConnect: { params: 1, noModel: true },
                        afterConnect: { params: 2, noModel: true },
                        beforeDisconnect: { params: 1, noModel: true },
                        afterDisconnect: { params: 1, noModel: true },
                        beforeSync: { params: 1 },
                        afterSync: { params: 1 },
                        beforeBulkSync: { params: 1 },
                        afterBulkSync: { params: 1 },
                        beforeQuery: { params: 2 },
                        afterQuery: { params: 2 }
                      

                      # 声明 Hook

                      Hook 的参数通过引用传递. 这意味着你可以更改值,这将反映在insert / update语句中. Hook 可能包含异步动作 - 在这种情况下,Hook 函数应该返回一个 promise.

                      // 方法1 通过 .init() 方法
                      class User extends Model {}
                      User.init({
                        username: DataTypes.STRING,
                        mood: {
                          type: DataTypes.ENUM,
                          values: ['happy', 'sad', 'neutral']
                        }
                      }, {
                        hooks: {
                          beforeValidate: (user, options) => {
                            user.mood = 'happy';
                          },
                          afterValidate: (user, options) => {
                            user.username = 'Toni';
                          }
                        },
                        sequelize
                      });
                      
                      // 方法2 通过 .addHook() 方法
                      User.addHook('beforeValidate', (user, options) => {
                        user.mood = 'happy';
                      });
                      
                      User.addHook('afterValidate', 'someCustomName', (user, options) => {
                        return Promise.reject(new Error("I'm afraid I can't let you do that!"));
                      });
                      
                      // 方法3 通过直接方法
                      User.beforeCreate((user, options) => {
                        return hashPassword(user.password).then(hashedPw => {
                          user.password = hashedPw;
                        });
                      });
                      
                      User.afterValidate('myHookAfter', (user, options) => {
                        user.username = 'Toni';
                      });
                      

                      # 移除 Hook

                      只能删除有名称参数的 hook.

                      class Book extends Model {}
                      Book.init({
                        title: DataTypes.STRING
                      }, { sequelize });
                      
                      Book.addHook('afterCreate', 'notifyUsers', (book, options) => {
                        // ...
                      });
                      
                      Book.removeHook('afterCreate', 'notifyUsers');
                      

                      # 全局 / 通用 Hook

                      const sequelize = new Sequelize(..., {
                          define: {
                              hooks: {
                                  beforeCreate: () => {
                                      // 做些什么
                                  }
                              }
                          }
                      });
                      

                      这将为所有模型添加一个默认 hook,如果模型没有定义自己的 beforeCreate hook,那么它将运行.

                      class User extends Model {}
                      User.init({}, { sequelize });
                      class Project extends Model {}
                      Project.init({}, {
                          hooks: {
                              beforeCreate: () => {
                                  // 做些其它什么
                              }
                          },
                          sequelize
                      });
                      
                      User.create() // 运行全局 hook
                      Project.create() // 运行其自身的 hook (因为全局 hook 被覆盖),
                      

                      无论模型是否指定了自己的 beforeCreate hook. 本地 hook 总是在全局 hook 之前运行

                      # 实例 Hook

                      当你编辑单个对象时,以下 hook 将触发

                      beforeValidate
                      afterValidate or validationFailed
                      beforeCreate / beforeUpdate / beforeSave  / beforeDestroy
                      afterCreate / afterUpdate / afterSave / afterDestroy
                      

                      # 模型 Hook

                      有时,你将一次编辑多个记录,方法是使用模型上的 bulkCreate, update, destroy 方法. 当你使用以下方法之一时,将会触发以下内容:

                      beforeBulkCreate(instances, options)
                      beforeBulkUpdate(options)
                      beforeBulkDestroy(options)
                      afterBulkCreate(instances, options)
                      afterBulkUpdate(options)
                      afterBulkDestroy(options)
                      

                      # Querying - 查询

                      # 属性

                      想要只选择某些属性,可以使用 attributes 选项. 通常是传递一个数组

                      Model.findAll({
                        attributes: ['foo', 'bar']
                      });
                      

                      属性可以使用嵌套数组来重命名

                      Model.findAll({
                        attributes: ['foo', ['bar', 'baz']]
                      });
                      

                      # Where

                      无论你是通过 findAll/find 或批量 updates/destroys 进行查询,都可以传递一个 where 对象来过滤查询.

                      where 通常用 attribute:value 键值对获取一个对象

                      const Op = Sequelize.Op;
                      
                      Post.findAll({
                        where: {
                          authorId: 2
                        }
                      });
                      // SELECT * FROM post WHERE authorId = 2
                      
                      Post.findAll({
                        where: {
                          authorId: 12,
                          status: 'active'
                        }
                      });
                      // SELECT * FROM post WHERE authorId = 12 AND status = 'active';
                      
                      Post.findAll({
                        where: {
                          [Op.or]: [{authorId: 12}, {authorId: 13}]
                        }
                      });
                      // SELECT * FROM post WHERE authorId = 12 OR authorId = 13;
                      
                      Post.findAll({
                        where: {
                          authorId: {
                            [Op.or]: [12, 13]
                          }
                        }
                      });
                      // SELECT * FROM post WHERE authorId = 12 OR authorId = 13;
                      
                      Post.destroy({
                        where: {
                          status: 'inactive'
                        }
                      });
                      // DELETE FROM post WHERE status = 'inactive';
                      
                      Post.update({
                        updatedAt: null,
                      }, {
                        where: {
                          deletedAt: {
                            [Op.ne]: null
                          }
                        }
                      });
                      // UPDATE post SET updatedAt = null WHERE deletedAt NOT NULL;
                      
                      Post.findAll({
                        where: sequelize.where(sequelize.fn('char_length', sequelize.col('status')), 6)
                      });
                      // SELECT * FROM post WHERE char_length(status) = 6;
                      

                      # 操作符

                      const Op = Sequelize.Op
                      
                      [Op.and]: {a: 5}           // 且 (a = 5)
                      [Op.or]: [{a: 5}, {a: 6}]  // (a = 5 或 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.eq]: 3,                // = 3
                      [Op.not]: true,            // 不是 TRUE
                      [Op.between]: [6, 10],     // 在 6 和 10 之间
                      [Op.notBetween]: [11, 15], // 不在 11 和 15 之间
                      [Op.in]: [1, 2],           // 在 [1, 2] 之中
                      [Op.notIn]: [1, 2],        // 不在 [1, 2] 之中
                      [Op.like]: '%hat',         // 包含 '%hat'
                      [Op.notLike]: '%hat'       // 不包含 '%hat'
                      [Op.iLike]: '%hat'         // 包含 '%hat' (不区分大小写)  (仅限 PG)
                      [Op.notILike]: '%hat'      // 不包含 '%hat'  (仅限 PG)
                      [Op.startsWith]: 'hat'     // 类似 'hat%'
                      [Op.endsWith]: 'hat'       // 类似 '%hat'
                      [Op.substring]: 'hat'      // 类似 '%hat%'
                      [Op.regexp]: '^[h|a|t]'    // 匹配正则表达式/~ '^[h|a|t]' (仅限 MySQL/PG)
                      [Op.notRegexp]: '^[h|a|t]' // 不匹配正则表达式/!~ '^[h|a|t]' (仅限 MySQL/PG)
                      [Op.iRegexp]: '^[h|a|t]'    // ~* '^[h|a|t]' (仅限 PG)
                      [Op.notIRegexp]: '^[h|a|t]' // !~* '^[h|a|t]' (仅限 PG)
                      [Op.like]: { [Op.any]: ['cat', 'hat']} // 包含任何数组['cat', 'hat'] - 同样适用于 iLike 和 notLike
                      [Op.overlap]: [1, 2]       // && [1, 2] (PG数组重叠运算符)
                      [Op.contains]: [1, 2]      // @> [1, 2] (PG数组包含运算符)
                      [Op.contained]: [1, 2]     // <@ [1, 2] (PG数组包含于运算符)
                      [Op.any]: [2,3]            // 任何数组[2, 3]::INTEGER (仅限PG)
                      
                      [Op.col]: 'user.organization_id' // = 'user'.'organization_id', 使用数据库语言特定的列标识符, 本例使用 PG
                      

                      # Instances - 实例

                      # 构建非持久性实例

                      为了创建定义类的实例,使用 build - 该方法将返回一个未保存的对象,你要明确地保存它

                      const project = Project.build({
                        title: 'my awesome project',
                        description: 'woot woot. this will make me a rich man'
                      })
                       
                      const task = Task.build({
                        title: 'specify the project idea',
                        description: 'bla',
                        deadline: new Date()
                      })
                      

                      要将其存储在数据库中,请使用 save 方法并捕获事件

                      project.save().then(() => {
                        // 回调
                      })
                       
                      task.save().catch(error => {
                        // 呃
                      })
                       
                      // 还可以使用链式构建来保存和访问对象:
                      Task
                        .build({ title: 'foo', description: 'bar', deadline: new Date() })
                        .save()
                        .then(anotherTask => {
                          // 你现在可以使用变量 anotherTask 访问当前保存的任务
                        })
                        .catch(error => {
                          // Ooops,做一些错误处理
                        })
                      

                      # 创建持久性实例

                      虽然使用 .build() 创建的实例需要显式的 .save() 调用来存储到 database 中; 但.create() 完全省略了这个要求,一旦调用就自动存储实例的数据.

                      Task.create({ title: 'foo', description: 'bar', deadline: new Date() }).then(task => {
                        // 你现在可以通过变量 task 来访问新创建的 task
                      })
                      

                      # 更新 / 保存 / 持久化一个实例

                      更改一些值并将更改保存到数据库

                      // 方法 1
                      task.title = 'a very different title now'
                      task.save().then(() => {})
                       
                      // 方法 2
                      task.update({
                        title: 'a very different title now'
                      }).then(() => {})
                      

                      # 销毁 / 删除持久性实例

                      创建对象并获得对象的引用后,可以从数据库中删除它. 相关的方法是 destroy:

                      Task.create({ title: 'a task' }).then(task => {
                        // 获取到 task 对象...
                        return task.destroy();
                      }).then(() => {
                       // task 对象已被销毁
                      })
                      

                      # 恢复软删除的实例

                      如果你使用 paranoid:true 软删除了模型的实例,之后想要撤消删除,请使用 restore 方法

                      Task.create({ title: 'a task' }).then(task => {
                        // 进行软删除...
                        return task.destroy();
                      }).then(() => {
                        // 恢复软删除...
                        return task.restore();
                      })
                      

                      # 一个实例的值

                      如果你记录一个实例,你会注意到有很多额外的东西. 为了隐藏这些东西并将其减少到非常有趣的信息,你可以使用 get 属性. 使用选项 plain: true 调用它将只返回一个实例的值

                      Person.create({
                        name: 'Rambow',
                        firstname: 'John'
                      }).then(john => {
                        console.log(john.get({
                          plain: true
                        }))
                      })
                       
                      // 结果:
                       
                      // { name: 'Rambow',
                      //   firstname: 'John',
                      //   id: 1,
                      //   createdAt: Tue, 01 May 2012 19:12:16 GMT,
                      //   updatedAt: Tue, 01 May 2012 19:12:16 GMT
                      // }
                      

                      # 重载实例

                      如果你需要让你的实例同步,你可以使用 reload 方法. 它将从数据库中获取当前数据,并覆盖调用该方法的模型的属性

                      Person.findOne({ where: { name: 'john' } }).then(person => {
                        person.name = 'jane'
                        console.log(person.name) // 'jane'
                       
                        person.reload().then(() => {
                          console.log(person.name) // 'john'
                        })
                      })
                      

                      # 递增

                      为了增加实例的值而不发生并发问题,你可以使用 increment.

                      首先,你可以定义一个字段和要添加的值.

                      User.findByPk(1).then(user => {
                        return user.increment('my-integer-field', {by: 2})
                      }).then(user => {
                        // Postgres默认会返回更新的 user (除非通过设置禁用 { returning: false })
                        // 在其他方言中,你将需要调用 user.reload() 来获取更新的实例...
                      })
                      

                      然后,你可以定义多个字段和要添加到其中的值.

                      User.findByPk(1).then(user => {
                        return user.increment([ 'my-integer-field', 'my-very-other-field' ], {by: 2})
                      }).then(/* ... */)
                      

                      最后,你可以定义一个包含字段及其递增值的对象.

                      User.findByPk(1).then(user => {
                        return user.increment({
                          'my-integer-field':    2,
                          'my-very-other-field': 3
                        })
                      }).then(/* ... */)
                      

                      # 递减

                      为了减少一个实例的值而不遇到并发问题,你可以使用 decrement.

                      首先,你可以定义一个字段和要添加的值

                      User.findByPk(1).then(user => {
                        return user.decrement('my-integer-field', {by: 2})
                      }).then(user => {
                        // Postgres默认会返回更新的 user (除非通过设置禁用 { returning: false })
                        // 在其他方言中,你将需要调用 user.reload() 来获取更新的实例...
                      })
                      

                      # 批量操作(一次性创建,更新和销毁多行)

                      除了更新单个实例之外,你还可以一次创建,更新和删除多个实例. 调用你需要的方法

                      • Model.bulkCreate
                      • Model.update
                      • Model.destroy

                      生成多个

                      User.bulkCreate([
                        { username: 'barfooz', isAdmin: true },
                        { username: 'foo', isAdmin: true },
                        { username: 'bar', isAdmin: false }
                      ]).then(() => { // 注意: 这里没有凭据, 然而现在你需要...
                        return User.findAll();
                      }).then(users => {
                        console.log(users) // ... 以获取 user 对象的数组
                      })
                      

                      一次更新几行

                      Task.update(
                          { status: 'inactive' }, /* 设置属性的值 */
                          { where: { subject: 'programming' }} /* where 规则 */
                        );
                      

                      删除多行

                      Task.destroy({
                          where: {
                            subject: 'programming'
                          },
                          truncate: true /* 这将忽 where 并用 truncate table 替代  */
                        });