zoukankan      html  css  js  c++  java
  • Sequelize

    资源:

    Sequelize 中文文档
    sequelize API
    sequelize 小技巧
    sequelize 菜鸟教程

    核心概念:

    连接数据库:

    const { Sequelize } = require('sequelize');
    
    // 方法 1: 传递一个连接 URI
    const sequelize = new Sequelize('sqlite::memory:') // Sqlite 示例
    const sequelize = new Sequelize('postgres://user:pass@example.com:5432/dbname') // Postgres 示例
    
    // 方法 2: 分别传递参数 (sqlite)
    const sequelize = new Sequelize({
      dialect: 'sqlite',
      storage: 'path/to/database.sqlite'
    });
    
    // 方法 2: 分别传递参数 (其它数据库)
    const sequelize = new Sequelize('database', 'username', 'password', {
      host: 'localhost',
      dialect: /* 选择 'mysql' | 'mariadb' | 'postgres' | 'mssql' 其一 */
    });
    

    模型定义:

    sequelize.define('model_name',{filed:value})

    创建表:

    首先定义模型: model
    然后同步:model.sync()
    创建表会自动创建主键,默认为 id

    增删改查:

    • 新增数据:model.create 相当于 build save两步合并;
    • 批量新增:model.bulkCreate([model,...],{...}) ;
      但是默认不会运行验证器,需要手动开启
     User.bulkCreate([
      { username: 'foo' },
      { username: 'bar', admin: true }
    ], { validate: true,//手动开启验证器
    fields: ['username']//限制字段 
    });
    // 因为限制了字段只存username,foo 和 bar 都不会是管理员.
    
    • 更新 model.update
      相当于 set, save两步合并,通常就直接修改实例属性,然后save()更新;
    • 部分更新:
      通过传递一个列名数组,可以定义在调用 save 时应该保存哪些属性
      save({fields:[ 'name',... ]}) 只更新数组里面的字段
    • 删除 model.destroy
    • 重载实例:model.reload
    • 查询
      include参数 对应sql的 join连接操作
      findAll 查找所有的
      findByPk 根据主键查找
      findOne 找到第一个实例
      findOrCreate 查找到或创建实例
      findAndCountAll 分页查找
    查询的选项参数
    Model.findAll({
    //查询指定字段
      attributes: ['foo', 'bar',
      [sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats']//函数聚合
    ]  ,
     where: {//对应where子句,过滤
        authorId: 2,
       authorId: {
          [Sequelize.Op.eq]: 2  //操作符运算
        }
      },
    order:[], //排序
    group:'name',//分组
    limit:10,//限制
    offset:1//页
    });
    

    实用方法:

    count,max, min 和 sum

    原始查询:

    const { QueryTypes } = require('sequelize');
    const users = await sequelize.query("SELECT * FROM `users`");//参数是sql语句
    

    偏执表:

    Sequelize 支持 paranoid 表的概念
    这意味着删除记录时不会真的删除,而是给字段deletedAt值设置为时间戳
    删除的时候默认是软删除,而不是硬删除

    class Post extends Model {}
    Post.init({ /* 这是属性 */ }, {
      sequelize,
      paranoid: true,// 传递该参数,创建偏执表
      // 如果要为 deletedAt 列指定自定义名称
      deletedAt: 'destroyTime'
    });
    

    强制删除:

    await Post.destroy({
      where: {
        id: 1
      },
      force: true //硬删除
    });
    

    软删除的实例,恢复:

    post.restore();
    
    Post.restore({
      where: {
        likes: {
          [Op.gt]: 100
        }
      }
    });
    

    查询包含软删除的记录:

    await Post.findAll({
      where: { foo: 'bar' },
      paranoid: false
    });
    

    关联类型:

    对应 sql语句的 foreign key 进行表关联
    HasOne BelongsTo HasMany BelongsToMany

    const A = sequelize.define('A', /* ... */);
    const B = sequelize.define('B', /* ... */);
    
    A.hasOne(B); // A 有一个 B ,外键在目标模型(B)中定义
    A.belongsTo(B); // A 属于 B  ,外键在目标模型(A)中定义
    A.hasMany(B); // A 有多个 B 外键在目标模型(B)中定义
    A.belongsToMany(B, { through: 'C' }); // A 属于多个 B , 通过联结表 C
    

    A.belongsToMany(B, { through: 'C' }) 关联意味着将表 C 用作联结表,在 AB 之间存在多对多关系. 具有外键(例如,aIdbId). Sequelize 将自动创建此模型 C(除非已经存在),并在其上定义适当的外键.

    创建标准关系:
    • 创建一个 一对一 关系, hasOnebelongsTo 关联一起使用;
    • 创建一个 一对多 关系, hasMany he belongsTo 关联一起使用;
    • 创建一个 多对多 关系, 两个 belongsToMany 调用一起使用.

    添加到实例的特殊方法:

    创建关联关系后,这些模型的实例会获得特殊方法
    例如:有两个模型 FooBar 拥有关联关系,则根据关联类型拥有以下可用方法;

    Foo.hasOne(Bar) 和 Foo.belongsTo(Bar)#
    • fooInstance.getBar()
    • fooInstance.setBar()
    • fooInstance.createBar()
    Foo.hasMany(Bar) 和 Foo.belongsToMany(Bar, { through: Baz })#
    • fooInstance.getBars()
    • fooInstance.countBars()
    • fooInstance.hasBar()
    • fooInstance.hasBars()
    • fooInstance.setBars()
    • fooInstance.addBar()
    • fooInstance.addBars()
    • fooInstance.removeBar()
    • fooInstance.removeBars()
    • fooInstance.createBar()

    多对多关系:

    代码分析:

    1. 创建表Foo,Bar ,设置为多对多,中间表为Foo_Bar
    2. sequelize.sync();同步到数据库,就是说如果模型对应的表不存在就创建
      插入数据 foo, bar
    3. foo.addBar(bar) ,foo 关联了一个bar,反映到数据库上面,则是中间表Foo_Bar插入一条数据 INSERT INTO Foo_Bar (FooId,BarId) VALUES(1,1)
    4. Foo.findOne({ include: Bar });数据查询,根据模型,查出Foo表的第一条数据,
      并带上关联表数据,字段是Bars(因为是多对多,所以这里是复数形式,每一条bar包含中间表的数据字段是 Foo_Bar
    const Foo = sequelize.define('Foo', { name: DataTypes.TEXT });
    const Bar = sequelize.define('Bar', { name: DataTypes.TEXT });
    Foo.belongsToMany(Bar, { through: 'Foo_Bar' });
    Bar.belongsToMany(Foo, { through: 'Foo_Bar' });
    
    await sequelize.sync();
    const foo = await Foo.create({ name: 'foo' });
    const bar = await Bar.create({ name: 'bar' });
    await foo.addBar(bar);// foo这条数据关联了一条bar,反映到表上则是在中间表Foo_Bar上插入一条数据
    const fetchedFoo =await  Foo.findOne({ include: Bar });
    console.log(JSON.stringify(fetchedFoo, null, 2));
    

    输出:

    {
      "id": 1,
      "name": "foo",
      "Bars": [
        {
          "id": 1,
          "name": "bar",
          "Foo_Bar": {
            "FooId": 1,
            "BarId": 1
          }
        }
      ]
    }
    

    高级关联概念

    预先加载:

    查询方法中使用 include 参数完成预先加载,翻译成sql其实就是 通过join关联子句;

    创建关联:

    可以一次性创建带关联关系的数据

    高级M:N关联:

    超级多对多:

    超级多对多创建出来的表跟多对多一样,没什么区别,区别就是一次使用6个关联,然后就可以进行各种预先加载

    //模型:
    const User = sequelize.define('user', {
      username: DataTypes.STRING,
      points: DataTypes.INTEGER
    }, { timestamps: false });
    
    const Profile = sequelize.define('profile', {
      name: DataTypes.STRING
    }, { timestamps: false });
    //自定义中间表
    const Grant = sequelize.define('grant', {
      id: {
        type: DataTypes.INTEGER,
        primaryKey: true,
        autoIncrement: true,
        allowNull: false
      },
      selfGranted: DataTypes.BOOLEAN
    }, { timestamps: false });
    
    // 超级多对多关系
    User.belongsToMany(Profile, { through: Grant });
    Profile.belongsToMany(User, { through: Grant });
    User.hasMany(Grant);
    Grant.belongsTo(User);
    Profile.hasMany(Grant);
    Grant.belongsTo(Profile);
    

    这样,我们可以进行各种预先加载:

    // 全部可以使用:
    User.findAll({ include: Profile });
    Profile.findAll({ include: User });
    User.findAll({ include: Grant });
    Profile.findAll({ include: Grant });
    Grant.findAll({ include: User });
    Grant.findAll({ include: Profile });
    

    多态关联:

    多态关联,就是说一个联结表的外键关联多个表
    由于外键引用了多个表,无法添加REFERENCES约束,需要禁用约束constraints: false

    一对多的多态关联:

    考虑模型 Image Video Comment ,
    图片,视频都可以有多个评论,
    但是一个评论只能是图片跟视频中的其中一种类型;

    // Helper 方法
    const uppercaseFirst = str => `${str[0].toUpperCase()}${str.substr(1)}`;
    
    class Image extends Model {}
    Image.init({
      title: DataTypes.STRING,
      url: DataTypes.STRING
    }, { sequelize, modelName: 'image' });
    
    class Video extends Model {}
    Video.init({
      title: DataTypes.STRING,
      text: DataTypes.STRING
    }, { sequelize, modelName: 'video' });
    
    class Comment extends Model {
      getCommentable(options) {//获取评论关联类型的那个实例
        if (!this.commentableType) return Promise.resolve(null);
        const mixinMethodName = `get${uppercaseFirst(this.commentableType)}`;
        return this[mixinMethodName](options);
      }
    }
    Comment.init({
      title: DataTypes.STRING,
      commentableId: DataTypes.INTEGER,
      commentableType: DataTypes.STRING
    }, { sequelize, modelName: 'comment' });
    
    Image.hasMany(Comment, {
      foreignKey: 'commentableId',
      constraints: false,
      scope: {//关联作用域  commentableType = 'image'
        commentableType: 'image'
      }
    });
    Comment.belongsTo(Image, { foreignKey: 'commentableId', constraints: false });
    
    Video.hasMany(Comment, {
      foreignKey: 'commentableId',
      constraints: false,
      scope: {//关联作用域  commentableType = 'video'
        commentableType: 'video'
      }
    });
    Comment.belongsTo(Video, { foreignKey: 'commentableId', constraints: false });
    
    Comment.addHook("afterFind", findResult => {
      console.log('afterFind,findResult=====',findResult);
      if (!Array.isArray(findResult)) findResult = [findResult];
      for (const instance of findResult) {
        if (instance.commentableType === "image" && instance.image !== undefined) {
          instance.commentable = instance.image;
        } else if (instance.commentableType === "video" && instance.video !== undefined) {
          instance.commentable = instance.video;
        }
        // 防止错误:
         delete instance.image;
         delete instance.dataValues.image;
         delete instance.video;
         delete instance.dataValues.video;
      }
    });
    
    多对多多态关联:
    class Tag extends Model {
      getTaggables(options) {
        const images = await this.getImages(options);
        const videos = await this.getVideos(options);
        // 在单个 taggables 数组中合并 images 和 videos
        return images.concat(videos);
      }
    }
    Tag.init({
      name: DataTypes.STRING
    }, { sequelize, modelName: 'tag' });
    
    // 在这里,我们明确定义联结模型
    class Tag_Taggable extends Model {}
    Tag_Taggable.init({
      tagId: {
        type: DataTypes.INTEGER,
        unique: 'tt_unique_constraint'
      },
      taggableId: {
        type: DataTypes.INTEGER,
        unique: 'tt_unique_constraint',
        references: null
      },
      taggableType: {
        type: DataTypes.STRING,
        unique: 'tt_unique_constraint'
      }
    }, { sequelize, modelName: 'tag_taggable' });
    
    Image.belongsToMany(Tag, {
      through: {
        model: Tag_Taggable,
        unique: false,
        scope: {//注意这里的作用域用于关联模型,因为这scope参数在through下
          taggableType: 'image'
        }
      },
      foreignKey: 'taggableId',
      constraints: false
    });
    Tag.belongsToMany(Image, {
      through: {
        model: Tag_Taggable,
        unique: false
      },
      foreignKey: 'tagId',
      constraints: false
    });
    
    Video.belongsToMany(Tag, {
      through: {
        model: Tag_Taggable,
        unique: false,
        scope: {//注意这里的作用域用于关联模型,因为这scope参数在through下
          taggableType: 'video'
        }
      },
      foreignKey: 'taggableId',
      constraints: false
    });
    Tag.belongsToMany(Video, {
      through: {
        model: Tag_Taggable,
        unique: false
      },
      foreignKey: 'tagId',
      constraints: false
    });
    
    在目标模型上应用作用域

    我们还可以在目标模型上应用关联作用域. 我们甚至可以同时进行,以下实例:

    Image.belongsToMany(Tag, {
      through: {
        model: Tag_Taggable,
        unique: false,
        scope: {
          taggableType: 'image'
        }
      },
      scope: {
        status: 'pending'
      },
      as: 'pendingTags',
      foreignKey: 'taggableId',
      constraints: false
    });
    

    其他主题

    事务:

    Sequelize 支持两种使用事务的方式:

    1. 非托管事务: 提交和回滚事务应由用户手动完成(通过调用适当的 Sequelize 方法).
    2. 托管事务: 如果引发任何错误,Sequelize 将自动回滚事务,否则将提交事务. 另外,如果启用了CLS(连续本地存储),则事务回调中的所有查询将自动接收事务对象.
    非托管事务:
    // 首先,我们开始一个事务并将其保存到变量中
    const t = await sequelize.transaction();
    
    try {
    
      // 然后,我们进行一些调用以将此事务作为参数传递:
    
      const user = await User.create({
        firstName: 'Bart',
        lastName: 'Simpson'
      }, { transaction: t });
    
      await user.addSibling({
        firstName: 'Lisa',
        lastName: 'Simpson'
      }, { transaction: t });
    
      // 如果执行到此行,且没有引发任何错误.
      // 我们手动提交事务.
      await t.commit();
    
    } catch (error) {
    
      // 如果执行到达此行,则抛出错误.
      // 我们回滚事务.
      await t.rollback();
    
    }
    
    托管事务:
    try {
    
      const result = await sequelize.transaction(async (t) => {
    
        const user = await User.create({
          firstName: 'Abraham',
          lastName: 'Lincoln'
        }, { transaction: t });
    
        await user.setShooter({
          firstName: 'John',
          lastName: 'Boothe'
        }, { transaction: t });
    
        return user;
    
      });
    
      // 如果执行到此行,则表示事务已成功提交,`result`是事务返回的结果
      // `result` 就是从事务回调中返回的结果(在这种情况下为 `user`)
    
    } catch (error) {
    
      // 如果执行到此,则发生错误.
      // 该事务已由 Sequelize 自动回滚!
    
    }
    

    作用域:

    不同于关联作用域, 作用域定义在模型中,帮助重用代码
    作用域在模型定义中定义,可以是查找器对象,也可以是返回查找器对象的函数 - 默认作用域除外,该作用域只能是一个对象

    class Project extends Model {}
    Project.init({
      // 属性
    }, {
      defaultScope: {//默认作用域
        where: {
          active: true
        }
      },
      scopes: {
        deleted: {
          where: {
            deleted: true
          }
        },
        activeUsers: {
          include: [
            { model: User, where: { active: true } }
          ]
        },
        random() {
          return {
            where: {
              someNumber: Math.random()
            }
          }
        }
    },
        sequelize,
        modelName: 'project'
      
    });
    await Project.scope('deleted').findAll(); //用法就是调用scope方法传入字符串,返回一个查询对象
    SELECT * FROM projects WHERE deleted = true // sql
        await Project.scope('random', { method: ['accessLevel', 19] }).findAll();
    SELECT * FROM projects WHERE someNumber = 42 AND accessLevel >= 19// sql


    作者:忍不住的k
    链接:https://www.jianshu.com/p/49ad2d4d7da7
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    ASP.NET编程的十大技巧
    C#学习心得(转)
    POJ 1177 Picture (线段树)
    POJ 3067 Japan (树状数组)
    POJ 2828 Buy Tickets (线段树)
    POJ 1195 Mobile phones (二维树状数组)
    HDU 4235 Flowers (线段树)
    POJ 2886 Who Gets the Most Candies? (线段树)
    POJ 2418 Cows (树状数组)
    HDU 4339 Query (线段树)
  • 原文地址:https://www.cnblogs.com/yf2196717/p/14826798.html
Copyright © 2011-2022 走看看