Sequelize.js是一款针对nodejs的ORM框架。javascript
使用nodejs链接过数据库的人确定对数据库不陌生了。若是是直接连接,须要本身创建并管理链接,还须要手动编写sql语句。简单的项目到是无所谓,但是一旦项目设计的东西比较复杂,表比较多的时候整个sql的编写就很是的消耗精力。前端
在Java、c#等语言中已经有轻量的数据库框架或者解决方案了。在nodejs中我推荐Sequelize。它是一个很成熟的框架,在速度和性能上也很是有优点。而其中最关键的地方就在于,平常开发只须要管理对象的建立、查询方法的调用等便可,极少须要编写sql语句。这一个好处就是省去了复杂的sql语句维护,同时也避免了因sql而引发的没必要要的bug。java
Sequelize是针对node.js和io.js提供的ORM框架。具体就是突出一个支持普遍,配置和查询方法统一。它支持的数据库包括:PostgreSQL、 MySQL、MariaDB、 SQLite 和 MSSQL。node
本文中测试以及API展现地址:github地址mysql
Sequelize的调用突出一个简单快捷。具体状况能够感觉一下下面的代码。若是有过开发经验的能够略过。git
Table1.findById(23);
//select a,b,c,d from table1 where id=23;
Table1.findAll({
where:{a:"test",b:76}
});
//select a,b,c,d from table1 where a="test" and "b=76;
复制代码
在单表查询的时候只须要简单的配置便可完成查询。是否是很是的简单方便呢?github
Sequelize的链接须要传入参数,而且能够配置开启线程池、读写分库等操做。sql
简单的写法是这样的:new Sequelize("表名","用户名","密码",配置)
数据库
正常使用中不多使用到全部的参数,这里提供一个经常使用的模板,只须要修改本身使用的值便可。c#
const sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost', //数据库地址,默认本机
port:'3306',
dialect: 'mysql',
pool: { //链接池设置
max: 5, //最大链接数
min: 0, //最小链接数
idle: 10000
},
});
复制代码
下面是详细的配置参数。
const sequelize = new Sequelize('database', 'username', 'password', {
// 数据库类型,支持: 'mysql', 'sqlite', 'postgres', 'mssql'
dialect: 'mysql',
// 自定义连接地址,能够是ip或者域名,默认本机:localhost
host: 'my.server.tld',
// 自定义端口,默认3306
port: 12345,
// postgres使用的参数,链接类型,默认:tcp
protocol: null,
// 是否开始日志,默认是用console.log
// 建议开启,方便对照生成的sql语句
logging: true,
// 默认是空
// 支持: 'mysql', 'postgres', 'mssql'
dialectOptions: {
socketPath: '/Applications/MAMP/tmp/mysql/mysql.sock',
supportBigNumbers: true,
bigNumberStrings: true
},
// sqlite的存储位置,仅sqlite有用
// - 默认 ':memory:'
storage: 'path/to/database.sqlite',
// 是否将undefined转化为NULL
// - 默认: false
omitNull: true,
// pg中开启ssl支持
// - 默认: false
native: true,
// 数据库默认参数,全局参数
define: {
underscored: false
freezeTableName: false,
charset: 'utf8',
dialectOptions: {
collate: 'utf8_general_ci'
},
timestamps: true
},
// 是否同步
sync: { force: true },
// 链接池配置
pool: {
max: 5,
idle: 30000,
acquire: 60000,
},
isolationLevel: Transaction.ISOLATION_LEVELS.REPEATABLE_READ
})
复制代码
在使用以前必定要先建立模型对象。就是数据库中表的名称、使用到的字段、字段类型等。
这里有一个推荐的开发方式。先在nodejs中将对象建立出来,而后调用Sequelize的同步方法,将数据库自动建立出来。这样就避免了既要写代码建表,又要手工建立数据库中的表的操做。只须要单独考虑代码中的对象类型等属性就行了。
若是数据库中已经建好了表,而且不能删除,这个时候就不能自动建立了,由于建立的时候会删除掉旧的数据。
下面是简单的对象建立多数状况下这样就能够了。
const users = db.define('t_user'/*自定义表名*/, {
id: {
type: Sequelize.INTEGER,
primaryKey: true, //主键
autoIncrement: true, //自增
comment: "自增id" //注释:只在代码中有效
},
//用户名
username: {
type: Sequelize.STRING,
validate:{
isEmail: true, //类型检测,是不是邮箱格式
}
},
//密码
pwd: {
type: Sequelize.STRING(10),
allowNull: false,//不容许为null
},
//状态
status: {
type: Sequelize.INTEGER,
defaultValue: 0,//默认值是0
},
//昵称
nickname: {
type: Sequelize.STRING
},
//token
token: {
type: Sequelize.UUID
},
create_time: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
}, {
//使用自定义表名
freezeTableName: true,
//去掉默认的添加时间和更新时间
timestamps: false,
indexes:[
//普通索引,默认BTREE
{
unique: true,
fields: ['pid']
},
]
});
//同步:没有就新建,有就不变
// users.sync();
//先删除后同步
users.sync({
force: true
});
复制代码
前段将了对象的建立,里面用到了对象的各类类型。这里再介绍一下类型的具体使用方式。
Sequelize.STRING //字符串,长度默认255,VARCHAR(255)
Sequelize.STRING(1234) //设定长度的字符串,VARCHAR(1234)
Sequelize.STRING.BINARY //定义类型VARCHAR BINARY
Sequelize.TEXT //长字符串,文本 TEXT
Sequelize.TEXT('tiny') //小文本字符串,TINYTEXT
Sequelize.INTEGER //int数字,int
Sequelize.BIGINT //更大的数字,BIGINT
Sequelize.BIGINT(11) //设定长度的数字,BIGINT(11)
Sequelize.FLOAT //浮点类型,FLOAT
Sequelize.FLOAT(11) //设定长度的浮点,FLOAT(11)
Sequelize.FLOAT(11, 12) //设定长度和小数位数的浮点,FLOAT(11,12)
Sequelize.REAL //REAL PostgreSQL only.
Sequelize.REAL(11) // REAL(11) PostgreSQL only.
Sequelize.REAL(11, 12) // REAL(11,12) PostgreSQL only.
Sequelize.DOUBLE // DOUBLE
Sequelize.DOUBLE(11) // DOUBLE(11)
Sequelize.DOUBLE(11, 12) // DOUBLE(11,12)
Sequelize.DECIMAL // DECIMAL
Sequelize.DECIMAL(10, 2) // DECIMAL(10,2)
Sequelize.DATE // 日期类型,DATETIME for mysql / sqlite, TIMESTAMP WITH TIME ZONE for postgres
Sequelize.DATE(6) // mysql 5.6.4+支持,分秒精度为6位
Sequelize.DATEONLY // 仅日期部分
Sequelize.BOOLEAN // int类型,长度为1,TINYINT(1)
Sequelize.ENUM('value 1', 'value 2') // 枚举类型
Sequelize.ARRAY(Sequelize.TEXT) //PostgreSQL only.
Sequelize.ARRAY(Sequelize.ENUM) // PostgreSQL only.
Sequelize.JSON // JSON column. PostgreSQL, SQLite and MySQL only.
Sequelize.JSONB // JSONB column. PostgreSQL only.
Sequelize.BLOB // BLOB (bytea for PostgreSQL)
Sequelize.BLOB('tiny') // TINYBLOB (bytea for PostgreSQL. Other options are medium and long)
Sequelize.UUID // PostgreSQL和SQLite的数据类型是UUID, MySQL是CHAR(36)类型
Sequelize.CIDR // PostgreSQL中的CIDR类型
Sequelize.INET // PostgreSQL中的INET类型
Sequelize.MACADDR // PostgreSQL中的MACADDR类型
Sequelize.RANGE(Sequelize.INTEGER) //PostgreSQL only.
Sequelize.RANGE(Sequelize.BIGINT) // PostgreSQL only.
Sequelize.RANGE(Sequelize.DATE) //PostgreSQL only.
Sequelize.RANGE(Sequelize.DATEONLY) //PostgreSQL only.
Sequelize.RANGE(Sequelize.DECIMAL) //PostgreSQL only.
Sequelize.ARRAY(Sequelize.RANGE(Sequelize.DATE)) // PostgreSQL only.
Sequelize.GEOMETRY //PostgreSQL (with PostGIS) or MySQL only.
Sequelize.GEOMETRY('POINT') // PostgreSQL (with PostGIS) or MySQL only.
Sequelize.GEOMETRY('POINT', 4326)// PostgreSQL (with PostGIS) or MySQL only.
复制代码
上面能够看到使用validate字段去验证字段的值是否符合标准,这样就能够在入库以前就能知道数据是否符合规则。不然贸然将陌生的数据存入数据库就好像将陌生人带到家里同样,是否安全全靠缘分啊。
Sequelize内置支持的验证仍是很是的多的,若是这些都不满意,还能够本身定义一个。
validate: {
is: ["^[a-z]+$",'i'], // 全匹配字母
is: /^[a-z]+$/i, // 全匹配字母,用规则表达式写法
not: ["[a-z]",'i'], // 不能包含字母
isEmail: true, // 检查邮件格式
isUrl: true, // 是不是合法网址
isIP: true, // 是不是合法IP地址
isIPv4: true, // 是不是合法IPv4地址
isIPv6: true, // 是不是合法IPv6地址
isAlpha: true, // 是不是字母
isAlphanumeric: true, // 是不是数字和字母
isNumeric: true, // 只容许数字
isInt: true, // 只容许整数
isFloat: true, // 是不是浮点数
isDecimal: true, // 是不是十进制书
isLowercase: true, // 是不是小写
isUppercase: true, // 是否大写
notNull: true, // 不容许为null
isNull: true, // 是不是null
notEmpty: true, // 不容许为空
equals: 'specific value', // 等于某些值
contains: 'foo', // 包含某些字符
notIn: [['foo', 'bar']], // 不在列表中
isIn: [['foo', 'bar']], // 在列表中
notContains: 'bar', // 不包含
len: [2,10], // 长度范围
isUUID: 4, // 是不是合法 uuids
isDate: true, // 是不是有效日期
isAfter: "2011-11-05", // 是否晚于某个日期
isBefore: "2011-11-05", // 是否早于某个日期
max: 23, // 最大值
min: 23, // 最小值
isArray: true, // 是不是数组
isCreditCard: true, // 是不是有效信用卡号
// 自定义规则
isEven: function(value) {
if(parseInt(value) % 2 != 0) {
throw new Error('请输入偶数!')
}
}
复制代码
Sequelize的API基本覆盖了经常使用的使用方式,其中单表查询经常使用的有一下几种。复杂的能够参考更多的API。
查询用的参数广泛通用,只有部分API的有特殊参数。这里展现一次经常使用参数,下面就略过了。
let list = await model.findAll({
where:{
id:{$gt:10},//id大于10的
name:"test" //name等于test
},
order:[
"id", //根据id排序
["id","desc"]//根据id倒序
],
limit:10,//返回个数
offset:20,//起始位置,跳过数量
attributes:["attr1","attr2"], //返回的字段
});
//select attr1,attr2 from model where ......
复制代码
这里默认数据的主键是id,查询的时候直接经过id查询数据。这里推荐在新建数据库的时候能够添加id做为惟一主键。
let model = await model.findById(12);
//select a,b,c from model where id=12;
复制代码
根据条件查询记录,这里的条件必定要填写,否则就是返回第一条数据了。
let model = await model.findOne({
where:{id:12}
});
//select a,b,c from model where id=12;
复制代码
分页查询恐怕是另一个经常使用方法了。任何一个列表都有须要分页的时候。
这个方法会同时执行2跳语句。
let data = await model.findAndCount({
limit:10,//每页10条
offset:0*10,//第x页*每页个数
where:{}
});
let list = data.rows;
let count = data.count;
//select count(*) from model where ...;
//select a,b,c from model where .... limit 0,10;
复制代码
添加就很是的自在了。简单的只须要传入model对象便可。这里要保证model对象的属性和字段名要一致。若是不一致就会出错。也能够传入配置参数来增长条件等。
let model= {
name:"test",
token:"adwadfv2324"
}
await model.create(model);
//insert into model (name,token) values("test","adwadfv2324");
复制代码
opts.default 默认值对象
这个方法首先会查询数据库,若是没有结果就会返回参数中的default对象。这个比较适合返回默认对象之类的场景。
这个方法用到的状况也比较多。一般用于自动建立不存在的数据。直接就返回了默认值。
根据主键或者惟一约束键匹配
经常使用于编辑的时候添加或者更新统一操做。
就是最经常使用的更新方法,能够传入要更新的model对象,同时用配置参数有条件的区别要更新的对象。
删除有2种状况,一种是物理删除。删除就从表中不存在了。另一种就是设置paranoid,这个是虚拟删除,默认一个字段表示数据是否删除,查询的时候去掉这个条件便可查询到删除的数据。
恢复多个实例,当启用paranoid时就可使用这个方法将曾今删除的数据恢复了。
Sequelize中的事务比较简单。可是若是有多个事务的话写出来的代码会很是的难看。这也算是Sequelize优化的比较差的地方了。
须要记得transaction参数要一致传递就能够了。其余就是一个正常的Promise调用。
//调用Sequelize初始化以后的sequelize对象
return sequelize.transaction(function (t) {
//返回最终的Promise
return User.create({
firstName: 'Abraham',
lastName: 'Lincoln'
}, {transaction: t}).then(function (user) {
return user.setShooter({
firstName: 'John',
lastName: 'Boothe'
}, {transaction: t});
});
}).then(function (result) {
//主动调用commit提交结果
return t.commit();
}).catch(function (err) {
//主动回滚操做
return t.rollback();
});
复制代码
外键可能算是Sequelize中的一个难点了。这里涉及的东西稍微多一点,咱们来慢慢捋一遍。
外键的定制做用----三种约束模式:
在Sequelize中使用外键须要提早检查一下下面的这些选项,里面有一条出错就会致使设置失败。
默认状况下,主键使用的是主表的id字段,外键是使用的按照table+字段的方式创建的外键。通常状况下须要手动指定。
//主表指定关系
test1.hasMany(test2, {
foreignKey: "pid",//外键名称
});
//子表指定关系
test2.belongsTo(test1, {
foreignKey: "pid",//外键名称
});
复制代码
默认就会在子表中添加一条外键记录,指向的就是主表的id。通常状况下这样就可以知足正常的使用了。好比一个主表记录商品信息,一个子表记录多个评论消息。
若是主表使用的主键id并不能知足正常的使用,还能够指定某一个固定的字段做为主表中的约束关系。
tips:主表中若是不是使用id做为主要关系,自定义的字段必须添加索引等条件,做为依赖中的关系。
test1.hasMany(test2, {
foreignKey: "pid",//外键字段名
sourceKey: "pid",//主键字段名
});
test2.belongsTo(test1, {
foreignKey: "pid",//关联名
targetKey:"pid"//自定义外键字段
});
//等待主键创建成功再创建子表的外键关系
setTimeout(() => {
test2.sync({
force: true
});
}, 2500);
复制代码
实际使用的时候我仍是倾向于这种关系。即表中关系已定的状况下仅仅指定外键关系。同步的时候仅仅同步表内容,不一样步这个外键关系。
真正的创建可使用手动建表的时候添加。或者也能够在自动建表结束后异步再执行一次外键关系的添加。
test1.hasMany(test2, {
foreignKey: "pid",
sourceKey: "pid",
constraints: false //不一样步创建外键关系
});
test2.belongsTo(test1, {
foreignKey: "pid",
targetKey:"pid",
constraints: false //不一样步创建外键关系
});
复制代码
实际的操做部分你们能够看github中的test.js。github地址
Sequelize在查询结果返回以后会返回一个它自定义的对象。这个对象是支持继续操做的,其中具体的值存放在datavalues中。不过能够放心的是在转化为字符串的时候是不会带有任何Sequelize的属性的。
//根据条件查询一条数据
let model = await test1.findOne({
where:{
id:5,
name:"test"
}
});
//修改其中的name字段的值
model.name="更新";
//保存,会自动update数据库中的值
model.save();
复制代码
正常的使用过程当中不多会说只须要查询一个表就能结果问题的。这里再说一下2个表查询的时候是怎么使用的。
这里的查询默认已经作好了外键的的关系。不过在使用的时候不作也是能够的,就是在查询的时候性能稍微很差而已。
//查询主表list的数据
//一条list中的数据对应多条item中的数据
let data = await models.List.findAll({
where:{id:5},//条件,这里jiashe只需查询一条
include: [{
model: models.Item,
as:"items",//返回的对象修改为一个固定的名称
}]
});
let list1=data[0];//返回的第一条数据就是要查询的数据
let list2=list1.items;//返回子表数据,items是自定义的名称
复制代码
上面的介绍已经解决了大多数状况下的查询等操做。并且我也相信,真的遇到了瓶颈,解决方案极可能也并非在Sequelize方面,或者说主要不是Sequelize的问题。好比大数据量的时候分表操做,就涉及到了更多的知识点。
nodejs在作后端方面还处于发展阶段。但愿有更多的前端可以接触并了解它。不只仅在开发过程当中对本身是一个加强,在长期的职业规划中也是一个很好的加强本身的武器。