第二章-查询与缓存

本文探讨了微服务架构下的查询与缓存处理,详细介绍了条件查询、分页查询及热门、最新数据的快速检索策略。同时,深入解析了Redis在提高查询性能中的作用,以及Spring Data Redis和Spring Cache的使用方法,展示了如何在微服务中实施有效的缓存策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第二章-查询与缓存

1. 基础微服务条件查询

1.1 标签-条件查询

POST /label/search 根据条件查询城市列表

  1. 修改LabelService ,增加方法

    /**
     * 根据条件查询
     * @param label
     * @return
     */
    public List<Label> findSearch(Label label) {
        return labelDao.findAll(createSpecification(label));
    }

    /**
     * 动态条件构建
     * @param label
     * @return
     */
    private Specification<Label> createSpecification(Label label) {
        return new Specification<Label>() {
            @Override
            public Predicate toPredicate(Root<Label> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                List<Predicate> predicateList = new ArrayList<Predicate>();
                //new一个list集合。来存放所有的条件
                List<Predicate> list = new ArrayList<>();
                if (label.getLabelname() != null && !"".equals(label.getLabelname())) {
                    Predicate predicate = cb.like(root.get("labelname").as(String.class), "%" + label.getLabelname() + "%");// labelname like "%小明%"
                    list.add(predicate);
                }
                if (label.getState() != null && !"".equals(label.getState())) {
                    Predicate predicate = cb.equal(root.get("state").as(String.class), label.getState());// state = "1"
                    list.add(predicate);
                }
                //new一个数组作为最终返回值的条件
                Predicate[] parr = new Predicate[list.size()];
                //把list直接转成数组
                parr = list.toArray(parr);
                return cb.and(parr);// where labelname like "%小明%" and state = "1"
            }
        };
    }

  1. 修改LabelController,增加方法
    /**
     * 根据条件查找
     * @param label
     * @return
     */
    @PostMapping("/search")
    public Result findSearch(@RequestBody Label label){
        List<Label> list = labelService.findSearch(label);
        return new Result(true, StatusCode.OK, "查询成功", list);
    }

1.2 带分页的条件查询

  1. 修改LabelService,增加方法
  /**
     * 带分页的条件查询
     * @param label
     * @param page
     * @param size
     * @return
     */
    public Page<Label> pageQuery(Label label, int page, int size) {
        //封装分页对象
        Pageable pageable = PageRequest.of(page-1, size);
        return labelDao.findAll(createSpecification(label), pageable);
    }
  1. 修改LabelController,增加方法
    /**
     * 根据条件查询带分页
     * @param label
     * @param page
     * @param size
     * @return
     */
    @PostMapping("/search/{page}/{size}")
    public Result pageQuery(@RequestBody Label label, @PathVariable int page, @PathVariable int size){
        Page<Label> pageData = labelService.pageQuery(label, page, size);
        return new Result(true, StatusCode.OK, "查询成功", new PageResult<Label>(pageData.getTotalElements(), pageData.getContent()));
    }

2. 招聘微服务开发

2.1 表结构分析

招聘微服务包括 企业信息招聘信息

  • 企业表 tb_enterprise
字段名称字段含义字段类型备注
idID文本
name企业名称文本
summary企业简介文本
address企业地址文本
labels标签列表文本用逗号分隔
coordinate企业位置坐标文本经度,纬度
ishot是否热门文本0:非热门 1:热门
logoLOGO文本
jobcount职位数数字
urlURL文本
  • 招聘信息表 tb_recruit
字段名称字段含义字段类型备注
idID文本
jobname招聘职位文本
salary薪资范围文本
condition经验要求文本
education学历要求文本
type任职方式文本
address办公地址文本
eid企业ID文本
createtime发布日期日期
state状态文本0:关闭 1:开启

2.2 代码生成

  • (1) 使用代码生成器生成招聘微服务代码 tensquare_recruit
  • (2)拷贝到当前工程,并在父工程引入。
  • (3) 修改Application类名称为RecruitApplication
  • (4)修改application.yml 中的端口为9002 ,url 为

jdbc:mysql://localhost:3306/tensquare_recruit?characterEncoding=UTF8

  • (5)进行浏览器测试

2.3 代码编写

2.3.1 热门企业列表

查询企业表ishot字段为1的记录

  • (1)EnterpriseDao新增方法定义
/**
* 根据热门状态获取企业列表,这个通过JPA自动实现了
* @param ishot
* @return
*/
public List<Enterprise> findByIshot(String ishot);
  • (2)EnterpriseService新增方法
/**
* 热门企业列表
* @return
*/
public List<Enterprise> hotlist(){
return enterpriseDao.findByIshot("1");
}
  • (3)EnterpriseController新增方法
/**
	 * 查询热门企业
	 * @return
	 */
	@GetMapping("/search/hotlist")
	public Result hotlist(){
		return new Result(true, StatusCode.OK,"查询成功",enterpriseService.hotlist());
	}
  • (4)测试 http://localhost:9002/enterprise/search/hotlist
2.3.2 推荐职位列表

需求分析: 查询状态为2并以创建日期降序排序,查询前6条记录

  • (1) 在RecruitDao新增方法定义
     /**
     * 查询最新职位列表按照 state 来查询(按照创建日期降序)
     * @param state
     * @return
     */
     List<Recruit> findTop6ByStateOrderByCreatetimeDesc(String state);//where state=? order by createime

  • (2) RecruitService新增方法
	/**
	 * 根据状态查询
	 * @param state
	 * @return
	 */
	public List<Recruit> findTop6ByStateOrderByCreatetimeDesc(String  state){
		return recruitDao.findTop6ByStateOrderByCreatetimeDesc(state);
	}
  • (3) RecruitController新增方法
	@GetMapping("/search/recommend")
	public Result recommend(){
		List<Recruit> list = recruitService.findTop6ByStateOrderByCreatetimeDesc("2");
		return new Result(true,StatusCode.OK,"查询成功",list);
	}
  • (4) 测试 http://localhost:9002/recruit/search/recommend
2.3.3 最新职位列表

需求分析:查询状态不为0并以创建日期降序排序,查询前6条记录

  • (1) 在RecruitDao新增方法定义
    /**
     * 查询最新职位列表不按照 state 来查询(按照创建日期降序)查询状态不为0并以创建日期降序排序,查询前12条记录
     * @param state
     * @return
     */
     List<Recruit> findTop6ByStateNotOrderByCreatetimeDesc(String state);//where state!=? order by createime

  • (2) RecruitService新增方法
 	/**
	 * 最新职位列表
	 * @return
	 */
	public List<Recruit> newlist(){
		return recruitDao.findTop6ByStateNotOrderByCreatetimeDesc("0");
	}
  • (3) RecruitController新增方法
	/**
	 * 最新职位列表
	 * @return
	 */
	@GetMapping("/search/newlist")
	public Result newlist(){
		return new Result(true,StatusCode.OK,"查询成功",recruitService.newlist());
	}
  • (4)测试 http://localhost:9002/recruit/search/newlist

3. 问答微服务开发

3.1 表结构分析

  • 招聘信息表 tb_recruit
字段名称字段含义字段类型备注
idID文本
title问题标题文本
content问题内容文本
createtime发布日期日期
updatetime更新日期日期
userid发布人ID文本
nickname发布人昵称文本
visits浏览量整型
thumbup点赞数整型
reply回复数整型
solve是否解决文本
replyname最新回复人文本
replytime最新回复时间日期

3.2 代码生成

  • (1)使用代码生成器生成招聘微服务代码 tensquare_qa
  • (2)拷贝到当前工程,并在父工程引入。
  • (3)修改Application类名称为QaApplication
  • (4)修改application.yml 中的端口为9003 ,url 为
    jdbc:mysql://localhost:3306/tensquare_qa?characterEncoding=UTF8
  • (5) 进行浏览器测试

3.3 代码编写

3.3.1 最新回答列表

需求分析:最新回复的问题显示在上方, 按回复时间降序排序

  • (1) ProblemDao新增方法定义
    /**
     * 根据标签ID查询最新问题列表
     * @param labelid
     * @param pageable
     * @return
     */
    @Query(value = "SELECT * FROM tb_problem, tb_pl WHERE id = problemid AND labelid=? ORDER BY replytime DESC", nativeQuery = true)
    public Page<Problem> newlist(String labelid, Pageable pageable);

  • (2) ProblemService新增方法
	/**
	 * 根据标签ID查询问题列表
	 * @param lableId 标签ID
	 * @param page 页码
	 * @param size 页大小
	 * @return
	 */
	public Page<Problem> findNewListByLabelId(String lableId,int page,int size) {
		PageRequest pageRequest = PageRequest.of(page-1, size);
		return problemDao.newlist(lableId,pageRequest);
	}
  • (3) ProblemController新增方法
	/**
	 * 根据标签ID查询最新问题列表
	 * @param labelid
	 * @return
	 */
	@GetMapping("/newlist/{labelid}/{page}/{size}")
	public Result findNewListByLabelId(@PathVariable String labelid,@PathVariable int page,@PathVariable int size ){
		Page<Problem> pageList = problemService.findNewListByLabelId(labelid, page, size);
		PageResult<Problem> pageResult = new PageResult<>(pageList.getTotalElements(), pageList.getContent());
		return new Result(true, StatusCode.OK, "查询成功",pageResult);
	}
  • (4) 测试 http://localhost:9003//problem/newlist/{labelid}/{page}/{size}
3.3.2 热门问答列表
  • (1) ProblemDao新增方法定义
    /**
     * 热门问题列表
     * @param labelid
     * @param pageable
     * @return
     */
    @Query(value = "SELECT * FROM tb_problem, tb_pl WHERE id = problemid AND labelid=? ORDER BY reply DESC", nativeQuery = true)
    public Page<Problem> hotlist(String labelid, Pageable pageable);
  • (2) ProblemService新增方法
	/**
	 * 根据标签ID查询热门问题列表
	 * @param lableId 标签ID
	 * @param page 页码
	 * @param size 页大小
	 * @return
	 */
	public Page<Problem> findHotListByLabelId(String lableId,int page,int size) {
		PageRequest pageRequest = PageRequest.of(page-1, size);
		return problemDao.hotlist(lableId,pageRequest);
	}
  • (3) ProblemController新增方法
	/**
	 * 根据标签ID查询热门问题列表
	 * @param labelid 标签ID
	 * @param page 页码
	 * @param size 页大小
	 * @return
	 */
	@GetMapping("/hotlist/{labelid}/{page}/{size}")
	public Result findHotListByLabelId(@PathVariable String labelid,@PathVariable int page,@PathVariable int size ){
		Page<Problem> pageList = problemService.findHotListByLabelId(labelid, page, size);
		PageResult<Problem> pageResult = new PageResult<>(pageList.getTotalElements(), pageList.getContent());
		return new Result(true, StatusCode.OK, "查询成功",pageResult);
	}
  • (4) 测试 ``
3.3.3 等待问答列表
  • (1) ProblemDao新增方法定义
    /**
     * 等待回答问题列表
     * @param labelid
     * @param pageable
     * @return
     */
    @Query(value = "SELECT * FROM tb_problem, tb_pl WHERE id = problemid AND labelid=? AND reply=0 ORDER BY createtime DESC", nativeQuery = true)
    Page<Problem> waitlist(String labelid, Pageable pageable);
  • (2) ProblemService新增方法
	/**
	 * 根据标签ID查询等待问题列表
	 * @param lableId 标签ID
	 * @param page 页码
	 * @param size 页大小
	 * @return
	 */
	public Page<Problem> findWaitListByLabelId(String lableId,int page,int size) {
		PageRequest pageRequest = PageRequest.of(page-1, size);
		return problemDao.waitlist(lableId,pageRequest);
	}
  • (3) ProblemController新增方法
	/**
	 * 根据标签ID查询等待问题列表
	 * @param labelid 标签ID
	 * @param page 页码
	 * @param size 页大小
	 * @return
	 */
	@GetMapping("/waitlist/{labelid}/{page}/{size}")
	public Result findWaitListByLabelId(@PathVariable String labelid,@PathVariable int page,@PathVariable int size ){
		Page<Problem> pageList = problemService.findWaitListByLabelId(labelid, page, size);
		PageResult<Problem> pageResult = new PageResult<>(pageList.getTotalElements(), pageList.getContent());
		return new Result(true, StatusCode.OK, "查询成功",pageResult);
	}
  • (4) 测试 ``

4. 文章微服务开发

4.1 表结构分析

  • 招聘信息表 tb_article
字段名称字段含义字段类型备注
idID 文本
columnid专栏ID文本
userid用户ID文本
title文章标题文本
content文章内容文本
image文章封面文本
createtime发表日期日期
updatetime修改日期日期
ispublic是否公开文本0:不公开 1:公开
istop是否置顶文本0:不置顶 1:置顶
visits浏览量整型
thumbup点赞数整型
comment评论数整型
state审核状态文本0:未审核 1:已审核
channelid所属频道整型关联频道表ID
urlURL地址文本
type文章类型文本0:分享 1:专栏

4.2 代码生成

  • (1) 使用代码生成器生成招聘微服务代码 tensquare_article
  • (2) 拷贝到当前工程,并在父工程引入。
  • (3) 修改Application类名称为ArticleApplication
  • (4) 修改application.yml 中的端口为9004 ,url 为
    jdbc:mysql://192.168.184.134:3306/tensquare_article?characterEncoding=UTF8
  • (5) 浏览器测试

4.3 代码编写

4.3.1 文章审核
  • (1) ArticleDao新增方法
    /**
     * 文章审核
     * @param id 需要审核的文章ID
     */
    @Modifying
    @Query(value = "UPDATE tb_article SET state=1 WHERE id = ?", nativeQuery = true)
    void examine(String id);
  • (2) ArticleService新增方法
	/**
	 * 文章审核
	 * @param id 需要审核的文章ID
	 */
	public void examine(String id){
		articleDao.examine(id);
	}
  • (3) ArticleController新增方法
	/**
	 * 审核
	 * @param id
	 * @return
	 */
	@PutMapping("/examine/{id}")
	public Result examine(@PathVariable String id){
		articleService.examine(id);
		return new Result(true, StatusCode.OK, "审核成功!");
	}

4.3.2 文章点赞
  • (1) ArticleDao新增方法
    /**
     * 文章审核
     * @param id 需要审核的文章ID
     */
    @Modifying
    @Query(value = "UPDATE tb_article SET state=1 WHERE id = ?", nativeQuery = true)
    void examine(String id);
  • (2) ArticleService新增方法
	/**
	 * 文章审核
	 * @param id 需要审核的文章ID
	 */
	public void examine(String id){
		articleDao.examine(id);
	}
  • (3) ArticleController新增方法
    /**
     * 文章点赞
     * <pre>
     *     thumbup=thumbup+1
     * </pre>
     * 上面的是为了防止错误,这样写了之后第一次点赞不会报错
     * @param id 需要点赞的文章id
     */
    @Modifying
    @Query(value = "UPDATE tb_article SET thumbup=thumbup+1 WHERE id = ?", nativeQuery = true)
    void addThumbup(String id);

  • (2) ArticleService新增方法
	/**
	 * 点赞
	 * @param id 文章ID
	 * @return
	 */
	public void addThumbup(String id){
		 articleDao.addThumbup(id);
	}
  • (3) ArticleController新增方法
	/**
	 * 文章点赞
	 * @param id 需要点赞的文章ID
	 * @return
	 */
	@PutMapping("/thumbup/{id}")
	public Result updateThumbup(@PathVariable String id){
		articleService.addThumbup(id);
		return new Result(true, StatusCode.OK,"点赞成功");
	}

5. 缓存处理

为了提高查询性能,我们通常使用redis缓存解决

5.1 Redis环境搭建

我们以docker的方式搭建redis服务

docker run -di --name=tensquare_redis -p 6379:6379 redis

5.2 SpringDataRedis

Spring-data-redis是spring大家族的一部分,提供了在srping应用中通过简单的配置访问
redis服务,对reids底层开发包(Jedis, JRedis, and RJC)进行了高度封装,RedisTemplate
提供了redis各种操作。

5.3 实现文章的缓存处理

5.3.1 查询文章操作缓存
  • (1) : 再tensquare-article的pom文件中引入依赖
	  <dependency>
		  <groupId>org.springframework.boot</groupId>
		  <artifactId>spring-boot-starter-data-redis</artifactId>
	  </dependency>
  • (2) : 修改application.yml ,在spring节点下添加配置
redis:
 host: 127.0.0.1
  • (3) : 修改ArticleService 引入RedisTemplate,并修改findById方法
	/**
	 * 根据ID查询实体
	 * @param id
	 * @return
	 */
	public Article findById(String id) {
		//查询缓存
		Article article= (Article)redisTemplate.opsForValue().get("article_"+id);
		if(article == null){
			article = articleDao.findById(id).get();
			//默认保留一天,这样在查询的时候,就会自动将文章放入缓存
			redisTemplate.opsForValue().set("article_"+id,article,1,TimeUnit.DAYS);
		}
		return article;
	}
5.3.2 修改或删除后清除缓存
	/**
	 * 修改
	 * @param article
	 */
	public void update(Article article) {
		redisTemplate.delete( "article_" + article.getId() );//删除缓存
		articleDao.save(article);
	}

	/**
	 * 删除
	 * @param id
	 */
	public void deleteById(String id) {
		redisTemplate.delete( "article_" + id);//删除缓存
		articleDao.deleteById(id);
	}
5.3.3 缓存过期处理

修改findById方法,设置1天过期时间

//默认保留一天,这样在查询的时候,就会自动将文章放入缓存
redisTemplate.opsForValue().set("article_"+id,article,1,TimeUnit.DAYS);

5.4 Spring Cache

Spring Cache使用方法与Spring对事务管理的配置相似。Spring Cache的核心就是对某
个方法进行缓存,其实质就是缓存该方法的返回结果,并把方法参数和结果用键值对的
方式存放到缓存中,当再次调用该方法使用相应的参数时,就会直接从缓存里面取出指
定的结果进行返回。
Spring Cache使用方法与Spring对事务管理的配置相似。Spring Cache的核心就是对某
个方法进行缓存,其实质就是缓存该方法的返回结果,并把方法参数和结果用键值对的
方式存放到缓存中,当再次调用该方法使用相应的参数时,就会直接从缓存里面取出指
定的结果进行返回。

常用注解:

  • (1) : @Cacheable 使用这个注解的方法在执行后会缓存其返回结果
  • (2) : @CacheEvict 使用这个注解的方法在其执行前或执行后移除Spring Cache中的某些
    元素。
  • (3) : @CachePut 使用这个注解的方法在执行后会缓存其返回结果,每次都会触发

5.5 活动信息的缓存

5.5.1 活动微服务代码生成
  • (1): 使用代码生成器生成招聘微服务代码 tensquare-gathering
  • (2): 拷贝当前工程,并且再父工程中引用。
  • (3): 修改启动类名 GatheriningApplication
  • (4): 修改Application.yml中的端口为9005,url为
jdbc:mysql://localhost:3306/tensquare_gathering?characterEncoding=UTF8
  • (5): 浏览器测试
5.5.2 活动详情的缓存实现
  • (1): 我们在tensquare-gathering的pom.xml中引入SpringDataRedis
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
  • (2) : 修改application.yml,再spring节点下配置redis
redis:
 host: 127.0.0.1
  • (3): 为GatheringApplication添加 @EnableCaching 开启缓存支持
  • (4): 在GatheringService的findById方法添加缓存注解,这样当此方法第一次运行,在
    缓存中没有找到对应的value和key,则将查询结果放入缓存
	/**
	 * 根据ID查询实体
	 * @param id
	 * @return
	 */
	@Cacheable(value="gathering",key ="#id")
	public Gathering findById(String id) {
		return gatheringDao.findById(id).get();
	}
  • (5) : 当我们对数据进行删改的时候,需要更新缓存。其实更新缓存也就是清除缓存,因
    为清除缓存后,用户再次调用查询方法无法提取缓存会重新查找数据库中的记录并放入
    缓存。
    在GatheringService的update、deleteById方法上添加清除缓存的注解
	/**
	 * 修改
	 * @param gathering
	 */
	@CachePut(value="gathering",key = "#gathering.id")
	public void update(Gathering gathering) {
		gatheringDao.save(gathering);
	}
	
	/**
	 * 删除
	 * @param id
	 */
	@CacheEvict(value="gathering",key = "#gathering.id")
	public void deleteById(String id) {
		gatheringDao.deleteById(id);
	}
  • (6) : 测试spring cache 是否可用

执行按照ID查询接口 http://localhost:9005/gathering/1

  • 执行之前清空redis
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379>
127.0.0.1:6379> keys *
(empty list or set)
  • 执行之后查询redis
127.0.0.1:6379>
127.0.0.1:6379> keys *
1) "gathering::1"
127.0.0.1:6379>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值