经验分享 - Node ORM 框架 Sequelize 使用心得分享

发布于 2023-07-09 11:03:58 字数 11463 浏览 29 评论 0

一、数据库基础知识

1.1 什么是 ORM?

对象-关系映射(Object-Relational Mapping,简称 ORM),ORM 框架主要的作用就是把数据库中的关系数据映射称为程序中的对象,也就是说你操作数据库不用直接写 SQL,而是直接操作对象就可以。

Java 中主流的 ORM 框架有:Hibernate、Mybatis、iBatis 等。

Node 中主流的 ORM 框架有:Sequelize、TypeORM、LoopBack、Mongoose、Waterline 等。

1.2 关系数据库都有哪些关系?

两个对象 A 和 B 之间只有哪些可能存在的关系?

  1. 1:1 1 个 A 对应 1 个 B(一夫一妻)。有 2 张表,A 表中有一个外键关联 B 表;
  2. 1:N 1 个 A 对应多个 B(一夫多妻)。有 2 张表,B 表中有一个外键关联 A 表;
  3. M:N 1 个 A 对应多个 B,一个 B 也可能对应多个 A(多夫多妻)。有 3 张表,C 表中有 2 个外键,分别关联 A 表、B 表;

1.3 关系数据库都有哪些查询?

单表查询

select * from t1;

关联查询

/*
 * 1、内连接查询
 * inner join
 * 取 2 个表的交集
 */
select * from t1 
inner join t2
on t1.lid = t2.lid

/*
 * 2、左外连接查询
 * left outer join 或简写 left join
 * 以左表数据量为基准,右表未匹配的字段,用 NULL 代替
 */
select * from t1 
left join t2
on t1.lid = t2.lid

/*
 * 3、右外连接联查询
 * right outer join 或简写 right join
 * 已右表数据量为基准,左表未匹配的字段,用 NULL 代替
 */
select * from t1 
right join t2
on t1.lid = t2.lid

/*
 * 4、全外连接查询
 * full outer join 或简写 full join
 * 已右表数据量为基准,左表未匹配的字段,用 NULL 代替
 * 注意:Mysql 不支持,使用 uion 代替
 */
select * from t1 
right join t2
on t1.lid = t2.lid

/*
 * 5、交叉查询
 * cross join
 * N x M 的结果返回
 */
select * from t1 
cross join t2

结果集操作

/*
 * 1、合并
 * union
 * 对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序
 */
 select t1.id,t1.name from t1
 union
 select t2.id,t1.name from t2
 
 /*
 * 2、合并全部
 * union all
 * 对两个结果集进行并集操作,包括重复行,不进行排序
 */
 select t1.id,t1.name from t1
 union all
 select t2.id,t1.name from t2
 
 /*
 * 3、求交集
 * intersect
 * 对两个结果集进行交集操作,不包括重复行,同时进行默认规则的排序
 * 注意:Mysql 不支持
 */
 select t1.id,t1.name from t1
 intersect
 select t2.id,t1.name from t2
 
/*
 * 4、求差集
 * minus
 * 对两个结果集进行差操作,不包括重复行,同时进行默认规则的排序
 * 注意:Mysql 不支持
 */
 select t1.id,t1.name from t1
 minus
 select t2.id,t1.name from t2

二、Sequelize 使用心得

2.1 Sequelize 的 ORM 特性

  • Sequelize 可以通过 Model 创建数据库、表和字段(对象 -> 库)
  • Sequelize 也可以将数据库表通过 CLI 工具,导出生成 Model 对象 (库 -> 对象)

考虑到对代码的侵入性和以后的扩展性,我在使用 Sequelize 的时候,仅用了它的(库 -> 对象)这一层。

2.2 Sequelize 的高级特性

  • 支持分库分表、读写分离的配置
  • 支持事务、回滚机制
  • 支持字段校验,以及自己写校验规则
  • 支持各种钩子(Hooks),如: beforeCreate afterCreate 等,可以作为全局拦截处理
  • 支持作用域(Scopes),以规定全局默认的查询范围,如:所有接口默认携带当前 userId 作为查询参数
  • 支持各种关系查询,如: 1:11:N1:M

2.3 Sequelize 脚本片段

安装

// 使用 NPM
$ npm install --save sequelize

# 还有以下之一:
$ npm install --save pg pg-hstore
$ npm install --save mysql2
$ npm install --save sqlite3
$ npm install --save tedious // MSSQL

// 使用 Yarn
$ yarn add sequelize

# 还有以下之一:
$ yarn add pg pg-hstore
$ yarn add mysql2
$ yarn add sqlite3
$ yarn add tedious // MSSQL

建立连接

const sequelize = new Sequelize('database', 'username', 'password', {
  host: 'localhost',
  dialect: 'mysql',

  pool: {
    max: 5,
    min: 0,
    idle: 10000
  },
  define:{
    timestamps: false //数据库表中可以没有 createdAt、updatedAt 这 2 个字段。
  }
});

// 或者你可以简单地使用 uri 连接
const sequelize = new Sequelize('mysql://user:pass@example.com:5432/dbname');

测试连接

sequelize
  .authenticate()
  .then(() => {
    console.log('恭喜,数据库已连接!');
  })
  .catch(err => {
    console.error('糟糕,数据库连接失败!', err);
  });

Model 生成表

const User = sequelize.define('user', {
  firstName: {
    type: Sequelize.STRING
  },
  lastName: {
    type: Sequelize.STRING
  }
});

// force: true 如果表已经存在,将会丢弃表
User.sync({force: true}).then(() => {
  // 表已创建
});

表生成 Model

// 使用 NPM 安装 cli 工具
$ npm install --save mysql2
$ npm install --save sequelize-auto

//使用 cli 工具将表结构生成 Model 文件到指定目录下
$ sequelize-auto -h localhost -u username -x password -p 3306 -e mysql -d database -o "dirpath" -t "table" -C

基本的增删改查(CRUD)

//增
User.create({
    userName: '张三',
    userCreateTime : new Date()
}).then((user) => {
    console.log(user);
})

//删
User.destory({
    where : {
        userId : 1
    }
}).then((result) => {
    console.log(result);
})

//改
User.update({
    userName : '李四'
},{
    where : {
        userId : 1
    }
}).then((result) => {
    console.log(result);
})

//查全部
User.findAll().then((users) => {
  	console.log(users)
})

//查详情
let userId = 1;
User.findById(userId).then((user) => {
  	console.log(user)
})

//分页查询
User.findAndCountAll({
    limit : 10,
    offset : 0
}).then((result) => {
    //result 包含:count 和 rows
  	console.log(result)
})

//原始查询
sequelize.query("SELECT * FROM `users`", {type: sequelize.QueryTypes.SELECT})
	.then(function(users) {
    	console.log(users)
	})

where 条件

//条件 [Op.or]
User.findAll({
    where : {
        userId : 1,
        [Op.or] : [
            {userId: 12}, 
            {userId: 13}
        ]
    }
}).then((result) => {
  	console.log(result)
})

//简写 $or
User.findAll({
    where : {
        userId : 1,
        $or : {
            userName : {
                $like : '%张%'
            },
            userNickName : {
                $like : '%张%'
            }
        }
    }
}).then((result) => {
  	console.log(result)
})

更多 where 关键词

const Op = Sequelize.Op

[Op.and]: {a: 5}           // 且 (a = 5)
[Op.or]: [{a: 5}, {a: 6}]  // (a = 5 或 a = 6)
[Op.gt]: 6,                // id > 6
[Op.gte]: 6,               // id >= 6
[Op.lt]: 10,               // id < 10
[Op.lte]: 10,              // id <= 10
[Op.ne]: 20,               // id != 20
[Op.eq]: 3,                // = 3
[Op.not]: true,            // 不是 TRUE
[Op.between]: [6, 10],     // 在 6 和 10 之间
[Op.notBetween]: [11, 15], // 不在 11 和 15 之间
[Op.in]: [1, 2],           // 在 [1, 2] 之中
[Op.notIn]: [1, 2],        // 不在 [1, 2] 之中
[Op.like]: '%hat',         // 包含 '%hat'
[Op.notLike]: '%hat'       // 不包含 '%hat'
[Op.iLike]: '%hat'         // 包含 '%hat' (不区分大小写)  (仅限 PG)
[Op.notILike]: '%hat'      // 不包含 '%hat'  (仅限 PG)
[Op.regexp]: '^[h|a|t]'    // 匹配正则表达式/~ '^[h|a|t]' (仅限 MySQL/PG)
[Op.notRegexp]: '^[h|a|t]' // 不匹配正则表达式/!~ '^[h|a|t]' (仅限 MySQL/PG)
[Op.iRegexp]: '^[h|a|t]'    // ~* '^[h|a|t]' (仅限 PG)
[Op.notIRegexp]: '^[h|a|t]' // !~* '^[h|a|t]' (仅限 PG)
[Op.like]: { [Op.any]: ['cat', 'hat']} // 包含任何数组['cat', 'hat'] - 同样适用于 iLike 和 notLike
[Op.overlap]: [1, 2]       // && [1, 2] (PG 数组重叠运算符)
[Op.contains]: [1, 2]      // @> [1, 2] (PG 数组包含运算符)
[Op.contained]: [1, 2]     // <@ [1, 2] (PG 数组包含于运算符)
[Op.any]: [2,3]            // 任何数组[2, 3]::INTEGER (仅限 PG)

[Op.col]: 'user.organization_id' // = 'user'.'organization_id', 使用数据库语言特定的列标识符, 本例使用 PG

// 所有上述相等和不相等的操作符加上以下内容:
[Op.contains]: 2           // @> '2'::integer (PG range contains element operator)
[Op.contains]: [1, 2]      // @> [1, 2) (PG range contains range operator)
[Op.contained]: [1, 2]     // <@ [1, 2) (PG range is contained by operator)
[Op.overlap]: [1, 2]       // && [1, 2) (PG range overlap (have points in common) operator)
[Op.adjacent]: [1, 2]      // -|- [1, 2) (PG range is adjacent to operator)
[Op.strictLeft]: [1, 2]    // << [1, 2) (PG range strictly left of operator)
[Op.strictRight]: [1, 2]   // >> [1, 2) (PG range strictly right of operator)
[Op.noExtendRight]: [1, 2] // &< [1, 2) (PG range does not extend to the right of operator)
[Op.noExtendLeft]: [1, 2]  // &> [1, 2) (PG range does not extend to the left of operator)

order 排序

//按 userUpdateTime 和 userCreateTime 倒序
User.findAll({
    order : [['userUpdateTime','DESC'],['userCreateTime','DESC']]
}).then((result) => {
  	console.log(result)
})

事务

什么时候用到事务?比如:你删除一个 Link,连同 Link 的所有记录一起删掉。删除 Link 和删除 LinkLog,这 2 步要不同时成功,要不同时失败,不存在一个删除,一个保留的情况,这时候就得用到事务。

/**
 * 不使用事务的写法
 * 这种写法的问题:可能删除了 Link,但是由于数据库异常,导致 LinkLog 删除失败。
 */

//1. 先删除 Link
Link.destory({
    where : {
        linkId : linkId
    }
}).then(() => {
    //2. 再删除 LinkLog
    LinkLog.destory({
        where : {
            linkLogLinkId : linkId
        }
    }).then((result) => {
        console.log(result);
    })
})

/**
 * 使用事务包裹
 */
sequelize.transaction((t) => {
    //1. 先删除 Link
    Link.destory({
        where : {
            linkId : linkId
        },
        transaction: t //关联事务
    }).then(() => {
        //2. 再删除 LinkLog
        LinkLog.destory({
            where : {
                linkLogLinkId : linkId
            },
            transaction: t //关联事务
        }).then((result) => {
            console.log(result);
        })
    })
});

关联关系 belongsTo 1:1

//belongsTo 1:1
//比如:1 个链接对应唯一的 1 个用户
//在 Link 这一侧建立关系

//1. 需要在 links 表中拥有字段 link_user_id,作为外键关联 users 表
//2. 在 Link 模型中建立关系
Link.belongsTo(user,{
    as : 'linkUser',
    foreignKey: 'linkUserId',
    targetKey:'userId'
});

//3. 关联查询当前链接对应的用户信息 linkUser
Link.findById(linkId,{
    include:[{
        model: user,
        as : 'linkUser', //与上面关系中的 as 名称相同
        required: false, //使用 inner 关联,还是 outer 关联
        where : {//可以添加关联条件
            userStatus : 0
        },
        attributes: ['userId','userName']//关联的对象只会查询这 2 个字段
    }]
})

//4.返回结果
{
    linkId : 12,
    linkName : '测试链接',
    linkCreateTime : '2018-11-04 11:22:55',
    linkUser : {
		userId : 100,
        userName : '张三'
    }
}

关联关系 hasMany 1:N

//hasMany 1:N
//比如:1 个链接对应多个链接记录
//在 Link 这一侧建立关系

//1. 需要在 link_logs 表中拥有字段 link_log_link_id,作为外键关联 links 表
//2. 在 Link 模型中建立关系
Link.hasMany(linkLog,{
    as : 'linkLogList',
    foreignKey: 'linkLogLinkId'
});

//3. 关联查询当前链接对应的所有记录数据 linkLogList
Link.findById(linkId,{
    include:[{
        model: linkLog,
        as : 'linkLogList',
        required: false
    }]
})

//4.返回结果
{
    linkId : 12,
    linkName : '测试链接',
    linkCreateTime : '2018-11-04 11:22:55',
    linkLogList : [{
		linkLogId : 1,
        linkLogName : '记录 1',
        ...//其它字段
    }]
}

关联关系 belongsToMany M:N

//belongsToMany M:N
//比如:1 个链接对应多个链接规则,1 个链接规则对应多个链接
//在 Link 这一侧建立关系

//1. 需要有 3 张表,links、rules 和 link_rule_refs,在 link_rule_refs 中需要有 2 个字段,1 个字段 link_id 作为外键和 links 表关联,另 1 个字段 rule_id 作为外键和 rules 表关联

//2. 在 Link 模型中建立关系
Link.belongsToMany(rule,{
    as : 'linkRuleList',
    through: ruleLink,//ruleLink 就是第 3 张表的 Model
    foreignKey: 'linkId',
    otherKey: 'ruleId'
});

//3. 关联查询当前链接对应的所有记录数据 linkRuleList
Link.findById(linkId,{
    include:[{
        model: this.rule,
        as : 'linkRuleList',
        required: false,
        attributes: ['ruleId','ruleName'],//rule 表的字段
    	through: {//关联表
        	attributes: []//可以指定关联表中的返回字段
        }
    }]
})

//4.返回结果
{
    linkId : 12,
    linkName : '测试链接',
    linkCreateTime : '2018-11-04 11:22:55',
    linkLogList : [{
		linkLogId : 1,
        linkLogName : '记录 1',
        ...//其它字段
    }]
}

三、文档相关

3.1 API 相关文档

  1. Sequelize 官网英文文档
  2. Sequelize 中文文档 v4

3.2 Model 所有查询 API

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

0 文章
0 评论
23 人气
更多

推荐作者

13886483628

文章 0 评论 0

流年已逝

文章 0 评论 0

℡寂寞咖啡

文章 0 评论 0

笑看君怀她人

文章 0 评论 0

wkeithbarry

文章 0 评论 0

素手挽清风

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文