13. 类别管理--根据id查询类别详情--持久层

13.1. 规划SQL语句

本次需要执行的SQL语句大致是:

select * from pms_category where id=?

关于字段列表,应该包括:

id, name, parent_id, depth, keywords, sort, icon, enable, is_parent, is_display

13.2. 抽象方法(可能需要创建VO类)

csmall-pojo的根包下的vo包下创建CategoryDetailsVO类,封装以上设计的字段对应的属性:

package cn.tedu.csmall.pojo.vo;

import lombok.Data;

import java.io.Serializable;

@Data
public class CategoryDetailsVO implements Serializable { private Long id;
private String name;
private Long parentId;
private Integer depth;
private String keywords;
private Integer sort;
private String icon;
private Integer enable;
private Integer isParent;
private Integer isDisplay; }

CategoryMapper接口中添加:

CategoryDetailsVO getDetailsById(Long id);

13.3. 在XML中配置SQL

CategoryMapper.xml中添加配置:

<!-- CategoryDetailsVO getDetailsById(Long id); -->
<select id="getDetailsById" resultMap="DetailsResultMap">
select
<include refid="DetailsQueryFields"/>
from
pms_category
where
id=#{id}
</select> <sql id="DetailsQueryFields">
<if test="true">
id, name, parent_id, depth, keywords,
sort, icon, enable, is_parent, is_display
</if>
</sql> <resultMap id="DetailsResultMap" type="cn.tedu.csmall.pojo.vo.CategoryDetailsVO">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="parent_id" property="parentId" />
<result column="depth" property="depth" />
<result column="keywords" property="keywords" />
<result column="sort" property="sort" />
<result column="icon" property="icon" />
<result column="enable" property="enable" />
<result column="is_parent" property="isParent" />
<result column="is_display" property="isDisplay" />
</resultMap>

13.4. 测试

@Test
@Sql({"classpath:truncate.sql", "classpath:insert_data.sql"})
public void testGetDetailsByIdSuccessfully() {
// 测试数据
Long id = 1L;
// 断言不会抛出异常
assertDoesNotThrow(() -> {
// 执行查询
Object category = mapper.getDetailsById(id);
// 断言查询结果不为null
assertNotNull(category);
});
} @Test
@Sql({"classpath:truncate.sql"})
public void testGetDetailsByIdFailBecauseNotFound() {
// 测试数据
Long id = -1L;
// 断言不会抛出异常
assertDoesNotThrow(() -> {
// 执行查询
Object category = mapper.getDetailsById(id);
// 断言查询结果为null
assertNull(category);
});
}

14. 类别管理--根据id查询类别详情--业务逻辑层

14.1. 接口和抽象方法

ICategoryService中添加:

CategoryDetailsVO getDetailsById(Long id);

14.2. 实现

CategoryServiceImpl中执行查询并返回。


14.3. 测试

@Test
@Sql({"classpath:truncate.sql", "classpath:insert_data.sql"})
public void testGetDetailsByIdSuccessfully() {
// 测试数据
Long id = 1L;
// 断言不抛出异常
assertDoesNotThrow(() -> {
service.getDetailsById(id);
});
} @Test
@Sql({"classpath:truncate.sql"})
public void testGetDetailsByIdFailBecauseNotFound() {
// 测试数据
Long id = -1L;
// 断言抛出异常
assertThrows(ServiceException.class, () -> {
service.getDetailsById(id);
});
}

15. 类别管理--根据id查询类别详情--控制器层

CategoryController中添加:

@GetMapping("/{id}")
public JsonResult<CategoryDetailsVO> getDetailsById(@PathVariable Long id) {
CategoryDetailsVO category = categoryService.getDetailsById(id);
return JsonResult.ok(category);
}

CategoryControllerTests中测试:

@Test
@Sql({"classpath:truncate.sql", "classpath:insert_data.sql"})
public void testGetDetailsByIdSuccessfully() throws Exception {
// 准备测试数据,注意:此次没有提交必要的name属性值
String id = "1";
// 请求路径,不需要写协议、服务器主机和端口号
String url = "/categories/" + id;
// 执行测试
// 以下代码相对比较固定
mockMvc.perform( // 执行发出请求
MockMvcRequestBuilders.get(url) // 根据请求方式决定调用的方法
.contentType(MediaType.APPLICATION_FORM_URLENCODED) // 请求数据的文档类型,例如:application/json; charset=utf-8
.accept(MediaType.APPLICATION_JSON)) // 接收的响应结果的文档类型,注意:perform()方法到此结束
.andExpect( // 预判结果,类似断言
MockMvcResultMatchers
.jsonPath("state") // 预判响应的JSON结果中将有名为state的属性
.value(State.OK.getValue())) // 预判响应的JSON结果中名为state的属性的值,注意:andExpect()方法到此结束
.andDo( // 需要执行某任务
MockMvcResultHandlers.print()); // 打印日志
} @Test
@Sql({"classpath:truncate.sql", "classpath:insert_data.sql"})
public void testGetDetailsByIdFailBecauseNotFound() throws Exception {
// 准备测试数据,注意:此次没有提交必要的name属性值
String id = "9999999999";
// 请求路径,不需要写协议、服务器主机和端口号
String url = "/categories/" + id;
// 执行测试
// 以下代码相对比较固定
mockMvc.perform( // 执行发出请求
MockMvcRequestBuilders.get(url) // 根据请求方式决定调用的方法
.contentType(MediaType.APPLICATION_FORM_URLENCODED) // 请求数据的文档类型,例如:application/json; charset=utf-8
.accept(MediaType.APPLICATION_JSON)) // 接收的响应结果的文档类型,注意:perform()方法到此结束
.andExpect( // 预判结果,类似断言
MockMvcResultMatchers
.jsonPath("state") // 预判响应的JSON结果中将有名为state的属性
.value(State.ERR_CATEGORY_NOT_FOUND.getValue())) // 预判响应的JSON结果中名为state的属性的值,注意:andExpect()方法到此结束
.andDo( // 需要执行某任务
MockMvcResultHandlers.print()); // 打印日志
}

16. 使用Redis

Redis是一款基于内存的NoSQL数据存储服务,是非关系型的,是使用K-V结构进行存储的

  • 基于内存:读写数据均在内存中直接操作
  • NoSQL:通常把能够存、取数据的服务都称之为数据库,所以,Redis也是数据库,但是,它没有SQL语句

在基于Spring Boot的开发中,当需要在程序中访问Redis中的数据时,需要添加spring-boot-starter-data-redis依赖项。

要操作Redis中的数据,需要使用RedisTemplate对象,则在csmall-product-webapi的根包下的config包中创建RedisConfiguration类,并在其中进行配置:

@Configuration
public class RedisConfiguration { @Bean
public RedisTemplate<String, Serializable> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setValueSerializer(RedisSerializer.json());
return redisTemplate;
} }

接下来,在测试的根包下创建RedisTests来测试访问Redis中的数据:

@SpringBootTest
public class RedisTests { @Autowired
RedisTemplate<String, Serializable> redisTemplate; @Test
void testSetValue() {
redisTemplate.opsForValue()
.set("name", "liuguobin");
} @Test
void testSetValueTTL() {
redisTemplate.opsForValue()
.set("name", "fanchuanqi", 60, TimeUnit.SECONDS);
} @Test
void testSetObjectValue() {
CategoryDetailsVO category = new CategoryDetailsVO();
category.setId(65L);
category.setIsParent(1);
category.setDepth(1);
category.setName("水果");
redisTemplate.opsForValue()
.set("category", category);
} @Test
void testGetValue() {
// 当key存在时,可获取到有效值
// 当key不存在时,获取到的结果将是null
Serializable name = redisTemplate.opsForValue()
.get("name");
System.out.println("get value --> " + name);
} @Test
void testGetObjectValue() {
// 当key存在时,可获取到有效值
// 当key不存在时,获取到的结果将是null
Serializable serializable = redisTemplate.opsForValue()
.get("category");
System.out.println("get value --> " + serializable);
if (serializable != null) {
CategoryDetailsVO category = (CategoryDetailsVO) serializable;
System.out.println("get value --> " + category);
}
} @Test
void testDeleteKey() {
// 删除key时,将返回“是否成功删除”
// 当key存在时,将返回true
// 当key不存在时,将返回false
Boolean result = redisTemplate.delete("name");
System.out.println("result --> " + result);
} @Test
void testRightPushList() {
// 存入List时,需要redisTemplate.opsForList()得到针对List的操作器
// 通过rightPush()可以向Redis中的List追加数据
// 每次调用rightPush()时使用的key必须是同一个,才能把多个数据放到同一个List中
List<CategoryDetailsVO> list = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
CategoryDetailsVO category = new CategoryDetailsVO();
category.setName("类别00" + i);
list.add(category);
} String key = "categoryList";
for (CategoryDetailsVO category : list) {
redisTemplate.opsForList().rightPush(key, category);
}
} @Test
void testListSize() {
// 获取List的长度,即List中的元素数量
String key = "categoryList";
Long size = redisTemplate.opsForList().size(key);
System.out.println("size --> " + size);
} @Test
void testRange() {
// 调用opsForList()后再调用range(String key, long start, long end)方法取出List中的若干个数据,将得到List
// long start:起始下标(结果中将包含)
// long end:结束下标(结果中将包含),如果需要取至最后一个元素,可使用-1作为此参数值
String key = "categoryList";
List<Serializable> range = redisTemplate.opsForList().range(key, 0, -1);
for (Serializable serializable : range) {
System.out.println(serializable);
}
} @Test
void testKeys() {
// 调用keys()方法可以找出匹配模式的所有key
// 在模式中,可以使用星号作为通配符
Set<String> keys = redisTemplate.keys("*");
for (String key : keys) {
System.out.println(key);
}
} }

最后,关于Key的使用,通常建议使用冒号区分多层次,类似URL的设计方式,例如:

  • 类别列表的Key:categories:listcategories
  • 某个id(9527)对应的类别的Key:categories:item:9527

4-10 CS后台项目练习-3 || Redis的更多相关文章

  1. 4-11 CS后台项目-4 及 Redis缓存数据

    使用Redis缓存数据 使用Redis可以提高查询效率,一定程度上可以减轻数据库服务器的压力,从而保护了数据库. 通常,应用Redis的场景有: 高频查询,例如:热搜列表.秒杀 改变频率低的数据,例如 ...

  2. 4-7 CS后台项目练习-1

    1. 关于此项目 此项目是一个自营性质电商类型的项目. 当前目标是设计后台管理相关功能. 2. 关于项目的开发流程 开发项目的标准流程应该有:需求分析.可行性分析.总体设计.详细设计等. 建议课后学习 ...

  3. 4-8 CS后台项目练习-2

    8. 类别管理--添加类别--持久层 8.1. 配置 续前日,无新增 8.2. 规划需要执行的SQL语句 续前日,无新增 8.3. 接口与抽象方法 此前需要执行的SQL语句大致是: select id ...

  4. Redis的安装以及在项目中使用Redis的一些总结和体会

    第一部分:为什么我的项目中要使用Redis 我知道有些地方没说到位,希望大神们提出来,我会吸取教训,大家共同进步! 注册时邮件激活的部分使用Redis 发送邮件时使用Redis的消息队列,减轻网站压力 ...

  5. CS通用项目系统搭建——三层架构第一天

    CS通用项目:使用三层架构进行搭建 三层架构: 表现层(UI(User Interface)):展示给用户的层面,包含窗体控件数据等信息. 业务逻辑层(BLL(Business Logic Layer ...

  6. docker部署项目: centos+python+redis+mysql+uwsgi+nginx

    一.Centos7安装docker 1.1 环境配置 先测试是否下载了docker:查看镜像:docker images没有下载,就依次执行以下环境的安装 ①curl http://mirrors.a ...

  7. 在项目中部署redis的读写分离架构(包含节点间认证口令)

    #### 在项目中部署redis的读写分离架构(包含节点间认证口令) ##### 1.配置过程 ---  1.此前就是已经将redis在系统中已经安装好了,redis utils目录下,有个redis ...

  8. Spring-Boot项目中配置redis注解缓存

    Spring-Boot项目中配置redis注解缓存 在pom中添加redis缓存支持依赖 <dependency> <groupId>org.springframework.b ...

  9. php大力力 [039节] 修改一下后台项目,同时启用印象笔记,要做的事情todo列表,记录在印象笔记,速度快一些

    php大力力 [039节]  修改一下后台项目,同时启用印象笔记,要做的事情todo列表,记录在印象笔记,速度快一些

随机推荐

  1. centos 7.0 下安装FFmpeg软件 过程

    这几天由于需要编写一个语音识别功能,用到了百度语音识别接口,从web端或小程序端传上来的音频文件是aac或者mp3或者wav格式的,需要使用FFmpeg进行格式转换,以符合百度api的要求. 安装FF ...

  2. Prometheus监控实战应用

    目录 1.Prometheus的主要特征及概述 2.普罗米修斯原理架构图 3.下载安装启动Prometheus 4.web客户端操作 5.默认图像 6.目录解释 7.配置文件 8.监控指标 8.1.监 ...

  3. Docker系列教程01-使用Docker镜像

    docker系列导读 一文带你读懂什么是docker Docker安装部署 10张图带你深入理解Docker容器和镜像 前言 学习Docker,我们需要掌握它的三大核心概念:镜像.容器和仓库. 今天先 ...

  4. FinClip小程序+Rust(三):一个加密钱包

    ​ 一个加密货币钱包,主要依赖加密算法构建.这部分逻辑无关iOS还是Android,特别适合用Rust去实现.我们看看如何实现一个生成一个模拟钱包,准备供小程序开发采用 前言 在之前的内容我们介绍了整 ...

  5. Go基础3:函数、结构体、方法、接口

    目录 1. 函数 1.1 函数返回值 同一种类型返回值 带变量名的返回值 函数中的参数传递 函数变量 1.2 匿名函数--没有函数名字的函数 在定义时调用匿名函数 将匿名函数赋值给变量 匿名函数用作回 ...

  6. 我被冻在了 vue2 源码工具函数第一行Object.freeze()(一)

    前言 最近参加若川的源码共度活动,第 24 期 vue2 源码工具函数,最开始: var emptyObject = Object.freeze({}); 之前知道 Object.freeze() 是 ...

  7. http协议与html

    目录 前端 HTTP协议 HTML简介 head内常见标签 body内基本标签 body内基本标签 特殊字符 布局标签(div.span) 图片标签(img) 超链接标签(a) 标签的两大重要参数(i ...

  8. python之部分内置函数与迭代器与异常处理

    目录 常见内置函数(部分) 可迭代对象 迭代器对象 for循环内部原理 异常处理 异常信息的组成部分 异常的分类 异常处理实操 异常处理的其他操作 for循环本质 迭代取值与索引取值的区别 常见内置函 ...

  9. 【单片机】NB-IoT移远BC28调试笔记

    一.入网总体思路 入网思路是参考 <Quectel_BC95&BC35-G&BC28_应用设计指导_V1.1.pdf>来做的.流程如图所示: 二.具体调试细节3.1 AT+ ...

  10. Excel导表工具-开源

    功能 支持int.float.bool.string基础类型 支持数组 支持kv 支持枚举 支持unity类型vector3,vector2,color 自动生成csharp类 单个excel中多个s ...