在线教育项目开发介绍和MyBatisPlus技术
项目介绍&Mybatis_plus
课程目标
1)在线教育行业介绍
2)商业模式
3)优学在线项目简介
4)环境搭建【重点】
5)Mybatis-plus使用【重点】
6)实现讲师列表查询【重点】
1、 行业分类
1.1 母婴
市场现状:
尽管母婴在线教育市场已经发展多年,但行业总体仍处于赚吆喝不赚钱的状态,主要原因在于国内垂直母婴网站大多存在同质化竞争激烈和盈利模式单一问题,从在线教育的内容来看,大部分母婴网站功能类似大多是基础的母婴知识库问题,咨询交流社区的内容,特色区分并不明显。
未来发展:
中国母婴产业是朝阳产业,现在处于快速发展的初期,国内每年将近有2000万新生儿,0到36个月的婴幼儿超过6000万,加上还有2000万左右的孕妈妈,市场规模已经超过万亿,所以发展母婴及儿童产品的在线教育市场,前景非常广阔。
代表网站:
妈妈网 https://www.mama.cn/
类似社交网站,视频频道跳转到腾讯视频
1.2 学前教育
市场状况:
大多数企业缺乏有效的产品形态,没有充分的开发在线教育的巨大市场。
未来发展:
学前教育的市场,整体市场还处于起步阶段,随着针对孩子的培养和教育的重视,未来市场的发展将潜力无限。
代表网站:
宝宝巴士 https://www.babybus.com
网站以免费动画为主,核心产品是丰富的幼儿早教APP
1.3 少儿编程
市场现状:
国内在线少儿外语教育领域持续风起云涌,比如新东方VIP ABC,51talk,Englishbreak,海绵外语,爱卡微口语,魔方英语,沪江网校等为代表的英语在线教育公司,以新东方,好未来,英孚为代表的传统培训机构,以及直接瞄准这一细分市场的创业公司,例如VIPKID
未来发展:
少儿外语的市场虽然广阔,但是已经拥有多家培训机构,线下的成本高昂等诸多因素而倒闭,目前市场发展处于最初探索期,产品升级迫在眉睫,大部分家长对这种在线教育形式仍然存在疑虑,但有越来越多的家长开始接受,随着认可程度的提高,未来市场同样有一定的潜力。
代表网站:
VIPKID https://www.vipkid.com.cn/
外教一对一在线教学
1.4 中小学生
市场现状:
中小学在线教育呈现多样化发展,题库,英语家教等领域内均有产品获得千万美元以上投资,同时竞争压力在加剧,不断有创业者进入这个领域,或者传统教育机构开始布局,尤其在国内的一线城市,目前这方面影响力广泛的APP有很多,比如学而思网校学大教育网,一起作业网,猿题库文库,网游网学霸君等
未来发展:
O2O教育方式对中小学的影响极其深远,其中未来教育的发展中最重要的机会上门家教或者线下机构合作都能对原有的线上教育形成有效补充,目前几乎所有的教育机构都在尝试O2O模式
代表网站:
学而思 https://www.xueersi.com
录播、直播、一对一
1.5 高校学生
市场现状:
目前市场主要集中在学历教育方面,国家对于网校的毕业证正在逐步认可,但是这方面教学的内容需要较高的知识基础,而且需要教学机构相当大的影响力,除了学历教育以外,就是学校自己开发的在线课程平台,专业性课程主要对内部学生开放,就其他基础性课程对公开课对外开放。
未来发展:
对于学历教育而言,无法有效的做到O2O模式,一般以移动端的学历考证知识的学习为主,现代社会对个人水平要求越来越高,在未来的趋势中,大学相关的在线教育领域会迎来一个快速的发展期。
代表网站:
中国大学慕课 http://www.icourse163.org
由高教社联手网易推出,让每一个有提升愿望的用户能够学到中国知名高校的课程,并获得认证。
学堂在线 http://www.xuetangx.com/
由清华大学研发出的中文MOOC,面向全球提供在线课程。
1.6 留学
市场现状:
教育部统计显示,中国目前每年的出国留学生总数在四十万人左右,其中本科及以下层面就读的人数增长迅猛,低龄化趋势明显,在线教育市场向二三线城市蔓延,与留学相关的在线教育培训机构迅速增加。
未来发展:
未来的发展离不开全球化,对于个人而言留学是一个增加阅历,提高教育经历,获得更高发展方式,未来的需求量将不断增长,同样进入该行业的教育机构或企业,也会增加金融教育机构与留学机构的结合是大势所趋。
代表网站:
启德考培在线 http://qide.edusoho.cn/
启德教育旗下的在线视频网站
1.7 职业考试
市场现状:
职业教育的投融资情况表现稳定,目前仍是在线教育领域内的热门投资板块,中国在线教育市场中职业教育的占比高达30%以上,另外官方也鼓励发展职业教育。
未来发展:
职业考试主要是针对一些与职业相关的证书的考试,但是考试类的证书需要国家教育机构承认,因此对进入在线职业教育领域的要求较高。
代表网站:
中公教育 http://www.offcn.com/
公务员考试,各种职业资格认证考试,线下培训和网校
1.8 职业技能
市场现状:
职业技能的培训,是目前在线教育市场发展迅速的领域,其中一些企业已经形成了一定的品牌,如腾讯课堂,网易云课堂,51CTO、中国会计网校等,总体占据了市场的85%使用率,另外现有在线职业教育服务和it培训等,聚焦于垂直领域,专业性虽强,但服务过于分散,规模较小,平台化在线职业教育有望通过提升用户搜索效率,降低寻找成本而获得用户青睐。
未来发展:
职业培训的未来市场十分广阔,并且与其他领域的盈利模式不同,容易盈利,大部分在线教育平台都是依靠这个领域的课程获得一定的利润。职业技能在未来的发展,潜力巨大,资格证书有望发展为线上服务的核心资源,但是对于新创企业而言,进入其中分享一块蛋糕并不是很容易,其他巨头占据了市场的主要份额,如果选择垂直化某个细分领域的进行运作,可获得意外的惊喜。
代表网站 :
51cto http://edu.51cto.com/
最早一批的it职业技能网站
1.9 成人外语
市场现状:
与少儿外语更注重基础性不同,成人外语主要是培优业务较多,更注重高水平的外语知识,同时小语种的学习人数也在增多。
未来发展:
随着从业者增加,在线教育,语言学习领域竞争不断加剧,部分语言学习产品因此竞争增加,提前遭遇发展瓶颈,越来越向大型语言教育机构汇聚,功能细分的小型垂直机构,未来的生存将更加艰难。O2O模式对于成人外语同样重要语言培训课程在线下拥有更高的用户体验,与传统培训机构合作,也能缓解线上线下的竞争关系
代表网站:
沪江外语 https://www.hujiang.com/
2、 八种商业模式
2.1 C2C模式(Consumer To Consumer 平台模式 )
用户到用户,这种模式本质是将自己的流量或者用户转卖给视频或者直播的内容提供者,通过出售内容分成获利。
平台模式避开了非常沉重的内容和服务,扩张迅速,但实际这种模式也有缺陷,在线教育这两年的发展使内容迅速贬值,比较难带来更免费用户和流量。
代表网站:
51cto http://edu.51cto.com/
腾讯课堂 https://ke.qq.com/
2.2 B2C模式(Business To Customer 会员模式)
平台就是自己商家—>用户(会员)
商家到用户,这种模式是自己制作大量自有版权的视频,放在自有平台上,让用户按月付费或者按年付费。 这种模式简单,快速,只要专心录制大量视频即可快速发展,其曾因为 lynda 的天价融资而 大热。但在中国由于版权保护意识不强,教育内容易于复制,有海量的免费资源的竞争对手众多等原因,难以取得像样的现金流。
代表网站:
lynda https://www.lynda.com/
慕课网 https://www.imooc.com/
谷粒学院 http://www. uijiuye .com/
2.3 B2B2C(商家到商家到用户)
B:商家;B:平台;C:用户
平台链接第三方教育机构和用户,平台一般不直接提供课程内容,而是更多承担教育的互联网载体角色,为教学过程各个环节提供全方位支持和服务。
代表网站:
51cto http://edu.51cto.com/
腾讯课堂 https://ke.qq.com/
2.4 垂直领域
智联招聘: 13年-14年 垂直领域
这种模式需要糅合录播,直播,帮助服务等多种手段,对学生学习某一项内容负责。这种模式收费高,有较强的壁垒。这种产品一旦形成口碑,会有稳定的用户群和收入,但产品非常复杂,难度大,门槛高,即使单独一个项目都会耗费大量的人力物力,因此发展速度较慢。
代表网站:
51cto的微职位 http://edu.51cto.com/
网易云课堂的微专业 https://study.163.com/
B2B 商家对商家:
1688.com
2.5 直播、互动
这种模式将传统课堂上的反馈,交互,答疑搬到线上。让用户容易接受,只要服务贴心,用 户就愿意买单,因此有丰富现金流。但缺陷是只能通过平台吸引用户,造成了竞争门槛过低, 模式雷同,对手众多,收益的永远是拥有流量或者用户的大平台。
代表网站:
腾讯课堂: https://ke.qq.com/
学而思 https://www.xueersi.com
2.6 1 对 1
让一个讲师在一定时间内对一个学员进行辅导,学生按照时间支付费用。这种模式收费容易, 现金流好,产品难度不大,市场空间大,但是人力资源的获取消耗却是巨大的,如果师资上控制不好,比如优秀的讲师留不住,或者整体成本太大,都会导致 1 对 1 模式难以发展。
代表网站:
VIPKID https://www.vipkid.com.cn/
学而思 https://www.xueersi.com
2.7 O2O 模式(Online To Offline 线上到线下)
饿了吗
就是通过免费内容或者运营,让线上平台获取用户和流量,将用户吸引到线下开课,或 者让学员到加盟的线下机构上课。这种模式形式简单,收益高,只要把控用户需求,吸引到用户,收费不成问题,而且符合传统的消费习惯。
代表网站:
启德教育 https://www.eic.org.cn/
2.8 freemium(免费增值)
Freemium最早由AVC的Fred Wilson在2006年提出, 指的是用免费服务吸引用户,然后通过增值服务,将部分免费用户转化为收费用户,实现变现。Freemium模式中有“二八定律”的因素,即一小部分对价格不敏感的高端用户,愿意为一些额 外的功能付费,为服务提供者带来大部分收入。
代表网站:
中国大学慕课 http://www.icourse163.org
通过免费的名校课程和高校建立合作,吸引用户。提供考研专栏和学校云增值服务
学堂在线 http://www.xuetangx.com/
课程免费,如果希望得到课程的认证证书则要缴纳相应的费用
3、 优学在线项目介绍
3.1功能简介
优学在线教育,是一个B2C模式的职业技能在线教育系统,分为前台用户系统和后台运营平台。
3.2系统模块
前后台分离开发:
1、 解耦
2、 抗并发
3、 后台和前端完全分离,并且后台接口可以供给多端使用!(APP,微信,PC,WAP)
3.3系统架构
架构设计需要考虑的几个方面:
性能:主要考虑访问频率,每个用户每天的访问次数。项目初始阶段用户的访问量并不大,如果考虑做运营推广,可能会迎来服务器访问量骤增,因此要考虑分布式部署,引入缓存
可扩展性:系统功能会随着用户量的增加以及多变的互联网用户需求不断地扩展,因此考虑到系统的可扩展性的要求需要使用微服务架构,引入消息中间件
高可用:系统一旦宕机,将会带来不可挽回的损失,因此必须做负载均衡,甚至是异地多活这类复杂的方案。如果数据丢失,修复将会非常麻烦,只能靠人工逐条修复,这个很难接受,因此需要考虑存储高可靠。我们需要考虑多种异常情况:机器故障、机房故障,针对机器故障,我们需要设计 MySQL 同机房主备方案;针对机房故障,我们需要设计 MySQL 跨机房同步方案。
安全性:系统的信息有一定的隐私性,例如用户的个人身份信息,不包含强隐私(例如玉照、情感)的信息,因此使用账号密码管理、数据库访问权限控制即可。
成本:视频类网站的主要成本在于服务器成本、流量成本、存储成本、流媒体研发成本,中小型公司可以考虑使用云服务器和云服务。
4、 优学在线环境搭建
4.1 项目架构
4.2 创建父工程yxzx-parent
在项目yxzx下创建模块:使用 Spring Initializr 快速初始化一个 Spring Boot 模块,版本:2.0.7.RELEASE
删除 src 目录
4.3 配置 pom.xml
修改版本为 :2.0.7.RELEASE
<artifactId> 节点后面添加
<packaging>pom</packaging> |
spring-boot-starter 改成 spring-boot-starter-web
<dependency> |
添加 <properties>
properties标签的作用:
在标签内可以把版本号作为变量进行声明,后面dependency中用到版本号时可以用${变量名}的形式代替,这样做的好处是:当版本号发生改变时,只有更新properties标签中的变量就行了,不用更新所有依赖的版本号
<properties> <java.version>1.8</java.version> <mybatis-plus.version>3.0.5</mybatis-plus.version> <velocity.version>2.0</velocity.version> <swagger.version>2.7.0</swagger.version> </properties> |
配置 <dependencyManagement> 锁定依赖的版本
dependencyManagement的作用
1.在Maven中dependencyManagement的作用其实相当于一个对所依赖jar包进行版本管理 的管理器。
2.pom.xml文件中,jar的版本判断的两种途径
(1):如果dependencies里的dependency自己没有声明version元素,那么maven就
会倒dependencyManagement里面去找有没有对该artifactId和groupId进行过版本声明,如果有,就继承它,如果
没有就会报错,告诉你必须为dependency声明一个version
(2):如果dependencies中的dependency声明了version,那么无论dependencyManagement中有无对该jar的version声明,都以dependency里的version为准。
<dependencyManagement> <dependencies> <!--mybatis-plus 持久层--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis-plus.version}</version> </dependency> <!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 --> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>${velocity.version}</version> </dependency> <!--swagger--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>${swagger.version}</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>${swagger.version}</version> </dependency> </dependencies> </dependencyManagement> |
4.4 创建yxzx-ebs模块
创建一个普通maven模块
Parent:yxzx_parent
Artifact:yxzx_ebs
4.5 配置 pom.xml
<dependencies> |
4.6 配置application.properties文件
resources目录下创建文件 application.properties
# 服务端口 |
SpringBoot中的环境设置:
https://www.jianshu.com/p/5307aed5a19b
4.7 创建SpringBoot启动类
package com.yxzx.ebs; |
4.8 实现讲师数据查询
步骤:
1. 用户发送请求—>2.Controller中Mapping映射—>3.Service接口—>4.Mapper接口(Mapper.xml文件)执行SQL语句;
2. 那么每一次查询都要手写SQL语句;
3. 怎么样才能不写SQL语句也能查询;SQL语句让系统帮我们自己生成!
Mapper动态代理;
在mapper.xml文件中写SQL语句;
只是不再dao层写实现类SQLSession.select(“namespaces.getId”,参数);
Mybatis-plus:工具:减少了SQL语句
可以逆向工程帮我们生成SQL,Mapper,Mapper类,Service
在小公司里面特别常见;
通过数据库表直接帮我们生成了java类;
大公司自己写!
5、 MyBatis-Plus介绍
5.1 简介
官网:http://mp.baomidou.com/
参考教程:http://mp.baomidou.com/guide/
MyBatis-Plus (简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
愿景:我们的愿景是成为 MyBatis 最好的搭档
5.2 特性
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写 错
支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer2005、 SQLServer 等多种数据库
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可 自由配置,完美解决主键问题
支持 XML 热加载:Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动
支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行 强大的 CRUD 操作
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
支持关键词自动转义:支持数据库关键词(order、key......)自动转义,还可自定义关键词
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页 等同于普通 List 查询
内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪 出慢查询
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则, 预防误操作
内置 Sql 注入剥离器:支持 Sql 注入剥离,有效预防 Sql 注入攻击
快速开始参考:http://mp.baomidou.com/guide/quick-start.html
测试项目: mybatis_plus
数据库:mybatis_plus
5.3 MP入门-环境搭建
5.3.1 创建数据库:
mybatis_plus
5.3.2 创建 User 表
其表结构如下:
idnameageemail
1 |
Jone |
18 |
test1@baomidou.com |
2 |
Jack |
20 |
test2@baomidou.com |
3 |
Tom |
28 |
test3@baomidou.com |
4 |
Sandy |
21 |
test4@baomidou.com |
5 |
Billie |
24 |
test5@baomidou.com |
其对应的数据库 Schema 脚本如下:
其对应的数据库 Data 脚本如下:
CREATE DATABASE `mybatis_plus`; USE `mybatis_plus`; DROP TABLE IF EXISTS user; CREATE TABLE user ( id BIGINT(20) NOT NULL COMMENT '主键ID', name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名', age INT(11) NULL DEFAULT NULL COMMENT '年龄', email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱', PRIMARY KEY (id) ); |
DELETE FROM user; INSERT INTO user (id, name, age, email) VALUES (1, 'Jone', 18, 'test1@baomidou.com'), (2, 'Jack', 20, 'test2@baomidou.com'), (3, 'Tom', 28, 'test3@baomidou.com'), (4, 'Sandy', 21, 'test4@baomidou.com'), (5, 'Billie', 24, 'test5@baomidou.com'); |
5.3.3 初始化工程
使用 Spring Initializr 快速初始化一个 Spring Boot 工程
Group:com.ujiuye
Artifact:mybatis-plus
版本:2.0.7.RELEASE
5.3.4 添加依赖
spring-boot-starter、spring-boot-starter-test
添加:mybatis-plus-boot-starter、MySQL、lombok、
在项目中使用Lombok可以减少很多重复代码的书写。比如说getter/setter/toString等方法的编写
<dependencies> |
注意:引入 MyBatis-Plus 之后请不要再次引入 MyBatis 以及 MyBatis-Spring,以避免因版本差异导致的问题。
5.3.5 idea中安装lombok插件
5.3.6 配置
在 application.properties 配置文件中添加 MySQL 数据库的相关配置:
mysql5
#mysql数据库连接 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus spring.datasource.username=root spring.datasource.password=123456 |
mysql8以上(spring boot 2.1)
注意:driver和url的变化
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
注意:
1、这里的 url 使用了 ?serverTimezone=GMT%2B8 后缀,因为Spring Boot 2.1 集成了 8.0版本的jdbc驱动,这个版本的 jdbc 驱动需要添加这个后缀,否则运行测试用例报告如下错误:
java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more
2、这里的 driver-class-name 使用了 com.mysql.cj.jdbc.Driver ,在 jdbc 8 中 建议使用这个驱动,之前的 com.mysql.jdbc.Driver 已经被废弃,否则运行测试用例的时候会有 WARN 信息
5.4 MP入门-编写代码
5.4.1 主类
在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹
注意:扫描的包名根据实际情况修改
@MapperScan的作用
https://blog.csdn.net/syq8023/article/details/92782972
@SpringBootApplication |
5.4.2 实体
创建包 entity 编写实体类 User.java(此处使用了 Lombok 简化代码)
@Data
该注解使用在类上,该注解会提供getter、setter、equals、canEqual、hashCode、toString方法。
@Data public class User { private Long id; private String name; private Integer age; private String email; } |
查看编译结果
5.4.3 mapper
创建包 mapper 编写Mapper 接口: UserMapper.java
User的mapper接口
public interface UserMapper extends BaseMapper<User> { } |
5.5 测试
添加测试类,进行功能测试:
@RunWith(SpringRunner.class) @SpringBootTest public class MybatisPlusApplicationTests { @Autowired private UserMapper userMapper; @Test public void testSelectList() { System.out.println(("----- selectAll method test ------")); //UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper //所以不填写就是无任何条件 List<User> users = userMapper.selectList(null); users.forEach(System.out::println); } } |
注意:
IDEA在 userMapper 处报错,因为找不到注入的对象,因为类是动态创建的,但是程序可以正确的执行。
为了避免报错,可以在 dao 层 的接口上添加 @Repository 注
控制台输出:
User(id=1, name=Jone, age=18, email=test1@baomidou.com)
User(id=2, name=Jack, age=20, email=test2@baomidou.com)
User(id=3, name=Tom, age=28, email=test3@baomidou.com)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
User(id=5, name=Billie, age=24, email=test5@baomidou.com )
通过以上几个简单的步骤,我们就实现了 User 表的 CRUD 功能,甚至连 XML 文件都不用编写!
5.6 配置日志
查看sql输出日志
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl |
6、 Mybatis_Plus CRUD操作
6.1 新增之 insert
@RunWith(SpringRunner.class) @SpringBootTest public class CRUDTests { @Autowired private UserMapper userMapper; @Test public void testInsert(){ User user = new User(); user.setName("Helen"); user.setAge(18); user.setEmail("55317332@qq.com"); int result = userMapper.insert(user); System.out.println(result); //影响的行数 System.out.println(user); //id自动回填 } } |
注意:数据库插入id值默认为:全局唯一id
6.2 主键策略
(1)ID_WORKER
MyBatis-Plus默认的主键策略是:ID_WORKER 全局唯一ID
使用IdWorker工具类测试id的生成,了解id生成原理
• 添加工具类:IdWorker.java
• 编写测试用例:
@Test public void testIdWorker(){ long id = new IdWorker().nextId(); System.out.println(id); } |
(2)自增策略
o 要想主键自增需要配置如下主键策略
o 需要在创建数据表的时候设置主键自增
o 实体字段中配置 @TableId(type = IdType.AUTO)
@TableId(type = IdType.AUTO)
private Long id;
要想影响所有实体的配置,可以设置全局主键配置
#全局设置主键生成策略
mybatis-plus.global-config.db-config.id-type=auto
其它主键策略:分析 IdType 源码可知
@Getter
public enum IdType {
/**
* 数据库ID自增
*/
AUTO(0),
/**
* 该类型为未设置主键类型
*/
NONE(1),
/**
* 用户输入ID
* 该类型可以通过自己注册自动填充插件进行填充
*/
INPUT(2),
/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
/**
* 全局唯一ID (idWorker)
*/
ID_WORKER(3),
/**
* 全局唯一ID (UUID)
*/
UUID(4),
/**
* 字符串全局唯一ID (idWorker 的字符串表示)
*/
ID_WORKER_STR(5);
private int key;
IdType(int key) {
this.key = key;
}
}
6.3 修改之update
6.3.1 根据Id更新操作
注意:update时生成的sql自动是动态sql:UPDATE user SET age=? WHERE id=?
@Test public void testUpdateById(){ User user = new User(); user.setId(1L); user.setAge(28); int result = userMapper.updateById(user); System.out.println(result); } |
6.3.2 自动填充
项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。
我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作:
(1)数据库表中添加自动填充字段
在User表中添加datetime类型的新的字段 create_time、update_time
(2)实体上添加注解
@Data
public class User {
......
@TableField(fill = FieldFill.INSERT)
private Date createTime;
//@TableField(fill = FieldFill.UPDATE)
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}
(3)实现元对象处理器接口
注意:不要忘记添加 @Component 注解
package com.ujiuye.mybatisplus.handler; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import org.apache.ibatis.reflection.MetaObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.util.Date; @Component public class MyMetaObjectHandler implements MetaObjectHandler { private static final Logger LOGGER = LoggerFactory.getLogger(MyMetaObjectHandler.class); @Override public void insertFill(MetaObject metaObject) { LOGGER.info("start insert fill ...."); this.setFieldValByName("createTime", new Date(), metaObject); this.setFieldValByName("updateTime", new Date(), metaObject); } @Override public void updateFill(MetaObject metaObject) { LOGGER.info("start update fill ...."); this.setFieldValByName("updateTime", new Date(), metaObject); } } |
6.3.3 乐观锁
乐观锁概述:
乐观锁是丢失更新问题的解决方案
丢失更新问题:
对于一条数据,有两个事务要对这个条数据进行修改,第一个事务提交修改后,第二个事务也进行了事务提交修改,结果事务查看结果不是自己修改后的结果,明明修改了但是结果确不是自己修改的内容,这种现象就称为丢失更新问题。
主要适用场景:当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新
乐观锁实现方式:
• 取出记录时,获取当前version
• 更新时,带上这个version
• 执行更新时, set version = newVersion where version = oldVersion
• 如果version不对,就更新失败
(1)数据库中添加version字段
ALTER TABLE `user` ADD COLUMN `version` INT
(2)实体类添加version字段
并添加 @Version 注解
@Version
@TableField(fill = FieldFill.INSERT)
private Integer version;
(3)元对象处理器接口添加version的insert默认值
@Override
public void insertFill(MetaObject metaObject) {
......
this.setFieldValByName("version", 1, metaObject);
}
特别说明:
• 支持的数据类型只有 int,Integer,long,Long,Date,Timestamp,LocalDateTime
• 整数类型下 newVersion = oldVersion + 1
• newVersion 会回写到 entity 中
• 仅支持 updateById(id) 与 update(entity, wrapper) 方法
• 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
(4)在 MybatisPlusConfig 中注册 Bean
创建配置类
package com.ujiuye.mybatisplus.config; import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; @EnableTransactionManagement @Configuration @MapperScan("com.atguigu.mybatis_plus.mapper") public class MybatisPlusConfig { /** * 乐观锁插件 */ @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } } |
@EnableTransactionMangerment注解的作用:
https://blog.csdn.net/u010963948/article/details/79208328
(5)测试乐观锁可以修改成功
测试后分析打印的sql语句,将version的数值进行了加1操作
/** * 测试 乐观锁插件 */ @Test public void testOptimisticLocker() { //查询 User user = userMapper.selectById(1L); //修改数据 user.setName("Helen Yao"); user.setEmail("helen@qq.com"); //执行更新 userMapper.updateById(user); } |
(5)测试乐观锁修改失败
/**
* 测试乐观锁插件 失败
*/
@Test
public void testOptimisticLockerFail() {
//查询
User user = userMapper.selectById(1L);
//修改数据
user.setName("Helen Yao1");
user.setEmail("helen@qq.com1");
//模拟取出数据后,数据库中version实际数据比取出的值大,即已被其它线程修改并更新了version
user.setVersion(user.getVersion() - 1);
//执行更新
userMapper.updateById(user);
}
6.4 select
6.4.1 根据id查询记录
@Test
public void testSelectById(){
User user = userMapper.selectById(1L);
System.out.println(user);
}
6.4.2 通过多个id批量查询
完成了动态sql的foreach的功能
@Test
public void testSelectBatchIds(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
users.forEach(System.out::println);
}
6.4.3 简单的条件查询
通过map封装查询条件
@Test
public void testSelectByMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name", "Helen");
map.put("age", 18);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
注意:map中的key对应的是数据库中的列名。例如数据库user_id,实体类是userId,这时map的key需要填写user_id
6.4.4 分页
MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能
(1)创建配置类
此时可以删除主类中的 @MapperScan 扫描注解,将注解赋值到配置类中
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
(2)测试selectPage分页
测试:最终通过page对象获取相关数据
@Test
public void testSelectPage() {
Page<User> page = new Page<>(1,5);
userMapper.selectPage(page, null);
page.getRecords().forEach(System.out::println);
System.out.println(page.getCurrent());//当前页
System.out.println(page.getPages());//总页数
System.out.println(page.getSize());//每页显示记录数
System.out.println(page.getTotal());//总记录数
System.out.println(page.hasNext());//是否有下一页
System.out.println(page.hasPrevious());//是否有上一页
}
控制台sql语句打印:SELECT id,name,age,email,create_time,update_time FROM user LIMIT 0,5
(3)测试selectMapsPage分页:结果集是Map
@Test
public void testSelectMapsPage() {
Page<User> page = new Page<>(1, 5);
IPage<Map<String, Object>> mapIPage = userMapper.selectMapsPage(page, null);
//注意:此行必须使用 mapIPage 获取记录列表,否则会有数据类型转换错误
mapIPage.getRecords().forEach(System.out::println);
System.out.println(page.getCurrent());
System.out.println(page.getPages());
System.out.println(page.getSize());
System.out.println(page.getTotal());
System.out.println(page.hasNext());
System.out.println(page.hasPrevious());
}
6.5 delete
6.5.1 根据id删除记录
@Test
public void testDeleteById(){
int result = userMapper.deleteById(8L);
System.out.println(result);
}
6.5.2 批量删除
@Test
public void testDeleteBatchIds() {
int result = userMapper.deleteBatchIds(Arrays.asList(8, 9, 10));
System.out.println(result);
}
6.5.3 简单的条件查询删除
@Test
public void testDeleteByMap() {
HashMap<String, Object> map = new HashMap<>();
map.put("name", "Helen");
map.put("age", 18);
int result = userMapper.deleteByMap(map);
System.out.println(result);
}
6.5.4 逻辑删除
• 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据
• 逻辑删除:假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录
(1)数据库中添加 deleted字段
ALTER TABLE `user` ADD COLUMN `deleted` boolean
(2)实体类添加deleted 字段
并加上 @TableLogic 注解 和 @TableField(fill = FieldFill.INSERT) 注解
@TableLogic
@TableField(fill = FieldFill.INSERT)
private Integer deleted;
(3)元对象处理器接口添加deleted的insert默认值
@Override
public void insertFill(MetaObject metaObject) {
......
this.setFieldValByName("deleted", 0, metaObject);
}
(4)application.properties 加入配置
此为默认值,如果你的默认值和mp默认的一样,该配置可无
Mp中默认0表示未被删除,可获取
1表示已被删除,无法获取
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
(5)在 MybatisPlusConfig 中注册 Bean
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
(6)测试逻辑删除
/**
* 测试 逻辑删除
*/
@Test
public void testLogicDelete() {
int result = userMapper.deleteById(1L);
System.out.println(result);
}
• 测试后发现,数据并没有被删除,deleted字段的值由0变成了1
• 测试后分析打印的sql语句,是一条update
• 注意:被删除数据的deleted 字段的值必须是 0,才能被选取出来执行逻辑删除的操作
(7)测试逻辑删除后的查询
MyBatis Plus中查询操作也会自动添加逻辑删除字段的判断
/**
* 测试 逻辑删除后的查询:
* 不包括被逻辑删除的记录
*/
@Test
public void testLogicDeleteSelect() {
User user = new User();
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
测试后分析打印的sql语句,包含 WHERE deleted=0
SELECT id,name,age,email,create_time,update_time,deleted FROM user WHERE deleted=0
7、 Mybatis_Plus条件构造器
如果想进行复杂条件查询,那么需要使用条件构造器 Wapper,涉及到如下方法
1、delete
2、selectOne
3、selectCount
4、selectList
5、selectMaps
6、selectObjs
7、update
7.1 wrapper介绍
Wrapper : 条件构造抽象类,最顶端父类
AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
QueryWrapper : Entity 对象封装操作类,不适用lambda语法
UpdateWrapper : Update 条件封装,用于Entity对象更新操作
AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。
LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper
LambdaUpdateWrapper : Lambda 更新封装Wrapper
查询方式 |
说明 |
setSqlSelect |
设置 SELECT 查询字段 |
where |
WHERE 语句,拼接 + WHERE 条件 |
and |
AND 语句,拼接 + AND 字段=值 |
andNew |
AND 语句,拼接 + AND (字段=值) |
or |
OR 语句,拼接 + OR 字段=值 |
orNew |
OR 语句,拼接 + OR (字段=值) |
eq |
等于= |
allEq |
基于 map 内容等于= |
ne |
不等于<> |
gt |
大于> |
ge |
大于等于>= |
lt |
小于< |
le |
小于等于<= |
like |
模糊查询 LIKE |
notLike |
模糊查询 NOT LIKE |
in |
IN 查询 |
notIn |
NOT IN 查询 |
isNull |
NULL 值查询 |
isNotNull |
IS NOT NULL |
groupBy |
分组 GROUP BY |
having |
HAVING 关键词 |
orderBy |
排序 ORDER BY |
orderAsc |
ASC 排序 ORDER BY |
orderDesc |
DESC 排序 ORDER BY |
exists |
EXISTS 条件语句 |
notExists |
NOT EXISTS 条件语句 |
between |
BETWEEN 条件语句 |
notBetween |
NOT BETWEEN 条件语句 |
addFilter |
自由拼接 SQL |
last |
拼接在最后,例如:last(“LIMIT 1”) |
7.2 wrapper使用:
7.2.1 ge、gt、le、lt、isNull、isNotNull
需求:删除年龄大于12 ,email不为空的用户信息
@Test
public void testDelete() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.ge("age", 12)
.isNotNull("email");
int result = userMapper.delete(queryWrapper);
System.out.println("delete return count = " + result);
}
SQL:UPDATE user SET deleted=1 WHERE deleted=0 AND age >= ? AND email IS NOT NULL
7.2.2 eq、ne
注意:seletOne返回的是一条实体记录,当出现多条时会报错
需求:查询姓名叫做“Tom”的用户信息
@Test
public void testSelectOne() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "Tom");
User user = userMapper.selectOne(queryWrapper);
System.out.println(user);
}
SELECT id,name,age,email,create_time,update_time,deleted,version FROM user WHERE deleted=0 AND name = ?
8、 数据库设计
8.1 数据库
yxzx_ebs
8.2 数据表
yxzx_ebs.sql
8.3 数据库设计规约
以下规约只针对本模块,更全面的文档参考《阿里巴巴Java开发手册》:
五、MySQL数据库
1、库名与应用名称尽量一致
2、表名、字段名必须使用小写字母或数字,禁止出现数字开头,
3、表名不使用复数名词
4、表的命名最好是加上“业务名称_表的作用”。如,edu_teacher
5、表必备三字段:id, gmt_create, gmt_modified
说明:
其中 id 必为主键,类型为 bigint unsigned、单表时自增、步长为 1。
(如果使用分库分表集群部署,则id类型为verchar,非自增,业务中使用分布式id生成器)
gmt_create, gmt_modified 的类型均为 datetime 类型,前者现在时表示主动创建,后者过去分词表示被 动更新。
6、单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。 说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
7、表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint (1 表示是,0 表示否)。
说明:任何字段如果为非负数,必须是 unsigned。
注意:POJO 类中的任何布尔类型的变量,都不要加 is 前缀。数据库表示是与否的值,使用 tinyint 类型,坚持 is_xxx 的 命名方式是为了明确其取值含义与取值范围。
正例:表达逻辑删除的字段名 is_deleted,1 表示删除,0 表示未删除。
8、小数类型为 decimal,禁止使用 float 和 double。 说明:float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不 正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。
9、如果存储的字符串长度几乎相等,使用 char 定长字符串类型。
10、varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索 引效率。
11、唯一索引名为 uk_字段名;普通索引名则为 idx_字段名。
说明:uk_ 即 unique key;idx_ 即 index 的简称
12、不得使用外键与级联,一切外键概念必须在应用层解决。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。
9、 实现讲师列表接口
9.1 创建MP代码生成器
在test/java目录下创建包com.yxzx.ebs,创建代码生成器:CodeGenerator.java
package com.yxzx.ebs; |
执行代码生成器方法
说明:
XxxServiceImpl 继承了 ServiceImpl 类,并且MP为我们注入了 XxxMapper
这样可以使用 service 层默认为我们提供的很多方法,当然也可以调用我们自己在 dao 层编写的方法。
9.2 修改entity
设置自动填充
@TableField(fill = FieldFill.INSERT)
@ApiModelProperty(value = "创建时间")
private Date gmtCreate;
@TableField(fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(value = "更新时间")
private Date gmtModified;
添加逻辑删除
@ApiModelProperty(value = "逻辑删除 1(true)已删除, 0(false)未删除")
@TableLogic
@TableField(fill = FieldFill.INSERT)
private Boolean isDeleted;
9.3 创建SpringBoot配置文件
在ebs包下创建config包,创建EbsConfig.java
@EnableTransactionManagement
@Configuration
@MapperScan("com.yxzx.ebs.teacher.mapper")
public class EbsConfig {
}
插件:
/**
* 分页插件
*
*/
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}
/**
* 逻辑删除插件
*/
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
9.4 编写后台管理api接口
在controller包下创建,创建EbsController.java
package com.yxzx.ebs.controller;
@RestController
@RequestMapping("/teacher")
public class EbsTeacherController {
}
获取所有讲师列表
@Autowired |
9.5 创建SpringBoot启动类
在edu包下创建config包,创建EduApplication.java
package com.yxzx.ebs;
@SpringBootApplication
public class EbsApplication {
public static void main(String[] args) {
SpringApplication.run(EbsApplication.class, args);
}
}
9.6 运行启动类
访问 http://localhost:8001/teacher
得到json数据
9.7 统一返回的json时间格式
默认情况下json时间格式带有时区,并且是世界标准时间,和我们的时间差了八个小时
在application.properties中设置
#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
- 点赞
- 收藏
- 关注作者
评论(0)