Sequelize手记 - (一)

最近开始接触数据库,如今广泛用的都是Mysql数据库,简单的了解了一下sql语句,没有太深刻的学习,而后就开始找相关的ORM框架,而后锁定了Sequelize,我的感受很强大,搜索了一些文档,可是很让人费解,讲的每一部分都是那么的官方,不太容易理解,记录一下学习路程。本文档以koa+Sequelize进行编码测试。javascript

准备工做

在尝试使用Sequelize以前先确认是否安装了Mysql数据库,安装node,这里使用的Mysql 5.6版本。前端

首先要建立一个项目执行命令以下:java

mkdir 文件夹名称
cd 文件夹名称
npm init -y     // 直接略过全部问答,所有采用默认答案

在开始以前首先要安装相关依赖:node

//  安装 sequelize koa mysql2
npm install --save-dev sequelize koa mysql2

注意:这里是mysql2mysql

创建链接

建立main.js文件,分别引入koasequelize创建与mqsql数据库链接。在实例化Sequelize时须要配置一些参数,第一个接收的参数时数据库的名称,第二个是用户名,第三个是密码,第四项是链接mysql的相关配置。sql

const Koa = require("koa");
const Sequelize = require("sequelize");
const app = new Koa();
const mysqlConfig ={
    host: 'localhost',  //  接数据库的主机
    port: '3306',       //  接数据库的端口
    protocol: 'tcp',    //  链接数据库使用的协议
    dialect: 'mysql',   //  使用mysql
    pool: { 
        max: 5,         //  最大链接数量
        min: 0,         //  最小链接数量
        idle: 10000     //  链接空置时间(毫秒),超时后将释放链接
    },
    retry: {        //  设置自动查询时的重试标志
        max: 3          //  设置重试次数
    },
    omitNull: false,    //  null 是否经过SQL语句查询
    timezone: '+08:00'  //  解决时差 - 默认存储时间存在8小时偏差
};
const sequelize = new Sequelize('aarontest', 'root', '123456',mysqlConfig );
app.listen(3000);

经过上述代码就已经与Mysql创建了链接。这个地方没有什么能够讲的,按照上述进行配置就能够了。test数据库不必定要存在不存在也是能够正常创建链接的。配置参数还有不少,这里只说了一些经常使用的,就很少赘述了。若是以为上述方法比较麻烦能够经过也提供了其余方法进行链接:数据库

const sequelize = new Sequelize('mysql://root:123456@localhost:3306/aarontest', {
    ...mysqlConfig
});

这种链接方式与mongoose链接方法差很少,以致于具体使用哪一种方式仍是要根据我的的编码习惯来决定。npm

创建model

sequelize是经过define方法创建模型的,Model至关于数据库中的表,该对象不能经过构造函数实例化,而只能经过sequelize.define()sequelize.import()方法建立。json

const AaronTest = sequelize.define('project', {
  title: Sequelize.STRING,
  description: Sequelize.TEXT
})

经过上面的方法建立好model以后,使用Navicat(数据库可视化工具)仍然没法看到咱们所建立的表,尴尬。。。sequelize提供了一个sync()方法能够同步模型到数据库。使用该方法的时候必定要注意所链接的数据库必定要存在不然会报错。后端

define方法接收三个参数,第一个参数为表名称,第二个为所须要建立的数据库字段,第三个参数是相关表配置。

const AaronTest = sequelize.define('project', {
  title: Sequelize.STRING,
  description: Sequelize.TEXT
});
AaronTest.sync();

这样model所建立的字段就被同步到了数据库中,一样相对应的表也被建立好了,须要注意经过这种方式同步的表会在表名称后面添加一个s做为复数。同步数据库是会默认添加两个字段createdAtupdatedAt有的时候可能不须要这两个字段。这个时候须要在第三个字段中添加timestampsfalse默认为true

const AaronTest = sequelize.define('project', {
  title: Sequelize.STRING,
  description: Sequelize.TEXT
},{
  timestamps: false
})

上面能够看出经过Sequelize.STRING设置当前字段的类型,Sequelize提供了不少数据类型供咱们进行使用:

类型 说明
STRING 将字段指定为变长字符串类型,默认长度为 255。Sequelize.STRING(64)
CHAR 将字段指定为定长字符串类型,默认长度为 255。Sequelize.CHAR(64)
TEXT 将字段指定为(无)有限长度的文本列。可用长度:tiny, medium, long,Sequelize.TEXT('tiny')
INTEGER 32位整型,可用属性:UNSIGNED,ZEROFILL,Sequelize.INTEGER('UNSIGNED')
BOOLEAN 小数,接受一个或两个参数表示精度,Sequelize.BOOLEAN()
TIME 指定为时间类型列,Sequelize.TIME()
DATE 指定为日期时间类型列,Sequelize.DATE()
DATEONLY 指定为日期类型列,Sequelize.DATEONLY()
HSTORE 指定为键/值类型列,仅Postgres适用,Sequelize.HSTORE()
JSON 指定为JSON字符串类型列,仅Postgres适用,Sequelize.JSON()
JSONB 指定为预处理的JSON数据列,仅Postgres适用,Sequelize.JSONB()
NOW 一个表示当前时间戳的默认值,Sequelize.NOW()
UUID UUID类型列,其默认值能够为UUIDV1或UUIDV4,Sequelize.UUID()
ENUM 枚举类型,Sequelize.ENUM()
ARRAY 数组类型,仅Postgres适用,Sequelize.ARRAY()

设定model的时候在添加第三个参数能够对当前model进行二次设置,以使数据库更增强壮,经常使用字段以下

  • timestamps:不要添加时间戳属性 (updatedAt, createdAt)
  • paranoid:paranoid 属性只在启用 timestamps 时适用,不从数据库中删除数据,而只是增长一个 deletedAt 标识当前时间,咱们常说的逻辑删除
  • underscored: 不使用驼峰式命令规则,这样会在使用下划线分隔,updatedAt的字段名会是 updated_at
  • freezeTableName:禁止修改表名. 默认状况下sequelize会自动使用传入的模型名(define的第一个参数)作为表名,若是你不想使用这种方式你须要进行如下设置
  • tableName:定义表名
  • createdAt:不想使用 createdAt
  • updatedAt:想 updatedAt 的实际名为'***'
  • deletedAt: 要将 deletedAt 设置为 destroyTime (注意要启用paranoid)

当建立model的时候可能须要有些附加属性,好比主键,自增,不能为null,默认值等等,能够在建立model的时候进行手动设置。

  • autoIncrement:是否自增
  • references:经过references选项能够建立外键
  • allowNull:设置 allowNull 选项为 false 后,会为列添加 NOT NULL 非空限制
  • defaultValue:设置默认值
  • type:字段类型
  • unique:添加惟一(unique)约束后插入重复值会报错,unique属性能够是boolean 或 string类型
  • primaryKey:设置为主键
  • comment:字段描述
  • field:指定数据库中的字段名

除了这些之外Sequelize在定义model的时候,还提供了一个validate属性,能够为添加的字段进行校验处理:

字段 说明 值类型
is 存储值必须知足正则 正则
not 除正则以外的值 布尔
isEmail 是否为邮箱 布尔
isUrl 检查Url格式 布尔
isIP 检查 IPv4 或 IPv6 格式 布尔
isIPv4 检查 IPv4 布尔
isIPv6 检查 IPv6 布尔
isAlpha 不能使用字母 布尔
isAlphanumeric 只容许字母数字字符 布尔
isNumeric 只能使用数字 布尔
isInt 只能是整数 布尔
isFloat 只能是浮点数 布尔
isDecimal 检查数字 布尔
isLowercase 检查小写字母 布尔
isUppercase 检查大写字母 布尔
notNull 不容许null 布尔
isNull 只能为null 布尔
notEmpty 不能空字符串 布尔
equals 只能使用指定值 字符串
contains 必须包含子字符串 字符串
notIn 不能是数组中的任意一个值 数组
isIn 只能是数组中的任意一个值 数组
notContains 不能包含子字符串 字符串
len 值的长度必在 2 和 10 之间 数组
isUUID 只能是UUID 数字
isDate 只能是日期字符串 布尔
isAfter 只能使用指定日期以后的时间 字符串
isBefore: 只能使用指定日期以前的时间 字符串
max 容许的最大值 数字
min 容许的最小值 数字
isArray 不能使用数组 布尔
isCreditCard 检查是有效的信用卡 布尔

除了上面的校验方法之外还提供了自定义校验方法,使用isEven去定义一个函数,其函数的第一个参数就是所存如的值:

const AaronTest = sequelize.define('project', {
    title: Sequelize.STRING,
    description: {
    type:Sequelize.TEXT,
    validate:{
        isEven: function(value) {
            if(parseInt(value) % 2 != 0) {
                throw new Error('Only even values are allowed!')
            }
        }
    }
    }
},{
    timestamps: false
})

上面说过使用sequelize.import()也能够建立model这个方法实际上是模型导入,经过文件导入模型定义。检查模型是否已经定义。被导入的模型会被缓存,因此屡次导入并不会重复加载,path表示要导入文件的路径,若是使用相对路径会自动转换为绝对路径。

const AaronTest = sequelize.import('../model/user.js');

user.js

module.exports = function(sequelize, DataTypes) {
  return sequelize.define("project", {
    name: DataTypes.STRING,
    description: DataTypes.TEXT
  })
}

CRUD

CRUD:是指在作计算处理时的增长(Create)、读取(Read)、更新(Update)和删除(Delete)几个单词的首字母简写。crud主要被用在描述软件系统中数据库或者持久层的基本操做功能。

建立

建立数据的方法有不少种,这里简单的介绍一些经常使用的:

第一种:

先建立数据实例,而后调用实例的save方法,完成数据存储。

const Aaron = AaronTest.build({
  'title': `后端 | ${Math.random()}`,
  'description': '技术部'
});
Aaron.save().then((result) => {
    //  成功
    console.log(result)
}).catch((error) => {
    //  失败
    console.log(error)
})

第二种:

经过静态create方法

const user = AaronTest.create({
  'title': `前端 | ${Math.random()}`,
  'description': '网络部'
}).then(function(result) {
    //  成功
    console.log(result)
}).catch(function(error) {
    //  失败
    console.log(error)
});

读取

经过findAll方法读取数据。

AaronTest.findAll({
  where: {
    description: '网络部'
  },
  limit: 10,
  offset: 0
}).then(function(result) {
  // success
  console.log(result)
}).catch(function(error) {
  // error
  console.log(error)
});

经过上述方法建立的数据能够直接做为返回结果返回给前台,可是若是对查询到的结果进行数据操做时不行的,由于查询到的结果是sequelize处理过的模型,若是想对其操做须要在参数中添加row:true属性。

AaronTest.findAll({
  where: {
    description: '网络部'
  },
  limit: 10,    //  查询多少条
  offset: 0,    //  查询开始位置
  raw:true
}).then(function(result) {
  // success
  console.log(result)
}).catch(function(error) {
  // error
  console.log(error)
});

添加row:true返回的则是一个没有被包装过的数组了。在项目过程当中须要查询一下当前所查询的数据共有多少条返回给前端。

AaronTest.count({
  where:{
    description: '网络部'
  }
}).then().then(function(result) {
  // success
  console.log(result)
}).catch(function(error) {
  // error
  console.log(error)
});

使用这种方法是确实能够查询到想要的结果,可是不管是先查询列表仍是先查询总数,都须要对数据库进行两次查询很繁琐。sequelize提供了一个很方便的方法。

AaronTest.findAndCountAll({
  where: {
    description: '网络部'
  },
  limit: 10,
  offset: 0,
  raw:true,
  attributes:["id", "title"]    //  须要查询出的字段
}).then(function(result) {
  // success
  console.log(result)
}).catch(function(error) {
  // error
  console.log(error)
});

经过上面的方法则能够查询到总数以及条件范围内的数据,一箭双雕。查询结果返回的是一个json对象,其包括controws两个属性,分别是总数和数据。很舒服有没有。

以上方式是查询列表,查询单条数据使用其余方法:

AaronTest.findOne({
  where:{
    id:6
  },
  raw:true,
  attributes:["id", "title"] 
}).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)
})

更新

修改数据能够直接调用静态的update方法,经过where条件查询,对其搜索到的数据进行查询,并对查询到的数据进行更改。

AaronTest.update({
  description: '前端部',
  title:`前端 | ${Math.random()}`
},{
  where:{
    description: "网络部"
  }
}).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)
})

该方法修改全部查询的到的数据,返回结果为数组形式,数据只有一个值,也就是数组的第0项,则是N条数据修改为功。

删除

删除操做经过destroy方法,一样也是经过where条件查询,对所查询数据进行删除。

AaronTest.destroy({
  where: {
    description: "UI部",
  }
}).then(function(result) {
  console.log(result)
}).catch(function(error) {
  console.log(error)
});

当删除成功后,返回结果为Number,删除多少条数据,若是没有删除则会返回0。此方法属于物理删除,删除后没法进行恢复。

查询参数

CRUD操做过程当中,都少不了的就是查询,细心的应该能够看的出,上面的例子中查询的时候多多少少的对其进行了一些小的改动。Sequelize中有两种查询:使用Model(模型)中的方法查询和使用sequelize.query()进行基于SQL语句的原始查询。上面用到的是Model查询方式,接下来就详细的介绍一些经常使用的参数以及其表明的意义。

attributes - 属性与查询字段

查询时,若是只须要查询模型的部分属性,能够在经过在查询选项中指定attributes实现。该选项是一个数组参数,在数组中指定要查询的属性便可,这个字段在上面进行查询的时候已经使用过了。

AaronTest.findOne({
  where:{
    id:6
  },
  raw:true,
  attributes:["id", "title", "description"]
}).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)
})

查询属性(字段)能够经过传入一个嵌套数据进行重命名,这里须要强调一下重命名所指的是对查询出的数据键值进行重命名处理,而不是更改数据表中的字段名称。

AaronTest.findOne({
  where:{
    id:2
  },
  attributes:["id", ["title","t"]],
  raw:true
}).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)
})
//  注意这里t ↓
//  { id: 2, t: '前端 | 0.8765218593370694' }

经过sequelize.fn方法能够进行聚合查询,我的以为这个方法不太经常使用,可是仍是简单的介绍一下,这种查询方式是向当前查询内容中添加一个新的属性,而且查询的是列表仍是查询单条数据。

应用场景:

好比有不少商品,每一个商品都有本身的分类,根据id进行查询了一个商品,可是与其同类的商品有多少?就可使用这个方法添加进去。下面例子中的count则是添加进去属性的键。

AaronTest.findAll({
    where:{
        id:2
    },
    attributes: [
        "id", 
        ["title","t"],
        [sequelize.fn('COUNT', sequelize.col('id')), 'count']
    ],
    raw:true
}).then((result) => {
    console.log(result)
}).catch((error) => {
    console.log(error)
})

可能有一种状况,当前所须要查询的表字段太多,可是只有一两个数据不想要,在attributes数组中添加很长的字段名称,这样会显得代码很臃肿。attributes不光能够为数组,还能够为对象在对象存在exclude这个属性,这个属性就是剔除掉那些不想要的属性。

AaronTest.findOne({
  where:{
    id:2
  },
  attributes:{
    exclude: ['id'] 
  },
  raw:true
}).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)
})

where - 指定筛选条件

上面的那么多例子中where出现的次数最多了,除了增长数据不须要,其余的都须要用到where条件,能够指定一个where选项以指定筛选条件,where是一个包含属性/值对对象,sequelize会根据此对象生产查询语句的筛选条件。

where的基础用法也就向上面那样,针对某些特定的条件进行查询处理。

AaronTest.findOne({
  where:{
    id:2
  },
  attributes:{
    exclude: ['id'] 
  },
  raw:true
}).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)
})

就像上面那样简单的查询没法知足全部的业务需求,Sequelize还提供了操做符以知足更多的查询条件,经常使用的操做符以下:

$and: {a: 5}                    // AND (a = 5)
$or: [{a: 5}, {a: 6}]           // (a = 5 OR a = 6)
$gt: 6,                         // > 6
$gte: 6,                        // >= 6
$lt: 10,                        // < 10
$lte: 10,                       // <= 10
$ne: 20,                        // != 20
$not: true,                     // IS NOT TRUE
$between: [6, 10],              // BETWEEN 6 AND 10
$notBetween: [11, 15],          // NOT BETWEEN 11 AND 15
$in: [1, 2],                    // IN [1, 2]
$notIn: [1, 2],                 // NOT IN [1, 2]
$like: '%hat',                  // LIKE '%hat'
$notLike: '%hat'                // NOT LIKE '%hat'
$iLike: '%hat'                  // 包含'%hat' (case insensitive) (PG only)
$notILike: '%hat'               // 不包含'%hat'  (PG only)
$like: { $any: ['cat', 'hat']}  // 像任何数组['cat', 'hat'] -也适用于iLike和notLike

limit/offset - 分页与限制返回结果数

在进行列表查询时,不能把查询道德全部数据所有返回出去,须要对数据进行分页处理。

// 获取 10 条数据(实例)
AaronTest.findAll({ limit: 10 })
// 跳过 8 条数据(实例)
AaronTest.findAll({ offset: 8 })
// 跳过 5 条数据并获取其后的 5 条数据(实例)
AaronTest.findAll({ offset: 5, limit: 5 })

查询排序

order选项用于查询结果的排序数据。排序时应该传入一个包含属性-排序方向的元组/数组,以保证正确的转义:

AaronTest.findAll({
  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)')
})

上面说的这些对于SQL语句了解一些,都是很容理解,有些API不经常使用也就没些,详细能够查看中文文档。

SQL语句查询

原始查询中有两种替换查询参数的方法,以:开头的参数的形式替换或以不命名以?替换。在选项对象中传递参数:

  • 若是传递一个数组,? 会按数组的顺序被依次替换
  • 巢传递一个对象,:key将会用对象的键替换。若是对象中未找到指定键,则会引起异常(反之亦然)
//  这里是sequelize,并非model
sequelize.query('SELECT * FROM projects WHERE id = ?',
  { replacements: ['active'], type: sequelize.QueryTypes.SELECT }
).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)
})

总结

以上是对sequelizeapi进行了整理,虽然不太全面,熟练掌握上述API能够作一个项目了,有关sequelize的更多的用法我也在继续爬坑中,可能文章中有些许错误,你们能够在下方留言,我会尽快作出改正。感谢你们的阅读。

相关文章
相关标签/搜索