项目介绍
各位同学,大家好,今天开始,我们就正式开始项目阶段的学习之旅了哦,咱们要一起完成一个基于若依框架+AI的养老项目。
在项目开始前,咱们先来聊一聊为什么选择做养老项目。
行业背景
中国老龄化程度加深,我国老龄事业和养老服务体系的发展得到了国家的高度重视,在国家政策的支持下,我国智慧养老产业主体持续增多,产业链不断整合,发展前景较好。我国正在形成一个多元化“互联网+养老”的智慧老年护理服务系统,智慧养老是我国的必然趋势
市场规模及预测
2023年中国养老产业市场规模达到12万亿元,同比增长16.8%。
预计2023-2027年中国养老产业迎来较快速增长。预计2027年中国养老产业市场规模达21.1万亿元
整体业务流程
中州养老系统是为养老院量身定制开发的专业的养老管理软件产品;涵盖来访管理、入退管理、在住管理、服务管理、财务管理等功能模块,涉及从来访参观到退住办理的完整流程。
项目原型访问地址:https://codesign.qq.com/s/459277624064324 密码: FSTI
中州养老项目分为两端,一个是管理后台,另外一个是家属端
管理后台:养老院员工使用,入住、退住,给老人服务记录等等
家属端:养老院的老人家属使用,查看老人信息,缴费,下订单等等
业务主页效果:
教育公司无养老相关资质,小程序无法正式上架,无法体验。在课程的对应阶段,我们会进行调试开发
技术架构
下图展现了中州养老项目主要使用的技术:
前端主要使用的Vue3+Element Plus
后端主要使用的是若依框架作为基础架构,当然后端也集成了很多其他的技术,比如有Springboot、Mybatis、Swagger、Spring cache、Spring Security、Mybatis-plus等
数据存储主要使用到了MySQL和Redis
使用了nginx来作为反向代理和前端的静态服务器
其他技术:阿里云物联网平台IOT、对象存储OSS、微信登录、千帆大模型、AI工具辅助开发等
课程安排
中州养老课程共17天,具体安排如下:
day01
项目简介,Mybatis-Plus框架
day02
版本控制工具Git、Git常用命令、版本冲突、IDE中集成Git
day03
认识若依框架,环境搭建、AI协助快速熟悉项目、通义灵码代码生成、模块开发-代码生成
day04
使用AI改造若依框架前后端代码和代码生成模板
day05
数据字典、集成OSS,入住办理-模块后端设计,通义灵码协助接口代码开发
day06
入住办理-基于动态表单快速构建表单项,通义灵码协助完成养老项目中的入住办理功能
day07
性能优化、Redis基础、常见的数据类型和命令,Java中操作Redis
day08
健康评估-大模型技术调研、对接千帆大模型,设计Prompt、大模型智能评估体检报告
day09
AI协助项目阶段性部署,代码质量检查、开发模式、jenkins、docker、日志管理(ELK)、禅道
day10
后台登录和鉴权、spring security入门、核心配置、小程序家属端,微信登录、定时任务
day11-day12
IOT产品设备管理,账号开通、设备管理、模拟上报、设备数据消费
day13
智能床位模块开发、家属端报表展示,AI协助编写sql以及优化、MySQL索引
day14
报警规则开发、报警数据过滤
day15
报警提醒(websocket)、项目总结
day15-17分组实战
后台:客户管理、来访管理、合同管理;家属端:绑定老人、老人列表;项目部署
聊完了课程安排后,接下来一起来看一下今天的课程安排:
在前面的Web开发课程中,咱们学习了持久层框架MyBatis,而目前很多企业也会采用能大大提高开发效率的持久层框架:MyBatisPlus。因此,咱们的项目会在若依的基础上集成更快捷的持久层框架MybatisPlus(简称:MP)。
今天的目标:
能够掌握MybatisPlus实现基本的增删改查
能够掌握MybatisPlus条件构建器来查询或更新的操作
能够掌握MybatisPlus分页插件逻辑的处理方式
掌握了MyBatisPlus的基本使用后,咱们会把它集成到项目中。
Mybatis-Plus
概述
Mybatis-Plus(简称MP)是一个基于Mybatis框架的增强工具,它在Mybatis的基础上只做增强而不做改变,旨在简化开发、提高效率。Mybatis-Plus提供了一系列的功能和特性,使得开发人员能够更加高效地使用Mybatis进行数据库操作。
官网地址:https://mybatis.plus/
愿景
我们的愿景是成为 MyBatis 最好的搭档,就像魂斗罗 中的 1P、2P,基友搭配,效率翻倍。
快速入门
环境准备
数据库的准备,基于我们熟悉的Tlias智能学习辅助系统中的部门管理和员工管理页面来完成开发
导入资料中提供的tlias初始项目,使用idea打开即可,项目的结构如下:
注意:检查项目结构jdk为17,检查maven配置和设置jdk17
在application.yml中修改jdbc参数为你自己的数据库参数
环境准备好之后,可以运行启动类,然后打开资料中提供的前端项目来验证部门管理页面是否能正常操作数据库,其他页面只提供了基础的Controller类,没有Service层业务逻辑,所以都是没有数据的
需求描述
基于MyBatisPlus,实现Tlias智能学习辅助系统部门管理页面的所有功能:
①新增部门功能
②根据id查询部门
③根据id更新部门
④根据id删除部门
导入依赖
tlias-pojo中pom.xml导入Mybatis-Plus的起步依赖,替换掉MyBatis的起步依赖, 注意刷新maven
tlias-web-management 注释依赖
将如下分页代码注释
ClazzServiceImpl 代码
EmpServiceImpl 代码
StudentServiceImpl 代码
接着,在引导类上添加注解,配置自动扫描Mapper:
定义mapper
为了简化单表CRUD,MybatisPlus提供了一个基础的BaseMapper接口,其中已经实现了单表的CRUD:
因此我们自定义的Mapper只要实现了这个BaseMapper,就无需自己实现单表CRUD了。
修改DeptMapper接口,让其继承BaseMapper,接口中的代码可以全部注释掉或者删除:
同时可以把原有的基础代码给注释掉或者删除掉,把对应的xml映射文件改个名字或删除(彻底失效)
修改Service层实现类:DeptServiceImpl,内容如下:
tlias-pojo中Dept代码修改如下
以上的所有操作数据库的方法,都是继承了BaseMapper之后提供好的方法,可以正常操作数据库
修改完成后,启动后端程序和nginx程序,打开前端页面,在部门管理页面执行增删改查,是不是都是可以正常使用呀,如果是的话,那就恭喜你啦,成功入门MyBatisPlus啦!
常见注解
在刚刚的入门案例中,我们仅仅引入了依赖,继承了BaseMapper就能使用MybatisPlus,非常简单。但是问题来了: MybatisPlus如何知道我们要查询的是哪张表?表中有哪些字段呢?
大家回忆一下,DeptMapper在继承BaseMapper的时候是不是指定了一个泛型呀:
泛型中的Dept就是与数据库对应的pojo
MybatisPlus就是根据pojo实体的信息通过反射来推断出表的信息,从而生成SQL的。默认情况下:
MybatisPlus会把pojo实体的类名驼峰转下划线作为表名
MybatisPlus会把名为id的字段作为主键
MybatisPlus会把pojo实体的所有变量名驼峰转下划线作为表的字段名,并根据变量类型推断字段类型
但很多情况下,默认的实现与实际场景不符,因此MybatisPlus提供了一些注解便于我们声明表信息。
@TableName
描述:表名注解,标识实体类对应的表
使用位置:实体类
示例:
@TableId
描述:主键注解,标识实体类中的主键字段
使用位置:实体类的主键字段
示例
注解支持两个属性:
IdType支持的类型有:
这里比较常见的有三种:
AUTO:利用数据库的id自增长
INPUT:手动生成id
ASSIGN_ID:雪花算法生成Long类型的全局唯一id(数据库对应的数据类型bigint,生成的位数至少19位数字),这是默认的ID策略
@TableField
普通字段注解
示例
一般情况下我们并不需要给字段添加@TableField注解,一些特殊情况除外:
成员变量名与数据库字段名不一致
成员变量是以isXXX命名,按照JavaBean的规范,MybatisPlus识别字段时会把is去除,这就导致与数据库不符。
成员变量名与数据库一致,但是与数据库的关键字冲突。使用@TableField注解给字段名添加转义字符:``
成员变量不是数据库中的字段,则需要使用exist表明为false
案例 Dept类操作
数据库表(注意修改了表名和字段名)
Dept类 代码
测试类
常见配置
MybatisPlus也支持基于yml文件的自定义配置,详见官方文档:https://www.baomidou.com/reference/
大多数的配置都有默认值,因此我们都无需配置。但还有一些是没有默认值的,例如:
全局id类型
需要注意的是,MyBatisPlus也支持手写SQL的,而mapper文件的读取地址可以自己配置:
type-aliases-package:实体类的别名扫描包
mapper-locations:xml映射文件目录
所以所有XXXMapper.xml文件移动到resources/mapper目录下
也可以配置成classpath:mapper/*.xml,也就是说我们只要把mapper.xml文件放置这个目录下就一定会被加载。
例如,我们开发DeptMapper.xml文件中的sql片段:
同时,放开DeptMapper.java中的update方法:
最后,修改DeptServiceImpl中的update方法:
重启后端程序,测试更新,依然正常可用,说明此时咱们放在mapper目录下的xml映射文件生效了。
核心功能
刚才的案例中都是以id为条件的简单CRUD,一些复杂条件的SQL语句就要用到一些更高级的功能了,所以,接下来咱们开始学习MyBatisPlus中的核心功能,包括3个小节:
条件构造器
自定义SQL
Service接口
条件构建器
除了新增以外,查询、修改、删除的SQL语句都需要指定where条件。因此BaseMapper中提供的相关方法除了以id作为where条件以外,还支持更加复杂的where条件。
参数中的Wrapper就是条件构造的抽象类,其下有很多默认实现,继承关系如图:
Wrapper的子类AbstractWrapper提供了where中包含的所有条件构造方法:
而QueryWrapper在AbstractWrapper的基础上拓展了一个select方法,允许指定查询字段:
而UpdateWrapper在AbstractWrapper的基础上拓展了一个set方法,允许指定SQL中的SET部分:
接下来,我们就来看看如何利用Wrapper实现复杂查询。
QueryWrapper
无论是修改、删除、查询,都可以使用QueryWrapper来构建查询条件。接下来看一些例子: 查询:查询姓名中包含“李”且薪资大于等于5000的员工的 id, name, phone, salary字段。代码如下:
首先让EmpMapper继承自BaseMapper:
注释如下代码
EmpMapper.xml代码
EmpServiceImpl代码修改
Emp实体类代码修改
为了方便测试,咱们使用单元测试学习QueryWrapper的使用:
更新:更新名为"李忠"的员工的薪水为18000,代码如下:
UpdateWrapper
更新:更新id为5, 6, 7的员工的薪水,加2000,代码如下:
update emp set salary = salary + 2000 where id in (5, 6, 7);
LambdaQueryWrapper
无论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称,这在编程规范中显然是不推荐的。 那怎么样才能不写字段名,又能知道字段名呢?
其中一种办法是基于变量的gettter方法结合反射技术来实现,因此我们只要将条件对应的字段的getter方法传递给MybatisPlus,它就能计算出对应的变量名了。而传递方法可以使用JDK8中的方法引用和Lambda表达式。 因此MybatisPlus又提供了一套基于Lambda的Wrapper,包含两个:
LambdaQueryWrapper
LambdaUpdateWrapper
分别对应QueryWrapper和UpdateWrapper
其使用方式如下:
自定义SQL
在上面的例子中,咱们利用Wrapper对象构建了复杂的查询SQL,其实,MyBatisPlus的Wrapper还有一种用法:利用MyBatisPlus的Wrapper来构建复杂的Where条件,然后自己定义SQL语句中剩下的部分。
什么意思呢?来看一个需求:
将id在指定范围的员工(例如5、6、7 )的薪水增加指定值
如果用MyBatis来实现,咱们可以在Mapper.xml文件中编写下面的SQL:
这段SQL片段的Where条件很容易通过Wrapper对象构建出来,而上方的Set条件是不太容易通过Wrapper对象构建的,不过咱们可以这样写:
在UpdateWrapper的setSql方法中可以编写set子句的代码片段,看似没问题,大家再想一下,将来调用Mapper层方法的代码是不是要写在Service层呀,那这样的话咱们就将SQL写在Service中了,这不符合开发规范,也不太好维护,那怎么办呢?答案就是使用自定义SQL:
使用自定义SQL,咱们就可以利用MyBatisPlus的Wrapper来构建复杂的Where条件,然后自己定义SQL语句中剩下的部分,一共分三步:
基于Wrapper构建where条件
在mapper方法参数中用Param注解声明wrapper变量名称,必须是ew
自定义SQL,并使用Wrapper条件
注意:自定义SQL是在Mapper.xml文件中定义的哦
IService接口
MybatisPlus不仅提供了BaseMapper,还提供了通用的Service接口及默认实现,封装了一些常用的service模板方法。 通用接口为IService,默认实现为ServiceImpl,其中封装的方法可以分为以下几类:
save:新增
remove:删除
update:更新
get:查询单个结果
list:查询集合结果
count:计数
page:分页查询
基本的增删改查
我们先来看下基本的CRUD接口,新增
save是新增单个元素
saveBatch是批量新增
saveOrUpdate是根据id判断,如果数据存在就更新,不存在则新增
saveOrUpdateBatch是批量的新增或修改
删除:
removeById:根据id删除
removeByIds:根据id批量删除
removeByMap:根据Map中的键值对为条件删除
remove(Wrapper):根据Wrapper条件删除removeBatchByIds:暂不支持
修改:
updateById:根据id修改
update(Wrapper):根据UpdateWrapper修改,Wrapper中包含set和where部分
update(T,Wrapper):按照T内的数据修改与Wrapper匹配到的数据
updateBatchById:根据id批量修改
Get:
getById:根据id查询1条数据
getOne(Wrapper):根据Wrapper查询1条数据
getBaseMapper:获取Service内的BaseMapper实现,某些时候需要直接调用Mapper内的自定义SQL时可以用这个方法获取到Mapper
List:
listByIds:根据id批量查询
list(Wrapper):根据Wrapper条件查询多条数据
list():查询所有
Count:
count():统计所有数量
count(Wrapper):统计符合Wrapper条件的数据数量
基本用法与快速入门
由于Service中经常需要定义与业务有关的自定义方法,因此我们不能直接使用IService,而是自定义Service接口,然后继承IService以拓展方法。同时,让自定义的Service实现类继承ServiceImpl,这样就不用自己实现IService中的接口了。
怎么样?听上去是不是很厉害?那接下来咱们就通过一个入门程序来感受一下是不是如此吧。
需求:基于MyBatisPlus的 Iservice 接口,实现Tlias智能学习辅助系统部门管理页面的所有功能:
新增部门功能
根据id查询部门
根据id更新部门
根据id删除部门
首先,找到DeptService接口,让它继承IService接口,并在泛型上指定实体类的类型:
里面原来书写的方法都可以全部删除啦。
接着,在找到DeptService接口的实现类:DeptServiceImpl,让它继承自ServiceImpl类,在泛型的位置指定Mapper层接口和实体类:
最后,修改DeptController中报错的代码,改一下方法调用就可以啦。
这样,就搞定了哦,接下来,重启程序,打开页面测试一下部门管理的增删改查是不是都还完全正确吧~
到这里,咱们就搞定了MyBatisPlus的基本使用啦,接下来,咱们一起通过员工管理页面来实战一下,在实战中也会引入一些新的技巧哦。
Tlias员工管理页面MyBatisPlus实战
分页查询
员工管理列表页是条件分页查询,先来完成基础的分页查询功能,MyBatisPlus提供了一个分页插件,只有引入这个分页插件,才能支持分页查询。
第一步:引入依赖
在项目中新建一个配置类:com.itheima.config.MybatisConfig
环境准备
EmpService 代码
EmpServiceImpl 原始代码
接着,就可以使用分页的API了:
编写代码,完成分页,这一步先不考虑条件查询哦:
对啦,友情提醒,千万不要忘记EmpService和EmpServiceImpl上要做的操作呢!
还有,要记得,分页查询要根据最后修改时间字段倒序排序哦;
EmpServiceImpl代码如下:
接下来,解决所属部门没有数据的问题
部门名称需要关联部门表查询,而MyBatisPlus是基于单表操作的,怎么办呢?
将多表操作转换成多次单表操作,查询出当前页面所有员工的部门数据,在内存中将部门名称关联到员工对象中。
EmpServiceImpl中修改后的代码如下:
最终页面上的效果如下:
条件分页查询
怎么样,在MyBatisPlus中分页查询是不是很简单?那接下来,再来感受一下条件查询的强大之处吧。
IService接口中的page方法除了能接收一个Page对象作为参数外,还支持传入一个Wrapper对象,同时封装查询条件:
接下来,就一起来改造分页查询的方法,添加查询条件吧
注意这里加上了条件查询,所以要加一个判断,排除掉查询到的员工列表为空的情况,否则程序运行到这里的第30行会报错呢。
到这里,就实现了员工管理的分页查询,不过咱们还可以继续改造它,上面代码片段中绿色背景的代码还要自己new一个Wrapper对象,并将其作为参数传入到page方法内部,还是有点麻烦,这里可以借助于IService中的另外一个方法:lambdaQuery()进一步简化,最终的代码如下:
新增员工
由于初始项目中去除了阿里云OSS相关的内容,因此,本次新增员工不考虑头像上传,只处理员工基本信息和工作经历信息。
先将Emp中的工作经历列表字段的注释取消,并标记字段不在数据库中。
在EmpController中调用Service层方法,将数据传给Service层处理,代码如下:
在EmpService中添加Controller层调用的方法:
在EmpServiceImpl中实现该方法:
由于这里涉及到了批量保存员工的工作经历,所以,需要在程序中添加EmpExprService和其实现类ServiceImpl:
EmpExprService:
EmpExprServiceImpl:
修改员工
修改员工涉及到2个操作:根据id查询员工用于页面回显和根据id修改员工信息。
先来完成第1步:根据id查询员工。
这里要注意的是,查询回显时不仅仅要查询到员工的基本信息,还要查询到员工的工作经历信息,在查询员工基本信息的时候还需要针对部门名称做单独的处理。
在Controller层调用Service层方法获取员工数据:
在EmpService中添加以下方法:
在EmpServiceImpl中实现该方法:
接下来,完成点击保存按钮将修改更新到数据库。
这里,又可以拆分为3个小的操作:
根据id更新员工的基本信息
根据id批量删除员工的工作经历信息
批量保存员工的工作经历信息
代码如下:
在Controller层调用Service层方法将数据传给Service层处理:
在EmpService中添加以下方法:
在EmpServiceImpl中实现该方法:
小伙伴还记得Web阶段根据id更新员工的Mapper层xml文件是怎么写的吗?是不是下面这样呀?
这里面做了很多的条件判断,那刚刚咱们在EmpServiceImpl的updateEmp方法中调用的updateById()方法能不能实现条件更新呢?来做一个测试:
在APIFox中发起一个请求,修改id等于1的员工数据,json中只保留id、username、phone三个字段:
发起请求后,重点查看控制台打印的SQL:
从执行的SQL中能看出来,MyBatisPlus自动执行了条件更新,值为null的字段并没有出现在set关键字的后方。
批量删除员工
员工管理页面上每条记录后面的删除按钮和批量删除按钮共用一个方法即可,方法代码如下:
在EmpService中添加一个方法:
在EmpServiceImpl中重写Service层新添加的方法来执行批量删除操作:
今日作业
1.把上课完成的部门管理页面的增删改查全部实现(必做)
2.参考02-代码版本控制Git安装Git,同时注册一个Gitee账号: https://gitee.com/ (必做)
3.基于MyBatisPlus完成班级管理页面和学员管理页面的增删改查(选做)
附录:雪花算法
雪花算法(Snowflake)是由 Twitter 公司开发的一种分布式 ID 生成算法,以下是关于它的详细介绍:
基本原理
雪花算法生成的 ID 是一个 64 位的二进制数字,用长整型(long)来表示,其结构如下:
符号位:最高位是符号位,始终为 0,用于标识正数。
时间戳部分:接下来的 41 位是时间戳,记录了 ID 生成的时间。这部分可以精确到毫秒级,能表示的时间范围约为 69 年,从某个固定的起始时间开始计算。
工作机器 ID 部分:再往后的 10 位用于表示工作机器 ID,其中 5 位用于表示数据中心 ID,5 位用于表示机器 ID。通过这 10 位,可以在分布式系统中区分不同的机器,最多可以支持 1024 台机器。
序列号部分:最后 12 位是序列号,用于在同一毫秒内对来自同一台机器的不同 ID 进行编号。同一毫秒内最多可以生成 4096 个不同的 ID。
优点
全局唯一性:在分布式系统中,无论有多少台机器同时生成 ID,雪花算法都能保证生成的 ID 是全局唯一的。
高性能:雪花算法的计算过程相对简单,不需要进行复杂的数据库查询或分布式协调操作,因此可以在短时间内生成大量的 ID,具有很高的性能。
趋势递增性:生成的 ID 是按照时间顺序递增的,在一定程度上可以满足对数据进行排序和分桶等操作的需求。
可移植性:雪花算法的实现相对简单,不依赖于特定的数据库或中间件,因此可以很容易地在不同的编程语言和系统中进行移植和使用。
缺点
时间回拨问题:如果机器的时间出现回拨,比如由于时钟同步问题或系统故障导致时间倒退,可能会生成重复的 ID。
对时钟的依赖:ID 的生成依赖于系统时钟的准确性,如果系统时钟不准确或不稳定,可能会影响 ID 的生成质量和唯一性。
分布式环境下的时钟同步:在分布式环境中,需要确保各个节点的时钟保持相对一致,否则可能会导致 ID 生成出现问题。
应用场景
数据库主键:在分布式数据库中,作为表的主键可以保证数据的唯一性和有序性,便于数据的存储和查询。
分布式系统中的消息 ID:在消息队列、分布式日志等系统中,为每条消息生成唯一的 ID,方便对消息进行追踪、排序和处理。
订单系统:为每个订单生成唯一的 ID,方便订单的管理、查询和跟踪,同时可以根据 ID 的时间顺序对订单进行排序和统计。