如果不想在nodejs中大量的手写sql,就可以采用orm框架sequelize,它非常类似于Java的JPA,让你的代码看起来更规范简洁高效。不过在采用sequelize编写的过程中遇到了一些问题,而网上的资料又太少;不是没有去sequelize官网看过,主要是官网的API示例都是针对一般的js语法,而我的项目使用了TypeScript,有好多地方功能虽然可以正常执行,但是代码下面会冒红(红颜色波浪线),鼠标放上去提示:TS2322:the type xxx is not assign to xxxxx。这种属于语法问题,typescript要求的比较严格。
下面来看一个例子,API为findOrCreate,意思是根据条件查找,如果找到就返回,没找到则创建一条。在对实体类进行jpa操作时,首先用sequelize定义字段类型与数据库字段的映射,例如:
export const StudentModel = sequelize.define('student', {
id: {
type: Sequelize.BIGINT,
field: 'id',
primaryKey: true,
autoIncrement: true
},
name: {
type: Sequelize.STRING,
field: 'name'
},
age: {
type: Sequelize.TINYINT,
field: 'age'
},
tel: {
type: Sequelize.STRING,
field: 'tel'
},
createTime: {
type: Sequelize.BIGINT,
field: 'create_time'
},
updateTime: {
type: Sequelize.BIGINT,
field: 'update_time'
}
}, {
timestamps: false,
tableName: 'student'
});
按照官网 findOrCreate的用法,根据where条件后面的选项来查找记录,如果没找到,则将where里面的条件和defaults里面的属性一起作为待插入项,所以我只需要像如下这么写:
await StudentModel.findOrCreate(
{
where: {name: `${name}`, tel: `${tel}`},
defaults: {
age: `${age}`,
createTime: `${time}`,
updateTime: `${time}`}
});
事实是运行功能执行正常,但是由于采用了TypeScript来编写,“defaults”那里此时会冒红,而且持续集成的编译不会通过,鼠标放在defaults上会提示TS2322: type 'xxxx' is not assignable to type 'xxxx'。意思是类型对应不上,要求你defaults里面的参数必须和StudentModel里面定义的完全一致,少一个都不行,我不知道为什么会这样,难道sequelize没有对typescript做优化吗?defaults里面如果把所有参数都填上,怪怪的,我id是自增的,肯定不能给id默认值,所以赋给null,where条件里面已经填了两个参数还要再填一遍,没办法先编译通过再说,改正后如下:
await StudentModel.findOrCreate(
{
where: {name: `${name}`, tel: `${tel}`},
defaults: {id:null,
name: `${name}`,
tel: `${tel}`,
age: `${age}`,
createTime: `${time}`,
updateTime: `${time}`}
});
再来说第二个问题:sequelize的api有一个回调方法,有的是then,有的是spread,这里根据官网文档使用spread将结果切分为俩个部分,一个是返回的实例对象,一个是行为布尔值,我是这么理解的。
await StudentModel.findOrCreate(
{
where: {name: `${name}`, tel: `${tel}`},
defaults: {id:null,
name: `${name}`,
tel: `${tel}`,
age: 21,
createTime: `${time}`,
updateTime: `${time}`}
})
.spread((student, created) => {
// 如果找到则直接更新,没找到会自动创建
if (created === false) {
(async () => {
await StudentModel.update(
{age: 22, updateTime: `${time}`},
{where: {id: student.id}});
})();
}
});
此处不管是找到也好,创建的也好,spread第一个参数返回的都是student对象,然后紧接着我会用到student对象的属性id值,本地执行也是正常的,但就是在student.id那里冒红 ,也是报TS2322啥的,意思是说student是object类型的无法点出id属性。后来问了web前端工程师,他说要对spread的第一个参数student加个类型,要加类型就要先定义类型,这样写:
interface Student {
id:number;
name?:string;
...
...
}
定义好以后 ,对spread的第一个参数student加个类型,如下所示
.spread((student: Student, created) => {
当然了,如果你嫌麻烦,也可以直接将其定义为any类型,对象依然是原来的那个对象,只是让typescript不再报语法错误
.spread((student: any, created) => {
这次student.id不再冒红了。我说我已经定义了一个StudentModel,这里的类型换成StudentModel怎么也冒红,他说typescript里面有专门定义自己属性的接口,不能混淆。
其实sequelize还有一个叫upsert的API,是updateOrInsert的简称,where条件里面必须包含能定为一条记录的唯一索引,否则无效,就拿上面的例子来说,如果你能获取到id值,则上面的写法可以换成upsert一个api就够了,很精简。
最后如果使用了sequelize后,启动时控制台报这个错
sequelize deprecated String based operators are now deprecated. Please use Symbol based operators for better security, read more at
此时可以这么做,修改你的sequelize.ts文件,重点在于operatorsAliases这一大堆:
const Op = Sequelize.Op;
export const sequelize = new Sequelize(dbConfig.database, dbConfig.user, dbConfig.password, {
host: dbConfig.host,
port: dbConfig.port,
dialect: 'mysql',
operatorsAliases: {
$eq: Op.eq,
$ne: Op.ne,
$gte: Op.gte,
$gt: Op.gt,
$lte: Op.lte,
$lt: Op.lt,
$not: Op.not,
$in: Op.in,
$notIn: Op.notIn,
$is: Op.is,
$like: Op.like,
$notLike: Op.notLike,
$iLike: Op.iLike,
$notILike: Op.notILike,
$regexp: Op.regexp,
$notRegexp: Op.notRegexp,
$iRegexp: Op.iRegexp,
$notIRegexp: Op.notIRegexp,
$between: Op.between,
$notBetween: Op.notBetween,
$overlap: Op.overlap,
$contains: Op.contains,
$contained: Op.contained,
$adjacent: Op.adjacent,
$strictLeft: Op.strictLeft,
$strictRight: Op.strictRight,
$noExtendRight: Op.noExtendRight,
$noExtendLeft: Op.noExtendLeft,
$and: Op.and,
$or: Op.or,
$any: Op.any,
$all: Op.all,
$values: Op.values,
$col: Op.col
},
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
},
});
参考https://stackoverflow.com/questions/46608382/sequelize-deprecated-error-message