今日任务
课程的CRUD
关于数据库的存储
有两种方案
方案一:
像课程详情 课程图片 都是数据课程的信息 可知直接放入t_course 就ok
方案二:
有时候我们只需要查询到课程的基本信息 不需要显示图片和详情 这个时候我们就会用到垂直分表
垂直分表:一些字段我们一般不需要直接查询 就把这些字段单独放一个表 通过外键关联 正常情况下 不需要查询关联表 如果需要 就通过外键查询就ok 这样能大大提高数据库的效率
生成代码
生成后测试是否已经加入
然后从我们的资料中拷贝Course.vue界面
在这之前 我们需要集成一个支持Vue的富文本编辑器
直接在终端运行
npm install quill –save
npm install –save vue-quill-editor
然后把list一系列的修改了 和之前一样
然后启动前后端服务
然后可能会报一个这样的错
别担心 这是因为你的redis没有启动起来 将redis启动就行了
这与页面就能出来了
接下来我们来做新增操作
页面都已经写好 我们只需要拷贝下来用就是了 因为我不是专业的前端 所以我们就直接用别人写好的就行了
后台:
我们需要修改的地方是 覆写service中的插入和修改方法 因为我们这个是有关联对象的 所以自带的方法已经满足不了我们了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 1@Override
2public boolean insert(Course entity) {
3 //要添加三张表 课程表 详情表 市场信心表
4 //tenantId tenantName userId userName
5 System.out.println(entity);
6 entity.setStatus(0);
7 courseMapper.insert(entity);
8 Long courseId = entity.getId();
9 //同时保存详情和市场信心
10 CourseDetail courseDetail = entity.getCourseDetail();
11 courseDetail.setCourseId(courseId);
12 courseDetailMapper.insert(courseDetail);
13 CourseMarket courseMarket = entity.getCourseMarket();
14 courseMarket.setCourseId(courseId);
15 courseMarketMapper.insert(courseMarket);
16 return true;
17}
18
19
对于Controller 在我么登录之后 需要传递一些信息进去 但是我们现在还用不到 所以就先注释掉
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 1@PutMapping
2public AjaxResult addOrUpdate(@RequestBody Course course){
3 try {
4 // @TODO 以后登录成功都能获取,现在使用holder来模拟
5 //登录成功后设置到UserInfoHolder,以后所有模块要使用都直接使用UserInfoHolder
6// course.setTenantId(UserInfoHolder.getTenant().getId());
7// course.setTenantName(UserInfoHolder.getTenant().getCompanyName());
8// course.setUserId(UserInfoHolder.getLoginUser().getId());
9// course.setUserName(UserInfoHolder.getLoginUser().getUsername());
10 if(course.getId()!=null){
11 courseService.updateById(course);
12 }else{
13 courseService.insert(course);
14 }
15
16 return AjaxResult.me();
17 } catch (Exception e) {
18 e.printStackTrace();
19 return AjaxResult.me().setMessage("保存对象失败!"+e.getMessage());
20 }
21}
22
23
修改和删除我就不在这展示了 和之前的都大同小异
课程的上线和下线
业务说明
上线:
现实生活中:
当培训机构研发出新的课程后,准备招生时,要先把它添加到数据中。但是期望暂时不需要用户能够搜索到。所以需要上线才能操作
系统中:
在系统中,我们添加了一个课程,用户不立即解就搜索到,需要上线以后才行.
下线:
当某个课程不想卖的时候,就要下线. 当课程下线后,用户不能搜索到,但是数据库是还有的.
方案-用es查询代替数据库查询
上线后,修改状态为”上线”,前台用户搜索时只能搜索到上线状态的.如果不想卖了,执行下线时,修改状态下线就ok.———-垃圾(每次都要操作数据库.)
如果有1000W的并发来查询课程,要高并发访问数据库,效率低下
上线时把课程数据
同步到es,用户查询直接从es查询.也就意味着没有上线的课程用户查询不到,因为没有放到es库.
下线时把es库课程数据删除掉. 用户就查询不到了. —NB(以基于索引搜索代替数据库查询)
好处:
降低数据库压力
提高了查询速度,增强用户体验-基于索引搜索,效率远远数据库搜索
ES java操作
准备es环境
Es服务端
方案选择
- 原生ESTransport Client方式 —> 就相当于写jdbc代码,非常麻烦.
- Springboot spring data es
spring data
是spring对数据访问抽象.这些数据可以放入db,index,nosql等包含以下:
spring data jpa spring对关系型数据库访问支持
spring data ES spring对es数据访问支持
spring data redis等 spring对redis数据访问支持
spring data …
Springboot spring data es —>对 spring data es简化了配置
安装和使用
如果你是第一次安装
elasticsearch 需要修改一下配置
将
这里面的所需的运存改为1g 当然 你电脑如果运存的大的话也可以不换
然后是启动 双击这个文件夹就行
直接就启动了
他有两个端口 一个是9200 一个是9300 9300是代码访问的客户端 9200是http的
然后再浏览器中访问
http://localhost:9200/
如果能看到这个界面 就说明是成功了
接下来我们使用kibana来访问她 首先要启动kibana的服务
首先修改一下配置 指定端口
然后启动(
尽量使用管理员权限启动)
启动成功后
我们在浏览器中访问
http://localhost:5601/
我们用的最多的是
其他的喜欢研究的可以自己去研究一下
课程上下线实现
技术架构
管理员:(千级)
添加:直接操作数据库
删除和修改: 同步操作方案
分页查询:直接操作数据.并发量小.
上线:同步信息到ES库.
下线:删除ES库数据
用户:(千万级)
搜索课程:直接从ES
实现
写的服务不具备通用性,没必要单独搞一个服务
关于数据库的设计
需要值得注意的是 :这里我们的数据库采用的是反3NF的设计
准备环境 -在自己模块
导包
1
2
3
4
5
6
7 1<!--springboot 对spring data es支持-->
2<dependency>
3 <groupId>org.springframework.boot</groupId>
4 <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
5</dependency>
6
7
配置文件
1
2
3
4
5
6
7 1spring:
2 data:
3 elasticsearch:
4 cluster-name: elasticsearch
5 cluster-nodes: 127.0.0.1:9300 #9200是图形界面端,9300代码端
6
7
入口类
1
2
3
4
5
6
7
8 1@SpringBootApplication
2public class ESApplication {
3 public static void main(String[] args) {
4 SpringApplication.run(ESApplication.class);
5 }
6}
7
8
EsCourse.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 1//包含了前台展示的字段和要添加到索引库都要写到这儿
2@Document(indexName = "hrm",type = "course")
3public class EsCourse {
4 @Id
5 private Long id;
6 private String name;
7 private String users;
8 private Long courseTypeId;
9 private String courseTypeName;
10 private Long gradeId;
11 private String gradeName;
12 private Integer status;
13 private Long tenantId;
14 private String tenantName;
15 private Long userId;
16 private String userName;
17 private Date startTime;
18 private Date endTime;
19 private String intro;
20 private String resources; //图片
21 private Date expires; //过期时间
22 private BigDecimal priceOld; //原价
23 private BigDecimal price; //原价
24 private String qq; //原价
25
26 @Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
27 private String all;
28
29 //getter和setter
30
31 //toString
32
33public String getAll() {
34 String tmp = name
35 +" "+ users
36 +" "+ courseTypeName
37 +" "+ gradeName
38 +" "+ tenantName
39 +" "+ userName
40 +" "+ intro;
41 return tmp;
42 }
43
44
EsCourseRepository.java
1
2
3
4
5
6
7
8 1import org.leryoo.index.doc.EsCourse;
2import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
3
4//在service通过注入它就可以完成索引库操作了
5public interface EsCourseRepository extends ElasticsearchRepository<EsCourse,Long> {
6}
7
8
接下来什么地方要用到就直接调用就行了 和操作数据库的方法都差不多
Service层 业务逻辑
上线
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76 1@Override
2public AjaxResult onLine(Long[] ids) {
3 try
4 {
5 //1添加索引库
6 // 通过id查询数据库里面课程
7 List<Course> courses = courseMapper
8 .selectBatchIds(Arrays.asList(ids));
9 //转换为doc
10 List<EsCourse> esCourses = courses2EsCourses(courses);
11 //批量添加就ok
12 repository.saveAll(esCourses);
13 //2 修改数据库状态和上线时间 - ids,time
14 Map<String,Object> params = new HashMap<>();
15 params.put("ids",ids);
16 params.put("onLineTime",new Date());
17 // 修改状态和上线时间
18 //update t_couse set status=1 and start_time={} where id in (1,2,3,4)
19 courseMapper.onLine(params);
20 return AjaxResult.me();
21 }catch (Exception e){
22 e.printStackTrace();
23 return AjaxResult.me().setSuccess(false).setMessage("上线失败!"+e.getMessage());
24 }
25
26}
27
28
29private List<EsCourse> courses2EsCourses(List<Course> courses) {
30 //1 声明要返回
31 List<EsCourse> result = new ArrayList<>();
32 //2 转换
33 for (Course course : courses) {
34 result.add(course2EsCourse(course));
35 }
36 //3返回
37 return result;
38}
39
40
41
42//把一个course转换为EsCourse
43private EsCourse course2EsCourse(Course course) {
44 //1 声明要返回
45 EsCourse result = new EsCourse();
46 // 2 转换
47 result.setId(course.getId());
48 result.setName(course.getName());
49 result.setUsers(course.getUsers());
50 result.setCourseTypeId(course.getCourseTypeId());
51 //type-同库-没有做关联查询
52 if (course.getCourseType() != null)
53 result.setCourseTypeName(course.getCourseType().getName());
54 result.setGradeId(course.getGrade());
55 result.setGradeName(course.getGradeName());
56 result.setStatus(course.getStatus());
57 result.setTenantId(course.getTenantId());
58 result.setTenantName(course.getTenantName());
59 result.setUserId(course.getUserId());
60 result.setUserName(course.getUserName());
61 result.setStartTime(course.getStartTime());
62 result.setEndTime(course.getEndTime());
63 //Detail
64 result.setIntro(null);
65 //resource
66 result.setResources(null);
67 //market
68 result.setExpires(null);
69 result.setPrice(null);
70 result.setPriceOld(null);
71 result.setQq(null);
72 // 3返回
73 return result;
74}
75
76
下线:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 1@Override
2public AjaxResult offLine(Long[] ids) {
3 try
4 {
5 //1 删除索引库里面的内容
6 // List<Course> courses = courseMapper
7 // .selectBatchIds(Arrays.asList(ids));
8 //转换为doc
9 // List<EsCourse> esCourses = courses2EsCourses(courses);
10 // repository.deleteAll(esCourses);
11 for (Long id : ids) {
12 repository.deleteById(id);
13 }
14 //2 修改数据库状态 status=0 end_time
15 Map<String,Object> params = new HashMap<>();
16 params.put("ids",ids);
17 params.put("offLineTime",new Date());
18 // 修改状态和下线时间
19 //update t_couse set status=0 and start_time={} where id in (1,2,3,4)
20 courseMapper.offLine(params);
21 return AjaxResult.me();
22 }catch (Exception e){
23 e.printStackTrace();
24 return AjaxResult.me().setSuccess(false).setMessage("下线失败!"+e.getMessage());
25 }
26
27}
28
29
SQL语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 1<!-- 上线-->
2<!-- void onLine(Map<String, Object> params);-->
3<update id="onLine" parameterType="map">
4 update t_course set status=1,start_time=#{onLineTime} where id in
5 <foreach collection="ids" separator="," open="(" close=")" item="id">
6 #{id}
7 </foreach>
8</update>
9
10
11<!-- void offLine(Map<String, Object> params);-->
12<update id="offLine" parameterType="map">
13 update t_course set status=0 , end_time=#{offLineTime} where id in
14 <foreach collection="ids" separator="," open="(" close=")" item="id">
15 #{id}
16 </foreach>
17</update>
18
19
注意前端传参
和封装参数
和选中行时往sels里面传值
需要注意的是 传值的时候 Controller层需要加上
然后编写SQL语句的时候
这里直接写参数
自此 课程模块就已经写完了
今天的内容就到这啦