sequelize常用api

2022-02-11 09:04:34 浏览数 (1)

sequelize

目前有许许多多的ORM,但是目前最为流行的依然是sequelize,所以这里总结写之前自己写自己的博客所涉及到的点,分享给大家,让大家也可以少踩坑,更快入门。

sequelize-cli的基本流程

  • sequelize中规定 模型的名称是单数、表的名称是复数
  • 总置文件就是用来给数据库mock添加数据的文件 生成文章表模型
代码语言:javascript复制
sequelize model:generate --name Article --attributes  title:string,desc:string,content:text,coverImg:string,status:bigint

生成一个模型名字叫Article的模型,有title、desc、content、coverImg、status五个字段,sequelize会自动为每张表添加idcreatedAtupdetedAt字段。

执行命令创建数据库 运行迁移
代码语言:javascript复制
sequelize db:migrate

这个时候就会通过mysql发现已经建表成功了,并且拥有了这些字段,接下来就是本地如果需要模拟添加数据,需要运行总置文件

新建一个总置文件
代码语言:javascript复制
sequelize seed:generate --name article

![image-20200819111548140](/Users/xiaojiu/Library/Application Support/typora-user-images/image-20200819111548140.png)

然后就可以在这里面进行mock自己插入值了,替换掉pepple为表名,替换后面的数组对象为自己需要插入的对象即可

运行迁移
代码语言:javascript复制
sequelize db:seed:all //只有一个文件这样 多个的时候要加文件名 不然就全部文件都执行了 db:seed --seed 文件名字

这样就添加了数据,刷新数据库已经可以看到数据了

生成comment评论模型

代码语言:javascript复制
sequelize model:generate --name Comment --attributes articleId:integer,content:text
运行迁移命令
代码语言:javascript复制
sequelize db:migrate
运行这个命令生成总置文件
代码语言:javascript复制
sequelize seed:generate --name comment //生成一个comment的总置文件

有了就可以在seeders文件夹下打开添加数据了

数据模型关联关系

代码语言:javascript复制
A.hasOne(B); // A 有一个 B
A.belongsTo(B); // A 属于 B
A.hasMany(B); // A 有多个 B
A.belongsToMany(B, { through: 'C' }); // A 属于多个 B , 通过联结表 C

多种关系在model模型中定义 通过associate

代码语言:javascript复制
module.exports = (sequelize, DataTypes) => {
  class Article extends Model {
    /**
     * Helper method for defining associations.
     * This method is not a part of Sequelize lifecycle.
     * The `models/index` file will call this method automatically.
     */
    static associate(models) {
      // 在此处定义关联 表示文章下面有很多评论
      models.Article.hasMany(models.Comment)
      models.Article.belongsTo(models.Type)
    }
  };
  Article.init({
    title: DataTypes.STRING,
    desc: DataTypes.STRING,
    content: DataTypes.TEXT,
    coverImg: DataTypes.STRING,
    status: DataTypes.BIGINT,
    typeId: DataTypes.BIGINT,
  }, {
    sequelize,
    modelName: 'Article',
  });
  return Article;
};

可以添加多个关系,在使用的时候查询需要使用include 例如

代码语言:javascript复制
router.get('/detail/:id', async (req, res) => {
    var article = await models.Article.findOne({
        where:{id:req.params.id},
        include: [models.Comment,{
            model:models.Type,
            attributes: ['className'] //限制只取某某字段 exclude排除
              //attributes: {exclude:['id']}    排除某个字段
        }]
    }) //findByPk查找主键id
    res.json({data:article})
})

include是一个数组,可以关联多个模型,也就是多张表的意思,里面的每一项为对象,attribute代表限制只需要目标模型的这几个字段即可,如果不限制,拿到的就是所有值。

查询方法

一般我们在使用*sequelize的方法查询时,一般语法是这样的:

代码语言:javascript复制
/** 数据库模型.方法名(各种查询条件) */

User.findOne({where:{username:req.body.username}})

例如上方分为三部分,User为自己定义的数据库模型,其实也就代表用这个模型间接的操作数据库,findOne是查询方法,这里提供了多种方法,后面一一解释,在后面需要给这个方法传递的参数有很多,就是这种搜索的显示条件,where同sql语句里面的where一个意思。

  • findOne ===== 查询一条
代码语言:javascript复制
User.findOne(
  {
    where: { username: '小九' }
  }
)

上面表示用User模型查询一条数据,条件是username字段是小九,这就是查询单条数据

  • findAll ===== 查询所有
代码语言:javascript复制
let result = await Type.findAll()

上面表示通过Type模型查询里面所有的数据,里面可以加条件,和上面一样。

  • findByPk ===== 通过主键id查找
代码语言:javascript复制

let article = await Article.findByPk(99)

上面表示通过Article模型查询主键id为99的这个数据,和查询单条数据区别不大,只是指定了查询字段为id

  • create ===== 创建一条数据
代码语言:javascript复制

let user = await models.User.create(req.body)

上面是创建新增一条数据,拿到前端传来的数据存到数据库,其实就是新增一个用户的意思

  • destory ===== 删除数据
代码语言:javascript复制

article.destroy( where: { username: '小九' })

上面表示删除username为小九的一条数据。根据条件删除。

  • Update ==== 修改数据
代码语言:javascript复制

let article = await models.Article.update(req.body, {         where: { name:'小九' }     })

上面表述查询到name为小九的这个用户,修改他的名字为前端传来的数据req.body这个对象。

  • bulkCreate ==== 批量创建
代码语言:javascript复制

const user = await User.bulkCreate([   { name: 'Jack Sparrow' },   { name: 'Davy Jones' } ]);

上面表示一次创建多条数据,这样的创建也会带来副作用,例如:性能低、不能对每一条数据都进行验证、

进阶查询

上面就是普通的增删查改,但是实际业务远比这些复杂,学会了上面的就来试试下面的各种业务场景吧。

  • findAndCountAll ==== 分页 模糊查询
代码语言:javascript复制
router.post('/AllArticle', async function (req, res, next) {
    let currentPage = parseInt(req.body.currentPage) || 1
    let pageSize = parseInt(req.body.pageSize) || 5
    let where = {};
    if (req.body.typeId) {
        where.typeId = req.body.typeId
    }
    if(req.body.status){
        where.status = req.body.status
    }

    let keyword = req.body.keyword

    if (keyword) {
        where.keyword = {
            [Op.like]: '%'   keyword   '%'
        }
    }

    let result = await models.Article.findAndCountAll({
        order: [['id', 'DESC']], //倒叙的方式输出 对比id 默认为ASC正序
        where,   //模糊查询的条件
        offset: (currentPage - 1) * pageSize,
        limit: pageSize,
        include: [models.User, models.Type]
    })
   
    const data = {
        articles: result.rows,
        pagination: {
            currentPage,
            pageSize,
            total: result.count
        }
    }
    new Result(data, '获取成功').success(res)
});

上面是我博客的一段真实场景代码,其作用是第一可以分页,第二倒序返回数据(这样可以后发表的博客显示在最前面),第三点可以支持模糊搜索,我们知道,前端一般采用分页,就需要总数,一页多少条,当前在第几页,这几个参数,所以这个方法会直接给你返回一个前端可以做分页的分页格式,那么我们看看查询条件,order为排序,上图表示以id为排序返回,正序倒序可以自己设定,根据场景而来,where是查询条件,模糊搜索需要满足一个条件,那么上面的语法表示,用户属于的这个关键词在我们的数据里面出现就会返回这条数据,这里的模糊搜索建议参考官网文档,场景不同模糊搜索的需求也就不一样了。其他三个字段就不用说了,前端传入的当前页,一页多少条,和总数,自行分析。

  • findOrCreate ==== 查询并新增
代码语言:javascript复制

let res = await User.findOrCreate({where:{name:'小九'}})

上面表示查询username为小九的数据,如果没有就新增一条数据。在实际场景中,我们经常新增数据之前首先需要确认这个用户是否已经新增过了,所以这个方法就适用于这种场景

  • findAndDelete ==== 查询并删除
代码语言:javascript复制

let res = await User.findAndDelete({where:{name:'小九'}})

和上面的一样,查询这个用户是否存在,存在再进行删除,防止出现删除的用户在数据库中并不存在的这种操作,多人操作的情况下可能会出现这种情况,所以可以使用这个方法。

查询条件

上面我们已经知道了基本的查询语法,但是实际业务中的查询可能更为麻烦,我们看看在sequelize中还提供了哪些参数吧:

代码语言:javascript复制
something.findOne({
  order: [
    // 转义 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'],

    // 按相关联的User 模型的 name 属性排序
    [User, 'name', 'DESC'],

    // 按相关联的User 模型的 name 属性排序并将模型起别名为 Friend
    [{model: User, as: 'Friend'}, 'name', 'DESC'],

    // 按相关联的User 模型的嵌套关联的 Company 模型的 name 属性排序
    [User, Company, 'name', 'DESC'],
  ]
  // 以下所有声明方式都会视为字面量,应该小心使用
  order: 'convert(user_name using gbk)'
  order: 'username DESC'
  order: sequelize.literal('convert(user_name using gbk)')
})

有时候我们查询出来的数据有很多,但是前端却不需要这么多数据,包括有的数据也不想暴露出去,那么我们如何对数据进行过滤呢?

代码语言:javascript复制
router.get('/getDetail/:id', async (req, res) => {
    var article = await models.Article.findOne({
        where: { id: req.params.id },
        attributes: ['title', 'desc', 'status', 'userId', 'typeId', 'coverImg', 'content'],
        include: [models.Comment, {
            model: models.Type,
            // attributes: ['className'] //限制只取某某字段
        }]
    })
    new Result(article, '获取成功').success(res)
})

如上:使用attributes,在里面写入你需要返回的字段即可,其他字段就可以过滤掉了。

有时候我们需要多表联合查询,假设这样的场景,对于我的博客,会出现,一个分类下面有多篇文章,我需要查出这个分类和当前分类下的所有文章应该如何做呢?

代码语言:javascript复制
router.get('/getTypeDetail/:id', async (req, res) => {
    var types = await models.Type.findOne({
        where:{id:req.params.id},
        include: [models.Article]
    }) //findByPk查找主键id
    res.json({data:types})
})

使用include联合查询,后面表示需要一起查询的model,这里在定义的时候需要对齐关联,例如models.Type.**hasMany(models.Article)在定义model模型的时候进行关联,这句表示type模型的下面有很多的文章模型,翻译成业务就是,分类下面可以包含很多文章

常用操作符

代码语言:javascript复制
const { Op } = require("sequelize");
Post.findAll({
  where: {
    [Op.and]: [{ a: 5 }, { b: 6 }],            // (a = 5) AND (b = 6)
    [Op.or]: [{ a: 5 }, { b: 6 }],             // (a = 5) OR (b = 6)
    someAttribute: {
      // 基本
      [Op.eq]: 3,                              // = 3
      [Op.ne]: 20,                             // != 20
      [Op.is]: null,                           // IS NULL
      [Op.not]: true,                          // IS NOT TRUE
      [Op.or]: [5, 6],                         // (someAttribute = 5) OR (someAttribute = 6)

      // 使用方言特定的列标识符 (以下示例中使用 PG):
      [Op.col]: 'user.organization_id',        // = "user"."organization_id"

      // 数字比较
      [Op.gt]: 6,                              // > 6
      [Op.gte]: 6,                             // >= 6
      [Op.lt]: 10,                             // < 10
      [Op.lte]: 10,                            // <= 10
      [Op.between]: [6, 10],                   // BETWEEN 6 AND 10
      [Op.notBetween]: [11, 15],               // NOT BETWEEN 11 AND 15

      // 其它操作符

      [Op.all]: sequelize.literal('SELECT 1'), // > ALL (SELECT 1)

      [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'
      [Op.startsWith]: 'hat',                  // LIKE 'hat%'
      [Op.endsWith]: 'hat',                    // LIKE '%hat'
      [Op.substring]: 'hat',                   // LIKE '%hat%'
      [Op.iLike]: '%hat',                      // ILIKE '%hat' (不区分大小写) (仅 PG)
      [Op.notILike]: '%hat',                   // NOT ILIKE '%hat'  (仅 PG)
      [Op.regexp]: '^[h|a|t]',                 // REGEXP/~ '^[h|a|t]' (仅 MySQL/PG)
      [Op.notRegexp]: '^[h|a|t]',              // NOT REGEXP/!~ '^[h|a|t]' (仅 MySQL/PG)
      [Op.iRegexp]: '^[h|a|t]',                // ~* '^[h|a|t]' (仅 PG)
      [Op.notIRegexp]: '^[h|a|t]',             // !~* '^[h|a|t]' (仅 PG)

      [Op.any]: [2, 3],                        // ANY ARRAY[2, 3]::INTEGER (仅 PG)
      [Op.match]: Sequelize.fn('to_tsquery', 'fat & rat') // 匹配文本搜索字符串 'fat' 和 'rat' (仅 PG)

      // 在 Postgres 中, Op.like/Op.iLike/Op.notLike 可以结合 Op.any 使用:
      [Op.like]: { [Op.any]: ['cat', 'hat'] }  // LIKE ANY ARRAY['cat', 'hat']

      // 还有更多的仅限 postgres 的范围运算符,请参见下文
    }
  }
});

运算符的逻辑组合

代码语言:javascript复制
const { Op } = require("sequelize");

Foo.findAll({
  where: {
    rank: {
      [Op.or]: {
        [Op.lt]: 1000,
        [Op.eq]: null
      }
    },
    // rank < 1000 OR rank IS NULL

    {
      createdAt: {
        [Op.lt]: new Date(),
        [Op.gt]: new Date(new Date() - 24 * 60 * 60 * 1000)
      }
    },
    // createdAt < [timestamp] AND createdAt > [timestamp]

    {
      [Op.or]: [
        {
          title: {
            [Op.like]: 'Boat%'
          }
        },
        {
          description: {
            [Op.like]: '%boat%'
          }
        }
      ]
    }
    // title LIKE 'Boat%' OR description LIKE '%boat%'
  }
});

范围运算符

代码语言:javascript复制
[Op.contains]: 2,            // @> '2'::integer  (PG range 包含元素运算符)
[Op.contains]: [1, 2],       // @> [1, 2)        (PG range 包含范围运算符)
[Op.contained]: [1, 2],      // <@ [1, 2)        (PG range 包含于运算符)
[Op.overlap]: [1, 2],        // && [1, 2)        (PG range 重叠(有共同点)运算符)
[Op.adjacent]: [1, 2],       // -|- [1, 2)       (PG range 相邻运算符)
[Op.strictLeft]: [1, 2],     // << [1, 2)        (PG range 左严格运算符)
[Op.strictRight]: [1, 2],    // >> [1, 2)        (PG range 右严格运算符)
[Op.noExtendRight]: [1, 2],  // &< [1, 2)        (PG range 未延伸到右侧运算符)
[Op.noExtendLeft]: [1, 2],   // &> [1, 2)        (PG range 未延伸到左侧运算符)

排序和分组

Sequelize 提供了 order and group 参数,来与 ORDER BY 和 GROUP BY 一起使用.

代码语言:javascript复制
Subtask.findAll({
  order: [
    // 将转义 title 并针对有效方向列表进行降序排列
    ['title', 'DESC'],

    // 将按最大年龄进行升序排序
    sequelize.fn('max', sequelize.col('age')),

    // 将按最大年龄进行降序排序
    [sequelize.fn('max', sequelize.col('age')), 'DESC'],

    // 将按 otherfunction(`col1`, 12, 'lalala') 进行降序排序
    [sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],

    // 将使用模型名称作为关联名称按关联模型的 createdAt 排序.
    [Task, 'createdAt', 'DESC'],

    // 将使用模型名称作为关联名称通过关联模型的 createdAt 排序.
    [Task, Project, 'createdAt', 'DESC'],

    // 将使用关联名称按关联模型的 createdAt 排序.
    ['Task', 'createdAt', 'DESC'],

    // 将使用关联的名称按嵌套的关联模型的 createdAt 排序.
    ['Task', 'Project', 'createdAt', 'DESC'],

    // 将使用关联对象按关联模型的 createdAt 排序. (首选方法)
    [Subtask.associations.Task, 'createdAt', 'DESC'],

    // 将使用关联对象按嵌套关联模型的 createdAt 排序. (首选方法)
    [Subtask.associations.Task, Task.associations.Project, 'createdAt', 'DESC'],

    // 将使用简单的关联对象按关联模型的 createdAt 排序.
    [{model: Task, as: 'Task'}, 'createdAt', 'DESC'],

    // 将由嵌套关联模型的 createdAt 简单关联对象排序.
    [{model: Task, as: 'Task'}, {model: Project, as: 'Project'}, 'createdAt', 'DESC']
  ],

  // 将按最大年龄降序排列
  order: sequelize.literal('max(age) DESC'),

  // 如果忽略方向,则默认升序,将按最大年龄升序排序
  order: sequelize.fn('max', sequelize.col('age')),

  // 如果省略方向,则默认升序, 将按年龄升序排列
  order: sequelize.col('age'),

  // 将根据方言随机排序(但不是 fn('RAND') 或 fn('RANDOM'))
  order: sequelize.random()
});

Foo.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']
  ]
});

三个常用快捷方法

代码语言:javascript复制
await User.max('age'); // 40
await User.max('age', { where: { age: { [Op.lt]: 20 } } }); // 10
await User.min('age'); // 5
await User.min('age', { where: { age: { [Op.gt]: 5 } } }); // 10
await User.sum('age'); // 55
await User.sum('age', { where: { age: { [Op.gt]: 5 } } }); // 50

0 人点赞