• 技术文章 >Java >java教程

    怎么使用Java实现先查询缓存再查询数据库

    WBOYWBOY2023-04-29 09:55:06转载24

    栗子

    现有一个使用商品名称查询商品的需求,要求先查询缓存,查不到则去数据库查询;从数据库查询到之后加入缓存,再查询时继续先查询缓存。

    思路分析

    可以写一个条件判断,伪代码如下:

    //先从缓存中查询
    String goodsInfoStr = redis.get(goodsName);
    if(StringUtils.isBlank(goodsInfoStr)){
    	//如果缓存中查询为空,则去数据库中查询
    	Goods goods = goodsMapper.queryByName(goodsName);
    	//将查询到的数据存入缓存
    	goodsName.set(goodsName,JSONObject.toJSONString(goods));
    	//返回商品数据
    	return goods;
    }else{
    	//将查询到的str转换为对象并返回
    	return JSON.parseObject(goodsInfoStr, Goods.class);
    }

    上面这串代码也可以实现查询效果,看起来也不是很复杂,但是这串代码是不可复用的,只能用在这个场景。假设在我们的系统中还有很多类似上面商品查询的需求,那么我们需要到处写这样的if(...)else{...}。作为一个程序员,不能把类似的或者重复的代码统一起来是一件很难受的事情,所以需要对这种场景的代码进行优化。

    上面这串代码的问题在于:入参不固定、返回值也不固定,如果仅仅是参数不固定,使用泛型即可。但最关键的是查询方法也是不固定的,比如查询商品和查询用户肯定不是一个查询方法吧。

    所以如果我们可以把一个方法(即上面的各种查询方法)也能当做一个参数传入一个统一的判断方法就好了,类似于:

    /**
     * 这个方法的作用是:先执行method1方法,如果method1查询或执行不成功,再执行method2方法
     */
    public static<T> T selectCacheByTemplate(method1,method2)

    想要实现上面的这种效果,就不得不提到Java8的新特性:函数式编程

    原理介绍

    在Java中有一个package:java.util.function ,里面全部是接口,并且都被@FunctionalInterface注解所修饰。

    Function分类

    具体我就不在赘述了,可以参考:Java 函数式编程梳理

    代码实现

    那么接下来就来使用Java优雅的实现先查询缓存再查询数据库吧!

    项目代码

    配置文件

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.7.2</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.example</groupId>
        <artifactId>SpringBoot-query</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>SpringBoot-query</name>
        <description>Demo project for Spring Boot</description>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <!-- redis -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
            <!-- fastjson -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.83</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>
    项目结构

    怎么使用Java实现先查询缓存再查询数据库

    其中CacheService是从缓存中查询数据,GoodsService是从数据库中查询数据

    SpringBootQueryApplication.java
    package com.example.springbootquery;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    @SpringBootApplication
    public class SpringBootQueryApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(SpringBootQueryApplication.class, args);
    	}
    
    }
    Goods.java
    package com.example.springbootquery.entity;
    public class Goods {
        private String goodsName;
        private Integer goodsTotal;
        private Double price;
        public String getGoodsName() {
            return goodsName;
        }
        public void setGoodsName(String goodsName) {
            this.goodsName = goodsName;
        }
        public Integer getGoodsTotal() {
            return goodsTotal;
        }
        public void setGoodsTotal(Integer goodsTotal) {
            this.goodsTotal = goodsTotal;
        }
        public Double getPrice() {
            return price;
        }
        public void setPrice(Double price) {
            this.price = price;
        }
        @Override
        public String toString() {
            return "Goods{" +
                    "goodsName='" + goodsName + '\'' +
                    ", goodsTotal='" + goodsTotal + '\'' +
                    ", price=" + price +
                    '}';
        }
    }
    CacheSelector.java

    自定义函数式接口:

    package com.example.springbootquery.function;
    
    @FunctionalInterface
    public interface CacheSelector<T> {
        T select() throws Exception;
    }
    CacheService.java
    package com.example.springbootquery.service;
    
    import com.example.springbootquery.entity.Goods;
    public interface CacheService {
        /**
         * 从缓存中获取商品
         *
         * @param goodsName 商品名称
         * @return goods
         */
        Goods getGoodsByName(String goodsName) throws Exception;
    }
    CacheServiceImpl.java
    package com.example.springbootquery.service.impl;
    
    import com.alibaba.fastjson.JSON;
    import com.example.springbootquery.entity.Goods;
    import com.example.springbootquery.service.CacheService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.stereotype.Service;
    
    @Service("cacheService")
    public class CacheServiceImpl implements CacheService {
    
        @Autowired
        private StringRedisTemplate redisTemplate;
    
        @Override
        public Goods getGoodsByName(String goodsName) throws Exception {
            String s = redisTemplate.opsForValue().get(goodsName);
            return null == s ? null : JSON.parseObject(s, Goods.class);
        }
    }
    GoodsService.java
    package com.example.springbootquery.service;
    import com.example.springbootquery.entity.Goods;
    public interface GoodsService {
        Goods getGoodsByName(String goodsName);
    }
    GoodsServiceImpl.java

    这里我就不连接数据库了,模拟一个返回

    package com.example.springbootquery.service.impl;
    
    import com.alibaba.fastjson.JSONObject;
    import com.example.springbootquery.entity.Goods;
    import com.example.springbootquery.service.GoodsService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.stereotype.Service;
    @Service
    public class GoodsServiceImpl implements GoodsService {
        @Autowired
        private StringRedisTemplate stringRedisTemplate;
        @Override
        public Goods getGoodsByName(String goodsName) {
            Goods goods = new Goods();
            goods.setGoodsName("商品名1");
            goods.setGoodsTotal(20);
            goods.setPrice(30.0D);
            stringRedisTemplate.opsForValue().set(goodsName, JSONObject.toJSONString(goods));
            return goods;
        }
    }
    BaseUtil.java (核心类)

    因为我不关心参数,只需要一个返回值就行了,所以这里使用的是Supplier。

    package com.example.springbootquery.util;
    import com.example.springbootquery.function.CacheSelector;
    import java.util.function.Supplier;
    public class BaseUtil {
        /**
         * 缓存查询模板
         *
         * @param cacheSelector    查询缓存的方法
         * @param databaseSelector 数据库查询方法
         * @return T
         */
        public static <T> T selectCacheByTemplate(CacheSelector<T> cacheSelector, Supplier<T> databaseSelector) {
            try {
                System.out.println("query data from redis ······");
                // 先查 Redis缓存
                T t = cacheSelector.select();
                if (t == null) {
                    // 没有记录再查询数据库
                    System.err.println("redis 中没有查询到");
                    System.out.println("query data from database ······");
                    return databaseSelector.get();
                } else {
                    return t;
                }
            } catch (Exception e) {
                // 缓存查询出错,则去数据库查询
                e.printStackTrace();
                System.err.println("redis 查询出错");
                System.out.println("query data from database ······");
                return databaseSelector.get();
            }
        }
    }

    用法

    package com.example.springbootquery;
    
    import com.example.springbootquery.entity.Goods;
    import com.example.springbootquery.service.CacheService;
    import com.example.springbootquery.service.GoodsService;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import static com.example.springbootquery.util.BaseUtil.selectCacheByTemplate;
    @SpringBootTest
    class SpringBootQueryApplicationTests {
        @Autowired
        private CacheService cacheService;
        @Autowired
        private GoodsService userService;
        @Test
        void contextLoads() throws Exception {
            Goods user = selectCacheByTemplate(
                    () -> cacheService.getGoodsByName("商品名1"),
                    () -> userService.getGoodsByName("商品名1")
            );
            System.out.println(user);
        }
    }
    第一次从数据中查询

    怎么使用Java实现先查询缓存再查询数据库

    第二次从缓存中查询

    怎么使用Java实现先查询缓存再查询数据库

    以上就是怎么使用Java实现先查询缓存再查询数据库的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:亿速云,如有侵犯,请联系admin@php.cn删除
    专题推荐:数据库 Java
    上一篇:Java中的BaseTypeHandler自定义类型转换器如何使用 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • java中UDP怎么发送数据• Java方法如何定义与调用• Java操作pdf的工具类itext怎么处理• Java+Tomcat环境怎么部署及安装• java的SynchronousQueue是什么
    1/1

    PHP中文网