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. Java中 equals和==的区分, new Integer和 非new的区别

    浅谈 equals 和 == ,new出的Integer和非new出的Integer 首先我们要知道在 == 比较的是内存地址值(不包括8种基本数据类型) equals比较的是两个值(内容)是否相同. ...

  2. Ubuntu 16.4安装1050ti显卡驱动问题及解决

    不得不说,ubuntu上安装nvidia驱动坑是真的多!!! 本来在之前已经安装好了,从 显卡驱动到cuda再到cudnn去不没问题,就已一次推送而来的显卡驱动更新,把我的环境全部弄坏,为了吸取教训, ...

  3. Linux主流发行版本配置IP总结(Ubuntu、CentOS、Redhat、Suse)

    我们先了解下IP的概念 IP地址简介 电脑连接互联网的必要条件:IP地址+子网掩码+网关+DNS IP地址是上网的唯一标识 - IPv4地址分类: IPv4地址分为A-E共计5类地址,其中A.B.C是 ...

  4. Python自定义排序及我实际遇到的一些题目实例

    写在前面,本文主要介绍Python基础排序和自定义排序的一些规则,如果都比较熟悉,可以直接翻到第三节,看下实际的笔试面试题中关于自定义排序的应用. 一.基础排序 排序是比较基础的算法,与很多语言一样, ...

  5. 论文解读(SAGPool)《Self-Attention Graph Pooling》

    论文信息 论文标题:Self-Attention Graph Pooling论文作者:Junhyun Lee, Inyeop Lee, Jaewoo Kang论文来源:2019, ICML论文地址:d ...

  6. 一个程序的自我修养「GitHub 热点速览 v.22.19」

    一个程序要诞生涉及前后端技术,比如,你可以用可视化网页搭建工具 tmagic-editor 完成前端部分,而后端部分的数据库以及数据处理可能就要用到 jsonhero-web 和 directus.知 ...

  7. 通过有序线性结构构造AVL树

    通过有序线性结构构造AVL树 本博客旨在结局利用有序数组和有序链表构造平衡二叉树(下文使用AVL树代指)问题. 直接通过旋转来构造AVL树似乎是一个不错的选择,但是稍加分析就会发现,这样平白无故做了许 ...

  8. ElasticSearch7.3学习(二十九)----聚合实战之使用Java api实现电视案例

    一.数据准备 创建索引及映射 建立价格.颜色.品牌.售卖日期字段 PUT /tvs PUT /tvs/_mapping { "properties": { "price& ...

  9. Django对接支付宝Alipay支付接口

    最新博客更新见我的个人主页: https://xzajyjs.cn 我们在使用Django构建网站时常需要对接第三方支付平台的支付接口,这里就以支付宝为例(其他平台大同小异),使用支付宝开放平台的沙箱 ...

  10. synchronized下的 i+=2 和 i++ i++执行结果居然不一样

    起因 逛[博客园-博问]时发现了一段有意思的问题: 问题链接:https://q.cnblogs.com/q/140032/ 这段代码是这样的: import java.util.concurrent ...