今日任務
課程的CRUD
關於數據庫的存儲
有兩種方案
方案一:
像課程詳情 課程圖片 都是數據課程的信息 可知直接放入t_course 就ok
方案二:
有時候我們只需要查詢到課程的基本信息 不需要顯示圖片和詳情 這個時候我們就會用到垂直分表
垂直分表:一些字段我們一般不需要直接查詢 就把這些字段單獨放一個表 通過外鍵關聯 正常情況下 不需要查詢關聯表 如果需要 就通過外鍵查詢就ok 這樣能大大提高數據庫的效率
生成代碼
生成後測試是否已經加入
然後從我們的資料中拷貝Course.vue界面
在這之前 我們需要集成一個支持Vue的富文本編輯器
直接在終端運行
npm install quill --save
npm install --save vue-quill-editor
然後把list一系列的修改了 和之前一樣
然後啓動前後端服務
然後可能會報一個這樣的錯
別擔心 這是因爲你的redis沒有啓動起來 將redis啓動就行了
這與頁面就能出來了
接下來我們來做新增操作
頁面都已經寫好 我們只需要拷貝下來用就是了 因爲我不是專業的前端 所以我們就直接用別人寫好的就行了
後臺:
我們需要修改的地方是 覆寫service中的插入和修改方法 因爲我們這個是有關聯對象的 所以自帶的方法已經滿足不了我們了
@Override
public boolean insert(Course entity) {
//要添加三張表 課程表 詳情表 市場信心表
//tenantId tenantName userId userName
System.out.println(entity);
entity.setStatus(0);
courseMapper.insert(entity);
Long courseId = entity.getId();
//同時保存詳情和市場信心
CourseDetail courseDetail = entity.getCourseDetail();
courseDetail.setCourseId(courseId);
courseDetailMapper.insert(courseDetail);
CourseMarket courseMarket = entity.getCourseMarket();
courseMarket.setCourseId(courseId);
courseMarketMapper.insert(courseMarket);
return true;
}
對於Controller 在我麼登錄之後 需要傳遞一些信息進去 但是我們現在還用不到 所以就先註釋掉
@PutMapping
public AjaxResult addOrUpdate(@RequestBody Course course){
try {
// @TODO 以後登錄成功都能獲取,現在使用holder來模擬
//登錄成功後設置到UserInfoHolder,以後所有模塊要使用都直接使用UserInfoHolder
// course.setTenantId(UserInfoHolder.getTenant().getId());
// course.setTenantName(UserInfoHolder.getTenant().getCompanyName());
// course.setUserId(UserInfoHolder.getLoginUser().getId());
// course.setUserName(UserInfoHolder.getLoginUser().getUsername());
if(course.getId()!=null){
courseService.updateById(course);
}else{
courseService.insert(course);
}
return AjaxResult.me();
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.me().setMessage("保存對象失敗!"+e.getMessage());
}
}
修改和刪除我就不在這展示了 和之前的都大同小異
課程的上線和下線
業務說明
上線:
現實生活中:
當培訓機構研發出新的課程後,準備招生時,要先把它添加到數據中。但是期望暫時不需要用戶能夠搜索到。所以需要上線才能操作
系統中:
在系統中,我們添加了一個課程,用戶不立即解就搜索到,需要上線以後才行.
下線:
當某個課程不想賣的時候,就要下線. 當課程下線後,用戶不能搜索到,但是數據庫是還有的.
方案-用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的設計
準備環境 -在自己模塊
導包
<!--springboot 對spring data es支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
配置文件
spring:
data:
elasticsearch:
cluster-name: elasticsearch
cluster-nodes: 127.0.0.1:9300 #9200是圖形界面端,9300代碼端
入口類
@SpringBootApplication
public class ESApplication {
public static void main(String[] args) {
SpringApplication.run(ESApplication.class);
}
}
EsCourse.java
//包含了前臺展示的字段和要添加到索引庫都要寫到這兒
@Document(indexName = "hrm",type = "course")
public class EsCourse {
@Id
private Long id;
private String name;
private String users;
private Long courseTypeId;
private String courseTypeName;
private Long gradeId;
private String gradeName;
private Integer status;
private Long tenantId;
private String tenantName;
private Long userId;
private String userName;
private Date startTime;
private Date endTime;
private String intro;
private String resources; //圖片
private Date expires; //過期時間
private BigDecimal priceOld; //原價
private BigDecimal price; //原價
private String qq; //原價
@Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
private String all;
//getter和setter
//toString
public String getAll() {
String tmp = name
+" "+ users
+" "+ courseTypeName
+" "+ gradeName
+" "+ tenantName
+" "+ userName
+" "+ intro;
return tmp;
}
EsCourseRepository.java
import org.leryoo.index.doc.EsCourse;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
//在service通過注入它就可以完成索引庫操作了
public interface EsCourseRepository extends ElasticsearchRepository<EsCourse,Long> {
}
接下來什麼地方要用到就直接調用就行了 和操作數據庫的方法都差不多
Service層 業務邏輯
上線
@Override
public AjaxResult onLine(Long[] ids) {
try
{
//1添加索引庫
// 通過id查詢數據庫裏面課程
List<Course> courses = courseMapper
.selectBatchIds(Arrays.asList(ids));
//轉換爲doc
List<EsCourse> esCourses = courses2EsCourses(courses);
//批量添加就ok
repository.saveAll(esCourses);
//2 修改數據庫狀態和上線時間 - ids,time
Map<String,Object> params = new HashMap<>();
params.put("ids",ids);
params.put("onLineTime",new Date());
// 修改狀態和上線時間
//update t_couse set status=1 and start_time={} where id in (1,2,3,4)
courseMapper.onLine(params);
return AjaxResult.me();
}catch (Exception e){
e.printStackTrace();
return AjaxResult.me().setSuccess(false).setMessage("上線失敗!"+e.getMessage());
}
}
private List<EsCourse> courses2EsCourses(List<Course> courses) {
//1 聲明要返回
List<EsCourse> result = new ArrayList<>();
//2 轉換
for (Course course : courses) {
result.add(course2EsCourse(course));
}
//3返回
return result;
}
//把一個course轉換爲EsCourse
private EsCourse course2EsCourse(Course course) {
//1 聲明要返回
EsCourse result = new EsCourse();
// 2 轉換
result.setId(course.getId());
result.setName(course.getName());
result.setUsers(course.getUsers());
result.setCourseTypeId(course.getCourseTypeId());
//type-同庫-沒有做關聯查詢
if (course.getCourseType() != null)
result.setCourseTypeName(course.getCourseType().getName());
result.setGradeId(course.getGrade());
result.setGradeName(course.getGradeName());
result.setStatus(course.getStatus());
result.setTenantId(course.getTenantId());
result.setTenantName(course.getTenantName());
result.setUserId(course.getUserId());
result.setUserName(course.getUserName());
result.setStartTime(course.getStartTime());
result.setEndTime(course.getEndTime());
//Detail
result.setIntro(null);
//resource
result.setResources(null);
//market
result.setExpires(null);
result.setPrice(null);
result.setPriceOld(null);
result.setQq(null);
// 3返回
return result;
}
下線:
@Override
public AjaxResult offLine(Long[] ids) {
try
{
//1 刪除索引庫裏面的內容
// List<Course> courses = courseMapper
// .selectBatchIds(Arrays.asList(ids));
//轉換爲doc
// List<EsCourse> esCourses = courses2EsCourses(courses);
// repository.deleteAll(esCourses);
for (Long id : ids) {
repository.deleteById(id);
}
//2 修改數據庫狀態 status=0 end_time
Map<String,Object> params = new HashMap<>();
params.put("ids",ids);
params.put("offLineTime",new Date());
// 修改狀態和下線時間
//update t_couse set status=0 and start_time={} where id in (1,2,3,4)
courseMapper.offLine(params);
return AjaxResult.me();
}catch (Exception e){
e.printStackTrace();
return AjaxResult.me().setSuccess(false).setMessage("下線失敗!"+e.getMessage());
}
}
SQL語句
<!-- 上線-->
<!-- void onLine(Map<String, Object> params);-->
<update id="onLine" parameterType="map">
update t_course set status=1,start_time=#{onLineTime} where id in
<foreach collection="ids" separator="," open="(" close=")" item="id">
#{id}
</foreach>
</update>
<!-- void offLine(Map<String, Object> params);-->
<update id="offLine" parameterType="map">
update t_course set status=0 , end_time=#{offLineTime} where id in
<foreach collection="ids" separator="," open="(" close=")" item="id">
#{id}
</foreach>
</update>
注意前端傳參
和封裝參數
和選中行時往sels裏面傳值
需要注意的是 傳值的時候 Controller層需要加上
然後編寫SQL語句的時候
這裏直接寫參數
自此 課程模塊就已經寫完了
今天的內容就到這啦