Sequelize学习笔记一

Sequelize是一个基于promise的Node.js ORM,目前支持 Postgres, MySQL, MariaDB, SQLite 以及 Microsoft SQL Server. 它具有强大的事务支持, 关联关系, 预读和延迟加载,读取复制等功能。

什么是ORM?
ORM(Oject-Relational Mapping对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射。这样,我们在具体的操作业务对象的时候,就不需要再去和复杂的SQL语句打交道,只需简单的操作对象的属性和方法。
优势:

  • 开发效率高,更容易维护
  • 数据访问更抽象,轻便
  • 支持面向对象封装

缺点:

  • 降低程序的执行效率,消耗系统性能
  • 多表查询等复杂语句语法更复杂

基本使用

安装

npm install --save sequelize
npm install --sage mysql2

连接数据库

const { Sequelize } = require('sequelize');
const sequelize = new Sequelize({
  dislect: 'mysql',  // 数据库类型
  host: 'localhost',  // 数据库地址
  port: 3306,  // 端口号
  username: 'root', // 用户名
  password: '123456', // 密码
  database: 'test', // 数据库名称
})

更多参数配置可参考sequelize API参数

连接

async function test() {
 try {
    await sequelize.authenticate();
    console.log('Connection has been established successfully.');
  } catch (error) {
    console.error('Unable to connect to the database:', error);
  }
}
test(); 

模型(model)

模型是 Sequelize 的本质. 模型是代表数据库中表的抽象. 在 Sequelize 中,它是一个 Model 的扩展类.
模型告诉 Sequelize 有关它代表的实体的几件事,例如数据库中表的名称以及它具有的列(及其数据类型).

模型定义

模型有两种定义方式:
1.调用sequelize.define(modelName, attributes, options)
2. 扩展Model并调用init(attributes, options)
两种效果基本等同,这里主要介绍第一种。

sequelize.define(modelName, attributes, options)

直接上例子,后面介绍详细参数:

const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:');

const User = sequelize.define('User', {
  // 在这里定义模型属性
  firstName: {
    type: DataTypes.STRING,
    allowNull: false
  },
  age: {
    type: DataTypes.STRING,
    defaultValue: 18
    // allowNull 默认为 true
  }
}, {
  // 这是其他模型参数
});

modelName(表名): 主要是代码库表名,默认情况下会进行类型推断,自动将模型名复数并将其用作表名(User=>Users)。也可通过options设置freezeTableName: true来停止复数化,或通过options设置tableName: 'tableName’来直接指定表名。

sequelize.define('User', {
  // ... (属性)
}, {
  tableName: 'userTable' // 指定操作数据名为'userTable'
});

attributes(属性):
这里主要指定表的列字段名和类型等属性,Sequelize提供了内置的数据类型,可以通过导入DataTypes类来使用
在这里插入图片描述
更多使用直接参考model数据类型
配置(options):
modelName参数的时候以提起过,主要用于表相关配置。例如修改连接表的名字,使用需要模型默认添加的createAt和updateAt字段等。

const User = sequelize.define('User', {
  // ... (属性)
}, {
  // 启用时间戳(默认启用)
  timestamps: true,

  // 不想要 createdAt
  createdAt: false,

  // 想要 updatedAt 但是希望名称叫做 updateTimestamp
  updatedAt: 'updateTimestamp'
});

模型同步

模型同步可以通过调用一个异步函数(返回一个Promise)model.sync(options). 通过此调用,Sequelize 将自动对数据库执行 SQL 查询. 请注意,这仅更改数据库中的表,而不更改 JavaScript 端的模型.

  • User.sync() - 如果表不存在,则创建该表(如果已经存在,则不执行任何操作)
  • User.sync({ force: true }) - 将创建表,如果表已经存在,则将其首先删除
  • User.sync({ alter: true }) - 这将检查数据库中表的当前状态(它具有哪些列,它们的数据类型等),然后在表中进行必要的更改以使其与模型匹配.

sync和drop操作是破坏性的. Sequelize 使用 match 参数作为附加的安全检查,该检查将接受 RegExp

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

 

模型实例操作

新增实例: user.create()
Model.create() 方法是使用 Model.build() 构建未保存实例并使用 instance.save() 保存实例的简写形式。

const jane = await User.create({ name: "Jane" });
// 在user表中新增一条name: jane数据
console.log(jane instanceof User); // true
console.log(jane.name); // "Jane"
console.log(jane.toJSON()); // Sequelize 实例具有很多附加条件,使用toJSON()更简便

更新实例: user.save()

const jane = await User.create({ name: "Jane", ages: 18 });
console.log(jane.name); // "Jane"
jane.name = "Ada";
jane.name = 20;
// 数据库中的名称仍然是 "Jane"
await jane.save();
// 现在该名称已在数据库中更新为 "Ada"!

如果仅保存部分修改,可以通过jane.save({ filelds: [‘name’] })

重载实例: user.reload()

const jane = await User.create({ name: "Jane" });
console.log(jane.name); // "Jane"
jane.name = "Ada";
// 数据库中的名称依然是 "Jane"
await jane.reload();
console.log(jane.name); // "Jane"

删除实例: user.destroy()

const jane = await User.create({ name: "Jane" });
console.log(jane.name); // "Jane"
jane.name = "Ada";
// 数据库中的名称依然是 "Jane"
await jane.reload();
console.log(jane.name); // "Jane"

递增和递减整数值: user.increment() / user.decrement()
为了避免并发问题,增减最好使用自带的方法。

await jane.increment(''age);
// age自增1
await jane.increment(['age', 'cash'], { by: 2 });
// age和cash都各自增加2
await jane.increment({
  'age': 2,
  'cash': 10
});
// age自增2, cash自增10

模型查询

简单查询

选择某些特定属性,可以使用 attributes 参数:

await.findAll();
// 查询所有用户
await.findAll({
  attributes: ['foo',  ['bar', 'baz'], 'que', [sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats']]
})
// 查询foo、bar as baz、que、count(hats) as n_hats
await.findAll({
  attributes: { exclude: ['baz'] }
})
// 查询所有排除某些字段和include(查询所有还包括)相反

条件查询

sequelize的Op封装了很多常用的条件判断方法。

const { Op } = require("sequelize");
...
Post.findAll({
  where: {
    name: 'binguo', 
    ages: {
      [Op.qt]: 2
    },
    [Op.or]: [
      { authorId: 12 },
      { authorId: 13 }
    ]
  }
});
查询name为binguo,ages大于2,authorId为12或13的所有数据

更多的用法如下

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']

        [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 未延伸到左侧运算符)
    }
  }
});

高级查询:
类似获得where char_length(“content”) = 7,获取某字段名值的长度等于7

Post.findAll({
  where: sequelize.where(sequelize.fn('char_length', sequelize.col('content')), 7)
});
// SELECT ... FROM "posts" AS "post" WHERE char_length("content") = 7
排序
User.findAll({
   order: [
     ['title'],
     [sequelize.fn('max', sequelize.col('age')), 'DESC']
   ]
})
按照title升序,最大年龄降序排序

order的规则:

  1. 一个字符串 (它将被自动引用)
  2. 一个数组, 其第一个元素将被引用,第二个将被逐字追加
  3. 一个具有 raw 字段的对象:
    raw 内容将不加引用地逐字添加
    其他所有内容都将被忽略,如果未设置 raw,查询将失败
  4. 调用 Sequelize.fn (这将在 SQL 中生成一个函数调用)
  5. 调用 Sequelize.col (这将引用列名)
分组

和排序语法类型

Project.findAll({ group: 'name' });
// 生成 'GROUP BY name'
限制和分页

使用 limit 和 offset 参数可以进行 限制/分页:

Project.findAll({ offset: 5, limit: 5 });
// 跳过5个实例,然后获取5个实例
实用方法:count/max/min/sum
await User.max('age'); // 40
await User.max('age', { where: { age: { [Op.lt]: 20 } } }); // 10
// 其他三个方法使用类似

查找器

findAll
查询将从表中检索所有条目
findByPK
findByPk 方法使用提供的主键从表中仅获得一个条目.
findOne
方法获得它找到的第一个条目(它可以满足提供的可选查询参数).
findOrCreate
除非找到一个满足查询参数的结果,否则方法 findOrCreate 将在表中创建一个条目. 在这两种情况下,它将返回一个实例(找到的实例或创建的实例)和一个布尔值,指示该实例是已创建还是已经存在.

const [user, created] = await User.findOrCreate({...});
// user返回找到或创建的实例,created是否创建

findAndCountAll
findAndCountAll 方法是结合了 findAll 和 count 的便捷方法. 在处理与分页有关的查询时非常有用,在分页中,你想检索带有 limit 和 offset 的数据,但又需要知道与查询匹配的记录总数.
返回两个对象count(符合条件记录的总数)和rows(获得记录的数组对象)

const { count, rows } = await User.findOrCreate({...});
//count - 一个整数 - 符合查询条件的记录总数
// rows - 一个数组对象 - 获得的记录

设置/获取器、虚拟字段

获取器get() / 设置器set()
有点类似于js中的拦截器,获取或设置的时候触发相应的方法。
在定义模型的时候添加get()和set()方法。

const User = sequelize.define('user', {
  username: {
    type: DataTypes.STRING,
    get() {
      const rawValue = this.getDataValue('username');
      return rawValue ? rawValue.toUpperCase() : null;
    }
    set(value) {
    	this.setDataValue('username', value.toLowerCase());
    }
  }
});

上面的实现的就是输入数据库的是小写,拿出数据的数据是大写。结合get和set可以进行一些格式化或者是数据加密的操作。

 
虚拟字段
通过设置type:DataTypes.VIRTUAL将该字段设置为虚拟字段,不存在于数据库中(不可设置)。
作用:结合get可以实现便捷拿出复合信息,类似compute属性的效果。

const User = sequelize.define('user', {
  firstName: DataTypes.TEXT,
  lastName: DataTypes.TEXT,
  fullName: {
    type: DataTypes.VIRTUAL,
    get() {
      return `${this.firstName} ${this.lastName}`;
    },
    set(value) {
      throw new Error('不要尝试设置 `fullName` 的值!');
    }
  }
});

验证&约束

验证是在纯 JavaScript 中在 Sequelize 级别执行的检查. 如果你提供自定义验证器功能,它们可能会非常复杂,也可能是 Sequelize 提供的内置验证器之一. 如果验证失败,则根本不会将 SQL 查询发送到数据库.

另一方面,约束是在 SQL 级别定义的规则. 约束的最基本示例是唯一约束. 如果约束检查失败,则数据库将引发错误,并且 Sequelize 会将错误转发给 JavaScript(在此示例中,抛出 SequelizeUniqueConstraintError). 请注意,在这种情况下,与验证不同,它执行了 SQL 查询.
约束

/* ... */ {
  username: {
    type: DataTypes.TEXT,
    allowNull: false, // 非空校验
    unique: true,// 唯一性约束
  },
} /* ... */

校验
校验可以是用sequelize内置的校验器validator.js

/* ... */ {
  ages: {
    type: DataTypes.TEXT,
    allowNull: false, // 非空校验
    validate: {
      len: [5, 10],
      notNull: {
          msg: '请输入年龄!'
       }
    }
  },
  size: {
    type: DataTypes.STRING,
    validate: {
      isIn: [['L', 'XL']],  //校验是否是L、XL码
    }
  }
} /* ... */

也可以自定义校验条件

name: {
  type: DataTypes.STRING,
  allowNull: true,
  validate: {
    customValidator(value) {
	  if (value === null && this.age !== 10) {
	    throw new Error("除非年龄为10,否则名称不能为 null");
      }
	}
  }
}	    

原始查询

sequelize.query(‘sql语句’)
默认情况下,函数将返回两个参数 - 一个结果数组,以及一个包含元数据(例如受影响的行数等)的对象.

const [results, metadata] = await sequelize.query("UPDATE users SET y = 42 WHERE x = 12");
await sequelize.query("SELECT * FROM `users`", { type: QueryTypes.SELECT }); // 查询格式化

如果需要格式化,可以在后续传递一个查询类型参数{type: QueryType.SELECT}还可以通过传递模型实例来格式化{mode: xxx, mapToModel: true}

替换

  • 如果传递一个数组, ? 将按照它们在数组中出现的顺序被替换
  • 如果传递一个对象, :key 将替换为该对象的键. 如果对象包含在查询中找不到的键,则会抛出异常,反之亦然.
await sequelize.query("SELECT * FROM `users` where size = ? or size = ?",
// await sequelize.query("SELECT * FROM `users` where size = :key1 or size = key2",
  {
    replacements: ['foo', 'bar'],
    // replacements: { key1: 'foo', key2: 'bar' },
    type: QueryTypes.SELECT
  });
  
// => SELECT * FROM `users` where size = 'foo' or size = 'bar'

注: 替换是整个value的替换,包括其’ '、[]、%等符号

绑定参数
绑定和替换类似,仅是绑定是用$number和$key标识, 替换是?和:key标识

  • 如果传递一个数组, $1 被绑定到数组中的第一个元素 (bind[0]).
  • 如果传递一个对象, $key 绑定到 object[‘key’]. 每个键必须以非数字字符开始. $1 不是一个有效的键,即使 object[‘1’] 存在.
  • 在这两种情况下 $$ 可以用来转义一个 $ 字符符号.

偏执表

偏执表会执行软删除,一个 paranoid 表是一个被告知删除记录时不会真正删除它的表.反而一个名为 deletedAt 的特殊列会将其值设置为该删除请求的时间戳。
定义
定义模型的时候设置options: paranoid: true
注: Paranoid 需要时间戳才能起作用(即,如果你传递 timestamps: false 了,paranoid 将不起作用)。自定义列名可通过deleteAt设置。

sequelize.define('User', {
  // ... (属性)
}, {
  tparanoid: true,
  // 如果要为 deletedAt 列指定自定义名称
  deletedAt: 'destroyTime'
});

这样你在调用destroy方法删除的时候仅执行软删除,可以通过restore方法恢复

// 展示实例 `restore` 方法的示例
// 我们创建一个帖子,对其进行软删除,然后将其还原
const post = await Post.create({ title: 'test' });
console.log(post instanceof Post); // true
await post.destroy();
console.log('soft-deleted!');
await post.restore();
console.log('restored!');

注:如果想要永久删除的可以设置force为true即可 await post.destroy({ force: true });

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值