我们日常使用的业务系统,核心都是围绕数据展开,基于数据变化出无穷的可能。本篇文章将基于《学生信息管理系统》这样浅显易懂的场景,介绍如何设计和创建模型,如何在多模型之间建立复杂的关联关系,以及如何在云开发平台中实际操作数据。
数据模型就是基于业务的深刻理解抽象出数据存储的框架,最终落实到实际使用中使数据的读写具有可靠性、扩展性和高效率,从而提升生产效率带来效益。
在传统业务应用开发过程中,首先最重要的是对数据库做好设计构建,其理论依据则是上世纪 70 年代提出的“数据库三范式”:
随着时代向前演进,为了满足日益复杂的业务系统要求,范式成员中又陆续新增了BCNF、4NF、5NF 等更多的范式,高阶范式一定满足低阶范式要求且范式设计越高阶,表的精细化越高,冗余度就越低。
既然数据库范式是减少冗余,那顾名思义,“反范式”就是增加冗余。事实上,在面对有些业务场景时,过于追求范式设计,会将拆分更多原子表,在数据整合时也会更多使用联表操作,联表本身就带来了复杂性和性能损耗,所以适当增加冗余反而更能高效率的完成查询任务,是一种“用空间换时间”的做法。
冗余,在提高查询性能的同时会增加数据写入的难度,通常需要双写或多写来保证冗余字段的一致性问题,所以开发者应精准识别业务中可提升性能、有价值的字段进行反范式设计。
综上所述,数据模型设计范式基本沿用关系型数据库范式:将表抽象为模型,将列抽象为字段,按照具体业务需求合理设置模型中的字段,系统已为每个模型固定内置了主键 “_id” 作为数据标识,开发者无需关心主键,只需要在模型中创建模型直接依赖的字段即可,适当增加冗余。
接下来,我们以《学生信息管理系统》为需求背景,从数据库E-R设计延伸出数据模型设计,直到生产中如何使用模型操作数据。
说明:以下截图均来自云后台数据管理界面,点击阅读原文登录
《学生信息管理系统》主要做学生相关数据管理,其中包含多对一、多对多和一对一关系,如下图所示:
基于业务需求,我们整理成表格信息,首先我们先依次创建出每个模型单体,不考虑关联关系。说明:
学生 | 班级 | 课程 | 学籍信息 |
---|---|---|---|
姓名 | 名称 | 编号 | 编号 |
年龄 | 年级 | 名称 | 学籍所在地 |
性别 | |||
所在班级 | 班内学生 | ||
所学课程 | 选课学生 | ||
学籍档案 | 绑定学生 |
创建模型时,统一使用云后台-数据管理-从空白创建-云开发MySQL数据库
如果mysql数据库未初始化,可点击下方的初始化按钮,再继续操作
如果没有性别枚举,可以在创建时新建选项集
接下来我们来为模型建立关联关系,在云开发数据管理中关联关系是成对出现的,例如在学生和班级关系中(多对一关系),班级是学生的父模型(一方),那么学生就是班级的子模型(多方)。当我们成功为学生模型创建多对一关系关联班级模型后,班级模型中就会出现一对多关系关联学生模型。此时关联关系字段会自动关联目标模型的数据标识,即主键。
说明:
将所有的模型都创建完毕,物理层的数据库存储也伴随模型而创建,接下来对数据进行操作。
3. 数据模型的物理意义
数据模型是业务需求的抽象,属于逻辑层含义,但实际进行数据存储和处理的还是物理层的数据库,为了更形象的解释其对应关系,我们采用关系型数据库 SQL 作为参照说明。
以下给出的 SQL 仅为了解释映射关系,而非实际存储
有同学肯定会问:既然模型字段和数据库列是一一对应的,为什么还需要数据模型,直接操作DB岂不更加直接明了?
以上,我们只是对模型和物理存储做浅尝即止的解释,为求在模型设计和数据操作时有更直观的理解,接下来我们利用创建好的模型做实机数据演示。
// 创建班级
const { data } = await models.class.createMany({
data: [
{
name: "1班",
grade: "一年级"
},
{
name: "2班",
grade: "一年级"
}
],
});
// 创建课程
const { data } = await models.course.createMany({
data: [
{
name: "语文",
code: "001"
},
{
name: "数学",
code: "002"
}
],
});
// 创建学籍信息
const { data } = await models.profile.createMany({
data: [
{
code: "01",
address: "北京朝阳区"
},
{
code: " 02",
address: "上海虹桥区"
}
],
});
// 创建学生
const { data } = await models.student.createMany({
data: [
{
"name": "小明",
"birth": 6,
"gender": "1",
// 建立学籍一对一关系,_id为学籍数据标识
"student_profile": {
"_id": "9ZRF3VHQR6"
},
// 建立班级多对一关系,_id为班级数据标识
"student_class": {
"_id": "9ZREB4QKDS"
},
// 建立课程多对多关系,_id为课程数据标识
"student_course": [
{
"_id": "9ZREE51ERE"
},
{
"_id": "9ZREDFX4G8"
}
]
},
],
});
我们创建好的两条学生数据如下:
姓名 | 年龄 | 性别 | 学籍档案 | 所在班级 | 所学课程 | 数据标识 |
---|---|---|---|---|---|---|
小明 | 6 | 男 | 02 | 2班 | 语文、数学 | 9ZREQJ0MPW |
小红 | 6 | 女 | 01 | 1班 | 语文、数学 | 9ZREUB0FJ0 |
用如下几个场景介绍关联关系的查询。
const { data } = await models.student.list({
// 展示参数,如果只展示当前模型非关联数据,可以使用 select:{$master:true}
select: {
_id: true,
name: true,
birth: true,
gender: true,
// 展示结果包含关联课程信息(多对多)
student_course: {
name: true,
code: true
},
// 展示结果包含班级信息(多对一)
student_class: {
name: true,
grade: true
},
// 展示结果包含学籍信息(一对一)
student_profile: {
code: true,
name: true
}
},
// 筛选条件,where指当前主模型条件;relateWhere指当前关联模型条件,其中relateWhere内的第一级参数为当前模型的关联字段,where内字段为关联模型字段
filter: {
// 条件:男生
where: {
$and: [
{
gender: {$eq: "1"}
}
]
},
// 条件:一年级 2 班
relateWhere: {
student_class:{ // 学生模型关联字段
where: {
$and:[
{
grade: {$eq: "一年级"} // 班级模型字段
},
{
name: {$eq: "2班"} // 班级模型字段
}
]
}
}
}
},
pageSize: 10, // 分页
pageNumber: 1, // 当前页数
getCount: true // 查询数据总数
}
});
// 返回查询到的数据列表 records 和 总数 total
console.log(JSON.stringify((data)));
// return
//{
// "records": [
// {
// "student_class": {"grade": "一年级","name": "2班","_id": "9ZREB4QKDS"},
// "gender": "1",
// "student_profile": {"code": " 02","_id": "9ZRF3VHQR6"},
// "name": "小明",
// "birth": 6,
// "_id": "9ZREQJ0MPW",
// "student_course": [
// {
// "code": "001",
// "name": "语文",
// "_id": "9ZREDFX4G8"
// },
// {
// "code": " 002",
// "name": "数学",
// "_id": "9ZREE51ERE"
// }
// ]
// }
// ],
// "total": 1
//}
查询所有学生信息,按姓名字典序排序
const { data } = await models.student.list({
select: {
$master:true // 展示学生模型所有非关联字段
},
filter: {
where: {}
},
orderBy:[
{
name: "DESC"
}
],
pageSize: 10,
pageNumber: 1,
getCount: true
});
本文分享自 腾讯云开发CloudBase 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!