一、在线教育概述

1.1 什么是在线教育

1.1.1 基本概述

在线教育顾名思义,是以网络为介质的教学方式,通过网络,学员与教师即使相隔万里也可以开展教学

活动;此外,借助网络课件,学员还可以随时随地进行学习,真正打破了时间和空间的限制,对于工作

繁忙,学习时间不固定的职场人而言网络远程教育是最方便不过的学习方式。

1.1.2 发展潜力

所有人离不开教育:早期教育、课外辅导、少儿英语、职业教育、出国留学、商学院、移民服务……而 在信息化爆发式发展的趋势下,在线教育越来越凸显出优势: ​ 1)在线教育可以突破时间和空间的限制,提升了学习效率; ​ 2)在线教育可以跨越因地域等方面造成的教育资源不平等分配,使教育资源共享化,降低了学习的门 槛。 ​ 基于在线教育的特点和优势,网络学校受到越来越多人的认可,各类新兴的网校及相关网站也不断涌 现。显然,这代表着网校已经逐渐走进大众的生活并成为一种学习的主流趋势。因此很多人开始选择在 线教育,特别是白领一族和大学生们。仅2012年一年,中国在线教育市场份额已经达到723亿元,且在线 教育用户呈规模性放大

1.1.3 适用行业

具体来说在线培训学习系统可适合于:

1)政府:现今我们的政府也提倡学习型组织,不断变化的政策环境、不断出现的新事物对政府公务员提

出了更高的要求,而且政府机构的网络资源较佳,“在线培训系统”对公务员学习新知识和提高素质有

很大帮助,更关键的是政府机构是垂直管理体制,只要在一个领域中创建并维护一套知识库,就可以让

整个领域共享这宝贵的知识财富。

2)学校:随着网络的兴起,各大中学校可通过建立网上学校,加强学校、老师、学生之间的相互交流沟

通,提高教学质量,亦可建立公共教学资源库,建设精品课程,宣传学校的教育实力。

3)行业:许多行业知识库体系庞大,专业多且层次深,因此行业一直注重知识和经验的积累,但这些宝

贵的知识财富散落在各地,并没有利用和共享,因此,充分利用现有资源就能够创建一套丰富的知识库

体系,让整个行业受益。

4)企业:企业的知识库体系通常是企业的核心竞争力,使用“在线教育培训系统”,企业能够创建自己

的知识库体系,并允许企业内部员工随时随地学习和分享这些知识。不断提升的员工素质和不断积累的企业知识库是企业能够保持长久的竞争力的关键。对于大型企业,还可以为合作伙伴及客户创建远程学

习平台,提升和考核合作伙伴的专业技能并降低服务和支持成本。

1.2 十个行业分类

1、母婴

市场现状:

尽管母婴在线教育市场已经发展多年,但行业总体仍处于赚吆喝不赚钱的状态,主要原因在于国内垂直 母婴网站大多存在同质化竞争激烈和盈利模式单一问题,从在线教育的内容来看,大部分母婴网站功能 类似大多是基础的母婴知识库问题,咨询交流社区的内容,特色区分并不明显。

未来发展:

中国母婴产业是朝阳产业,现在处于快速发展的初期,国内每年将近有2000万新生儿, 0到36个月的婴 幼儿超过6000万,加上还有2000万左右的孕妈妈,市场规模已经超过万亿,所以发展母婴及儿童产品的 在线教育市场,前景非常广阔。

代表网站:妈妈网 妈妈网

类似社交网站, 视频频道跳转到腾讯视频

2、学前教育

市场状况:

大多数企业缺乏有效的产品形态,没有充分的开发在线教育的巨大市场。

未来发展:

学前教育的市场,整体市场还处于起步阶段,随着针对孩子的培养和教育的重视,未来市场的发展将潜 力无限。

代表网站:宝宝巴士 https://www.babybus.com

网站以免费动画为主,核心产品是丰富的幼儿早教APP

3、少儿外语

市场现状:

国内在线少儿外语教育领域持续风起云涌, 比如新东方VIP ABC, 51talk, Englishbreak,海绵外语,爱 卡微口语,魔方英语,沪江网校等为代表的英语在线教育公司,以新东方,好未来,英孚为代表的传统 培训机构,以及直接瞄准这一细分市场的创业公司,例如VIPKID

未来发展:

少儿外语的市场虽然广阔,但是已经拥有多家培训机构,线下的成本高昂等诸多因素而倒闭,目前市场 发展处于最初探索期,产品升级迫在眉睫,大部分家长对这种在线教育形式仍然存在疑虑,但有越来越 多的家长开始接受,随着认可程度的提高,未来市场同样有一定的潜力。

代表网站:VIPKID 北京大米科技有限公司-VIPKID官网-北美外教-1对1口语培训-在线英语教育

外教一对一在线教学

4、中小学生

市场现状:

中小学在线教育呈现多样化发展,题库,英语家教等领域内均有产品获得千万美元以上投资,同时竞争 压力在加剧,不断有创业者进入这个领域,或者传统教育机构开始布局,尤其在国内的一线城市,目前 这方面影响力广泛的APP有很多,比如学而思网校学大教育网,一起作业网,猿题库文库,网游网学霸君 等

未来发展:

O2O教育方式对中小学的影响极其深远,其中未来教育的发展中最重要的机会上门家教或者线下机构合 作都能对原有的线上教育形成有效补充,目前几乎所有的教育机构都在尝试O2O模式

代表网站:学而思 https://www.xueersi.com

录播、直播、一对一

5、高校学生

市场现状:

目前市场主要集中在学历教育方面,国家对于网校的毕业证正在逐步认可,但是这方面教学的内容需要 较高的知识基础,而且需要教学机构相当大的影响力,除了学历教育以外,就是学校自己开发的在线课 程平台,专业性课程主要对内部学生开放,就其他基础性课程对公开课对外开放。

未来发展:

对于学历教育而言,无法有效的做到O2O模式,一般以移动端的学历考证知识的学习为主,现代社会对 个人水平要求越来越高,在未来的趋势中,大学相关的在线教育领域会迎来一个快速的发展期。

代表网站:中国大学慕课 http://www.icourse163.org

由高教社联手网易推出,让每一个有提升愿望的用户能够学到中国知名高校的课程,并获得认证。 学堂在线 学堂在线 - 精品在线课程学习平台

由清华大学研发出的中文MOOC,面向全球提供在线课程。

6、留学

市场现状:

教育部统计显示,中国目前每年的出国留学生总数在四十万人左右,其中本科及以下层面就读的人数增 长迅猛,低龄化趋势明显,在线教育市场向二三线城市蔓延,与留学相关的在线教育培训机构迅速增 加。

未来发展:

未来的发展离不开全球化,对于个人而言留学是一个增加阅历,提高教育经历,获得更高发展方式,未 来的需求量将不断增长,同样进入该行业的教育机构或企业,也会增加金融教育机构与留学机构的结合 是大势所趋。

代表网站:启德考培在线 http://qide.edusoho.cn/

启德教育旗下的在线视频网站

7、职业考试

市场现状:

职业教育的投融资情况表现稳定,目前仍是在线教育领域内的热门投资板块,中国在线教育市场中职业 教育的占比高达30%以上,另外官方也鼓励发展职业教育。

未来发展:

职业考试主要是针对一些与职业相关的证书的考试,但是考试类的证书需要国家教育机构承认,因此对 进入在线职业教育领域的要求较高。

代表网站:

中公教育 公务员考试-2023国考公务员报名/时间/职位-培训-中公教育网

公务员考试,各种职业资格认证考试,线下培训和网校

8、职业技能

市场现状:

职业技能的培训,是目前在线教育市场发展迅速的领域,其中一些企业已经形成了一定的品牌,如腾讯 课堂, 网易云课堂, 51CTO、 中国会计网校等,总体占据了市场的85%使用率,另外现有在线职业教育 服务和it培训等,聚焦于垂直领域,专业性虽强,但服务过于分散,规模较小,平台化在线职业教育有望 通过提升用户搜索效率,降低寻找成本而获得用户青睐。

未来发展:

职业培训的未来市场十分广阔,并且与其他领域的盈利模式不同,容易盈利,大部分在线教育平台都是 依靠这个领域的课程获得一定的利润。 职业技能在未来的发展,潜力巨大,资格证书有望发展为线上服 务的核心资源,但是对于新创企业而言,进入其中分享一块蛋糕并不是很容易,其他巨头占据了市场的 主要份额,如果选择垂直化某个细分领域的进行运作,可获得意外的惊喜。

代表网站 :51cto 51CTO学堂-IT培训_IT人充电上51CTO学堂

最早一批的it职业技能网站

9、成人外语

市场现状:

与少儿外语更注重基础性不同,成人外语主要是培优业务较多,更注重高水平的外语知识,同时小语种 的学习人数也在增多。

未来发展:

随着从业者增加,在线教育,语言学习领域竞争不断加剧,部分语言学习产品因此竞争增加,提前遭遇 发展瓶颈,越来越向大型语言教育机构汇聚,功能细分的小型垂直机构,未来的生存将更加艰

难。 O2O模式对于成人外语同样重要语言培训课程在线下拥有更高的用户体验,与传统培训机构合作, 也能缓解线上线下的竞争关系

代表网站:沪江外语 沪江网校-英日法韩13国外语、考研留学、职场兴趣在线学

10、个人兴趣

市场现状:

超过30%的用户表示在网上学习是满足个人的兴趣爱好,公开数据显示中国的兴趣爱好市场规模约 为500亿元。

未来发展:

社会主流人群可支配收入的增长,促使兴趣培训市场,进一步提升,个人兴趣领域,在未来空间发展可 以与k12教育基础教育的相媲美,随着个体对于精神满足的追求,国内市场将迎来一个长期稳定的发展 期。

代表网站:美食杰 https://www.meishij.net/

美食菜谱类,有图文和视频

1.3 八种商业模式

C2C模式(Consumer To Consumer 平台模式 )

用户到用户,这种模式本质是将自己的流量或者用户转卖给视频或者直播的内容提供者,通过出售内容 分成获利。

平台模式避开了非常沉重的内容和服务,扩张迅速,但实际这种模式也有缺陷,在线教育这两年的发展 使内容迅速贬值,比较难带来更免费用户和流量。

代表网站:51cto 51CTO学堂-IT培训_IT人充电上51CTO学堂 腾讯课堂 腾讯课堂_专业的在线教育平台

B2C模式(Business To Customer 会员模式)·

商家到用户,这种模式是自己制作大量自有版权的视频,放在自有平台上,让用户按月付费或者按年付 费。 这种模式简单,快速,只要专心录制大量视频即可快速发展,其曾因为 lynda 的天价融资而 大热。 但在中国由于版权保护意识不强,教育内容易于复制,有海量的免费资源的竞争对手众多等原因,难以 取得像样的现金流。

代表网站:lynda https://www.lynda.com/ 慕课网 慕课网-程序员的梦工厂

B2B2C (商家到商家到用户)

平台链接第三方教育机构和用户,平台一般不直接提供课程内容,而是更多承担教育的互联网载体角 色,为教学过程各个环节提供全方位支持和服务。

代表网站:51cto 51CTO学堂-IT培训_IT人充电上51CTO学堂 腾讯课堂 腾讯课堂_专业的在线教育平台

垂直领域

这种模式需要糅合录播,直播,帮助服务等多种手段,对学生学习某一项内容负责。这种模式收费高, 有较强的壁垒。这种产品一旦形成口碑,会有稳定的用户群和收入,但产品非常复杂,难度大,门槛 高,即使单独一个项目都会耗费大量的人力物力,因此发展速度较慢。

代表网站:51cto的微职位 51CTO学堂-IT培训_IT人充电上51CTO学堂 网易云课堂的微专业 网易云课堂 - 悄悄变强大

直播/互动

这种模式将传统课堂上的反馈,交互,答疑搬到线上。让用户容易接受,只要服务贴心,用 户就愿意买 单,因此有丰富现金流。但缺陷是只能通过平台吸引用户,造成了竞争门槛过低, 模式雷同,对手众 多,收益的永远是拥有流量或者用户的大平台。

代表网站:腾讯课堂: 腾讯课堂_专业的在线教育平台 学而思 https://www.xueersi.com

1 对 1

让一个讲师在一定时间内对一个学员进行辅导,学生按照时间支付费用。这种模式收费容易, 现金流 好,产品难度不大,市场空间大,但是人力资源的获取消耗却是巨大的,如果师资上控制不好,比如优 秀的讲师留不住,或者整体成本太大,都会导致 1 对 1 模式难以发展。

代表网站:VIPKID 北京大米科技有限公司-VIPKID官网-北美外教-1对1口语培训-在线英语教育 学而思 https://www.xueersi.com

O2O 模式(Online To Offline 线上到线下)

就是通过免费内容或者运营,让线上平台获取用户和流量,将用户吸引到线下开课,或 者让学员到加盟 的线下机构上课。这种模式形式简单,收益高,只要把控用户需求,吸引到用户,收费不成问题,而且 符合传统的消费习惯。

代表网站:启德教育 出国留学_申请留学指导_专业的留学咨询中介-启德教育

freemium (免费增值)

Freemium最早由AVC的Fred Wilson在2006年提出, 指的是用免费服务吸引用户,然后通过增值服务,将 部分免费用户转化为收费用户,实现变现。 Freemium模式中有“二八定律”的因素,即一小部分对价格不 敏感的高端用户,愿意为一些额 外的功能付费,为服务提供者带来大部分收入。

代表网站: 中国大学慕课 http://www.icourse163.org

通过免费的名校课程和高校建立合作,吸引用户。提供考研专栏和学校云增值服务

学堂在线 学堂在线 - 精品在线课程学习平台

课程免费,如果希望得到课程的认证证书则要缴纳相应的费用

二、项目概述

2.1 功能简介

智慧学院,是一个B2C模式的职业技能在线教育系统,分为前台用户系统和后台运营平台。

2.2 系统模块

2.3 系统架构

架构设计需要考虑的几个方面:

  • 性能:主要考虑访问频率,每个用户每天的访问次数。项目初始阶段用户的访问量并不大,如果考虑做运营推广,可能会迎来服务器访问量骤增,因此要考虑分布式部署,引入缓存

  • 可扩展性:系统功能会随着用户量的增加以及多变的互联网用户需求不断地扩展,因此考虑到系统的可扩展性的要求需要使用微服务架构,引入消息中间件

  • 高可用:系统一旦宕机,将会带来不可挽回的损失,因此必须做负载均衡,甚至是异地多活这类复杂的方案。如果数据丢失,修复将会非常麻烦,只能靠人工逐条修复,这个很难接受,因此需要考虑存储高可靠。我们需要考虑多种异常情况:机器故障、机房故障,针对机器故障,我们需要设计 MySQL 同机房主备方案;针对机房故障,我们需要设计 MySQL 跨机房同步方案。

  • 安全性:系统的信息有一定的隐私性,例如用户的个人身份信息,不包含强隐私(例如玉照、情感)的信息,因此使用账号密码管理、数据库访问权限控制即可。

  • 成本:视频类网站的主要成本在于服务器成本、流量成本、存储成本、流媒体研发成本,中小型公司可以考虑使用云服务器和云服务。

三、MyBatis-Plus基本操作

3.1 MyBatis-Plus简介

MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

愿景

我们的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍

3.2 MyBatis-Plus特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑

  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作

  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求

  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错

  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题

  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作

  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )

  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用

  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询

  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库

  • 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询

  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

3.3 MyBatis-Plus快速入门

3.3.1 创建数据库表

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)
);
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');

3.3.2 初始化工程

使用 Spring Initializr 快速初始化一个 Spring Boot 工程

名称 说明
Group com.itfxp
Artifact mybatis-plus
版本 2.2.1.RELEASE

3.3.3 导入相关依赖

注意: 引入 MyBatis-Plus 之后请不要再次引入 MyBatis 以及 MyBatis-Spring ,以避免因版本差异导致的问题。

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency>
​<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency>
​<!--mybatis-plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.0.5</version></dependency>
​<!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency>
​<!--lombok用来简化实体类 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>
</dependencies>

3.3.4 相关配置

在 application.properties 配置文件中添加 MySQL 数据库的相关配置:

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=root

注意:

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 信息

3.3.5 编写主启动类

在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹

@SpringBootApplication
@MapperScan("com.itfxp.mybatisplus.mapper")
public class MybatisPlusApplication {
​public static void main(String[] args) {SpringApplication.run(MybatisPlusApplication.class, args);}
}

注意: 扫描的包名根据实际情况修改

3.3.6 编写User实体类

创建包 entity 编写实体类User.java(此处使用了 Lombok 简化代码)

@Data
public class User {private Long id;private String name;private Integer age;private String email;
}

3.3.7 编写mapper

创建包 mapper 编写Mapper 接口: UserMapper.java

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itfxp.mybatisplus.entity.User;
​
public interface UserMapper  extends BaseMapper<User> {
}

3.3.8 测试

@SpringBootTest
class MybatisPlusApplicationTests {
​@Autowiredprivate UserMapper userMapper;
​@Testpublic void testSelectList() {System.out.println(("----- selectAll method test ------"));//UserMapper 中的  selectList() 方法的参数为  MP 内置的条件封装器  Wrapper //所以不填写就是无任何条件List<User> users = userMapper.selectList(null);users.forEach(System.out::println);}
}

注意事项:

问题:

项目maven打包的时候报错There are test failures.,导致运行失败。

解决办法:

选中项目,点击Toggle 'Skip Tests' Mode,重新编译/打包即可;

通过以上几个简单的步骤,我们就实现了 User 表的 CRUD 功能,甚至连 XML 文件都不用编写!

3.4 MyBatis-Plus的CRUD操作

3.4.1 配置日志

查看sql输出日志

#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

3.4.2 insert

插入操作

@Test
public void testInsert(){User user = new User();user.setName("Helen");user.setAge(18);user.setEmail("test@qq.com");int result = userMapper.insert(user);  System.out.println(result); //影响的行数 System.out.println(user); //id自动回填
}

注意: 数据库插入id值默认为:全局唯一id

主键策略

ID_WORKER

MyBatis-Plus默认的主键策略是: ID_WORKER 全局唯一ID

参考资料:分布式系统唯一*ID*生成方案汇总: https://www.cnblogs.com/haoxinyue/p/5208136.html

自增策略

要想主键自增需要配置如下主键策略

需要在创建数据表的时候设置主键自增

实体字段中配置 @TableId(type = IdType.AUTO)

@TableId(type = IdType.AUTO)
private Long

要想影响所有实体的配置,可以设置全局主键配置

#全局设置主键生成策略
mybatis-plus.global-config.db-config.id-type=auto

3.4.3 update

根据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);
}

自动填充

项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。 我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作:

1) 数据库表中添加自动填充字段

在User表中添加datetime类型的新的字段 create_time、update_time

2)实体上添加注解

@Data
public class User {@TableId(type = IdType.AUTO)private Long id;private String name;private Integer age;private String email;@TableField(fill = FieldFill.INSERT)private Date createTime;//@TableField(fill = FieldFill.UPDATE)@TableField(fill = FieldFill.INSERT_UPDATE)private Date updateTime;
}

3)实现元对象处理器接口

注意:不要忘记添加 @Component 注解

package com.itfxp.mybatisplus.config;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);@Overridepublic void insertFill(MetaObject metaObject) {LOGGER.info("start insert fill ....");this.setFieldValByName("createTime", new Date(), metaObject);this.setFieldValByName("updateTime", new Date(), metaObject);}@Overridepublic void updateFill(MetaObject metaObject) {LOGGER.info("start update fill ....");this.setFieldValByName("updateTime", new Date(), metaObject);}
}

4)测试

@Test
public void testInsert() {User user = new User();user.setName("HelenOne");user.setAge(18);user.setEmail("HelenOne@qq.com");int result = userMapper.insert(user);System.out.println(result); //影响的行数System.out.println(user); //id自动回填
}

3.4.4 乐观锁

主要适用场景: 当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新

乐观锁实现方式

  • 取出记录时,获取当前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

@EnableTransactionManagement
@Configuration
@MapperScan("com.itfxp.mybatisplus.mapper")
public class MybatisPlusConfig {/*** 乐观锁插件*/@Beanpublic OptimisticLockerInterceptor optimisticLockerInterceptor() {return new OptimisticLockerInterceptor();}
}

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);
}

6)测试乐观锁可以修改失败

/**
* 测试乐观锁插件 失败
*/
@Test
public void testOptimisticLockerFail() {//查询User user = userMapper.selectById(1L);//修改数据user.setName("Helen Yao1");user.setEmail("helen@qq.com1");//模拟取出数据后,数据库中version实际数据比取出的值大,即已被其它线程修改并更新 了versionuser.setVersion(user.getVersion() - 1);//执行更新userMapper.updateById(user);
}

3.4.5 select

根据id查询记录

@Test
public void testSelectById(){User user = userMapper.selectById(1L); System.out.println(user);
}

通过多个id批量查询

@Test
public void testSelectBatchIds(){List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));              users.forEach(System.out::println);
}

简单的条件查询

通过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

分页查询

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());
}

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());
}

3.4.6 delete

根据id删除记录

@Test
public void testDeleteById(){int result = userMapper.deleteById(8L); System.out.println(result);
}

批量删除

@Test
public void testDeleteBatchIds() {int result = userMapper.deleteBatchIds(Arrays.asList(8, 9, 10));                                System.out.println(result);
}

简单的条件查询删除

@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);
}

逻辑删除

  • 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据

  • 逻辑删除:假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录

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默认的一样,该配置可无

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)测试逻辑删除

  • 测试后发现,数据并没有被删除,deleted字段的值由0变成了1

  • 测试后分析打印的sql语句,是一条update

  • 注意:被删除数据的deleted 字段的值必须是 0,才能被选取出来执行逻辑删除的操作

/**
* 测试 逻辑删除
*/
@Test
public void testLogicDelete() {int result = userMapper.deleteById(1L);System.out.println(result);
}

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

四、MyBatis-Plus Wapper

4.1 wapper介绍

Wrapper : 条件构造抽象类,最顶端父类

  • AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件

    • QueryWrapper : Entity 对象封装操作类,不是用lambda语法

    • UpdateWrapper : Update 条件封装,用于Entity对象更新操作

    • AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。

      • LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper

      • LambdaUpdateWrapper :Lambda 更新封装Wrapper

4.2 AbstractWrapper

注意:以下条件构造器的方法入参中的 column均表示数据库字段

1、ge、gt、le、lt、isNull、isNotNull

@Test
public void testDelete() {QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.isNull("name").ge("age", 12).isNotNull("email");int result = userMapper.delete(queryWrapper);System.out.println("delete return count = " + result);
}
UPDATE user SET deleted=1 WHERE deleted=0 AND name IS NULL AND age >= ? AND email IS NOT NULL

2、eq、ne

注意:seletOne返回的是一条实体记录,当出现多条时会报错

@Test
public void testSelectOne() {QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.eq("name", "Tom");User user = userMapper.selectOne(queryWrapper);System.out.println(user);
}

SQL语句

SELECT id,name,age,email,create_time,update_time,deleted,version FROM user WHERE deleted=0 AND name = ? 

3、between、notBetween

包含大小边界

@Test
public void testSelectCount() {QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.between("age", 20, 30);Integer count = userMapper.selectCount(queryWrapper);System.out.println(count);
}

SQL语句

SELECT COUNT(1) FROM user WHERE deleted=0 AND age BETWEEN ? AND ? 

4、allEq

全部eq(或个别isNull)

@Test
public void testSelectList() {QueryWrapper<User> queryWrapper = new QueryWrapper<>();Map<String, Object> map = new HashMap<>();map.put("id", 2);map.put("name", "Jack");map.put("age", 20);queryWrapper.allEq(map);List<User> users = userMapper.selectList(queryWrapper);users.forEach(System.out::println);
}

SQL语句

SELECT id,name,age,email,create_time,update_time,deleted,version FROM user WHERE deleted=0 AND name = ? AND id = ? AND age = ? 

5、like、notLike、likeLeft、likeRight

selectMaps返回Map集合列表

@Test
public void testSelectMaps() {QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.notLike("name", "e").likeRight("email", "t");List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);//返回值是Map列表maps.forEach(System.out::println);
}

SQL语句

SELECT id,name,age,email,create_time,update_time,deleted,version FROM user WHERE deleted=0 AND name NOT LIKE ? AND email LIKE ? 

6、in、notIn、inSql、notinSql、exists、notExists

in、notIn:

notIn("age",{1,2,3})--->age not in (1,2,3)notIn("age", 1, 2, 3)--->age not in (1,2,3)

inSql、notinSql:可以实现子查询

  • 例: inSql("age", "1,2,3,4,5,6")--->age in (1,2,3,4,5,6)

  • 例: inSql("id", "select id from table where id < 3")--->id in (select id from table where id < 3)

@Test
public void testSelectObjs() {QueryWrapper<User> queryWrapper = new QueryWrapper<>();//queryWrapper.in("id", 1, 2, 3);queryWrapper.inSql("id", "select id from user where id < 3");List<Object> objects = userMapper.selectObjs(queryWrapper);//返回值是Object列表objects.forEach(System.out::println);
}

SQL语句

SELECT id,name,age,email,create_time,update_time,deleted,version FROM user WHERE deleted=0 AND id IN (select id from user where id < 3) 

7、or、and

注意:这里使用的是 UpdateWrapper 不调用or则默认为使用 and

@Test
public void testUpdate1() {//修改值User user = new User();user.setAge(99);user.setName("Andy");//修改条件UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();userUpdateWrapper.like("name", "h").or().between("age", 20, 30);int result = userMapper.update(user, userUpdateWrapper);System.out.println(result);
}

SQL语句

UPDATE user SET name=?, age=?, update_time=? WHERE deleted=0 AND name LIKE ? OR age BETWEEN ? AND ?

8、嵌套or、嵌套and

这里使用了lambda表达式,or中的表达式最后翻译成sql时会被加上圆括号

@Test
public void testUpdate2() {//修改值User user = new User();user.setAge(99);user.setName("Andy");//修改条件UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();userUpdateWrapper.like("name", "h").or(i -> i.eq("name", "李白").ne("age", 20));int result = userMapper.update(user, userUpdateWrapper);System.out.println(result);
}

SQL语句

UPDATE user SET name=?, age=?, update_time=?  WHERE deleted=0 AND name LIKE ? OR ( name = ? AND age <> ? ) 

9、orderBy、orderByDesc、orderByAsc

@Test
public void testSelectListOrderBy() {QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.orderByDesc("id");List<User> users = userMapper.selectList(queryWrapper);users.forEach(System.out::println);
}

SQL语句

SELECT id,name,age,email,create_time,update_time,deleted,version FROM user WHERE deleted=0 ORDER BY id DESC 

10、last

直接拼接到 sql 的最后

注意:只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用

@Test
public void testSelectListLast() {QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.last("limit 1");List<User> users = userMapper.selectList(queryWrapper);users.forEach(System.out::println);
}

SQL语句

SELECT id,name,age,email,create_time,update_time,deleted,version FROM user WHERE deleted=0 limit 1 

11、指定要查询的列

@Test
public void testSelectListColumn() {QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.select("id", "name", "age");List<User> users = userMapper.selectList(queryWrapper);users.forEach(System.out::println);
}

SQL语句

SELECT id,name,age FROM user WHERE deleted=0 

12、set、setSql

最终的sql会合并 user.setAge(),以及 userUpdateWrapper.set() 和 setSql() 中 的字段

@Test
public void testUpdateSet() {//修改值User user = new User();user.setAge(99);//修改条件UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();userUpdateWrapper.like("name", "h").set("name", "老李头")//除了可以查询还可以使用set设置修改的字段.setSql(" email = '123@qq.com'");//可以有子查询int result = userMapper.update(user, userUpdateWrapper);
}

SQL语句

UPDATE user SET age=?, update_time=?, name=?, email = '123@qq.com' WHERE deleted=0 AND name LIKE ? 

在线教育_Day01-项目介绍和MyBatisPlus相关推荐

  1. 基于SpringBoot+Vue前后端分离的在线教育平台项目

    基于SpringBoot+Vue前后端分离的在线教育平台项目 赠给有缘人,希望能帮助到你!也请不要吝惜你的大拇指,你的Star.点赞将是对我最大的鼓励与支持! 开源传送门: 后台:Gitee | Gi ...

  2. 学习全栈在线教育实战项目(尚硅谷) 第一天

    学习全栈在线教育实战项目(尚硅谷) 第一天 1.建立数据库,表,创建springboot工程 (SpringBoot大大简化了我们的编码,我们不用一个个导入依赖,直接引入一个依赖即可,就会上网下载依赖 ...

  3. 在线教育系统项目开发功能介绍

    近年来因为疫情原因,越来越多的教育机构开通了线上教育平台,在线教学由于是通过互联网进行,那么只要家长或孩子有时间就都可以打开手机或者电脑进行学习,也因此,线上教育彻底打破了地域差距和时间限制,无论身处 ...

  4. 目前在线教育发展情况介绍

    在线教育发展概况 在线教育是一个炙手可热的领域,几乎每隔几周就会有一个提供在线课程的创业公司宣布获得百万美元级投资,如本周旧金山又有一家名为UniversityNow的创业公司宣布融资1730万美元. ...

  5. 在线教育平台项目——需求分析

    Hello,我是 Alex 007,一个热爱计算机编程和硬件设计的小白,为啥是007呢?因为叫 Alex 的人太多了,再加上每天007的生活,Alex 007就诞生了. 有一段时间没好好写文章了,可不 ...

  6. 谷粒学苑-在线教育实战项目-面试总结-简历优化

    项目名称: 凉州区乡村在线教育平台.正商书院在线教育平台.在线教育大数据分析平台.高等学校云课程平台 项目简介: 本系统采用微服务架构设计,是一款基于微信公众号B2C模式的在线学习平台,该平台包含三大 ...

  7. 【项目】在线教育平台项目总结

    1.项目总体描述 在线教育平台采用了B2C商业模块,基于微服务架构,采用前后端分离的方式进行开发 2.功能模块 基于前后台的模式开发,前台系统是给使用这个平台进行学习的用户,后台系统是给管理员使用的 ...

  8. 在线教育平台项目——整体架构

    Hello,我是 Alex 007,一个热爱计算机编程和硬件设计的小白,为啥是007呢?因为叫 Alex 的人太多了,再加上每天007的生活,Alex 007就诞生了. 有一段时间没好好写文章了,可不 ...

  9. 在线教育_Day02-_项目环境搭建和讲师管理接口开发

    一.数据库设计 1.1 创建数据库 创建数据库:自定义数据库名 1.2 创建数据表 导入资料中的 gl_edu.sql文件,创建表 1.3 数据库设计规约 以下规约只针对本模块,更全面的文档参考< ...

最新文章

  1. 分布式计算开源框架Hadoop入门实践
  2. python面试-Python面试中常见的3个问题
  3. Android SDK下载失败的解决方法
  4. [elk]logstash grok原理
  5. matlab::mex::ArgumentList outputs的创建
  6. 系统命名与 SQL 命名之争 - 第 1 部分
  7. C# - winform使用Dictionary的时候,程序一闪而过!
  8. 守望先锋:源氏跑酷之球图制作分享
  9. 人脸识别门禁系统设计(一)
  10. java的duplicate用法_Java ByteBuffer duplicate()用法及代码示例
  11. 学科前沿大作业:区块链技术的相关应用
  12. 一根均线选股法_一根均线选股法视频教程
  13. C++学习笔记——第三天运算符和表达式
  14. 编程训练-求矩阵乘积
  15. 讨厌的HR [转贴] --一篇颇有争议的文章
  16. python求数的积_python求数组积
  17. 管理经济分析01:博弈论与经济学
  18. Dessert(dfs)
  19. python dash教程_开发者必备神器Dash使用教程
  20. html组织架构插件,jQuery组织架构图插件okrTree.js

热门文章

  1. 安卓手机投屏软件_安卓投屏软件eshow下载
  2. 面试的时候问:你的期望薪资多少?怎么谈?
  3. 2022第一波行业红利,“东数西算”时代的云网智能新机会
  4. docker部署轻量级文件浏览器------filebrowser
  5. 我的3D之路-----非教科书式3D计算机图形学零起点全攻略
  6. Learning Policies for Adaptive Tracking with Deep Feature Cascades(深度增强学习、目标跟踪)
  7. android+信号强度,Android信号强度计算方式
  8. html怎么图片排成一排且有间隔,图片排版的17个实用技巧
  9. Paddle使用半监督式学习完成语句分类
  10. Docker compose - 最开始的version 字段是什么,为什么要写这个字段