第四阶段笔记 Jingtao_day11

day 11 Redis

此文档是根据上课流程编写,更多细节及图片请参见刘老师的专栏

江哥的专栏

cgb2008-京淘day11

一. Redis客户端命令
  1. Hash类型

    i. 说明

    ​ 可以用散列类型保存"对象"和属性值,一般在工作中存储的都是基于一个业务对象的数据。

    ​ user{"name":"aa","age":5}

    ii. Hash类型命令

    hset user name aa
    hset user age 5
    hget user age => "5"
    hgetall user => "name" "aa" "age" "5"
    hkeys user => "name" "age"
  2. List类型

    i. 说明

    ​ 双端循环链表,可以从首尾两端访问数据 L===R

    ii. List类型命令

    队列:lpush list 1 2 3 4 5 rpop ==> 1
    栈:lpush list 1 2 3 4 5 lpop ==> 5
  3. Set类型

    sadd set1 a
    sadd set1 b
    scard set1
    sinter set1 set2
    sinterstore set3 set1 set2
    sismember set1 c
    smembers set1
  4. 事务命令

    Redis中的事务是弱事务,如果在分布式系统中无法对事务进行控制。

    multi
    exec
    discard
二. 使用程序操作redis
  1. 整合入门案例

    i. 导入jar包

    
    
        redis.clients
        jedis
    
    
        org.springframework.data
        spring-data-redis
    

    ii. 创建TestRedis测试类

    public class TestRedis {
        @Test
        //连接服务:IP地址:端口
        public void testSetGet() throws InterruptedException {
            Jedis jedis = new Jedis("192.168.126.129",6379);
            jedis.flushAll(); //先清空redis缓存
            //1.存取redis
            jedis.set("好好学习", "天天向上");
            String value = jedis.get("好好学习");
            System.out.println(value);
    
            //2.判断key是否存在
            if(jedis.exists("好好学习")){
                jedis.set("好好学习", "天天敲代码");
            }else {
                jedis.set("好好学习", "天天开心");
            };
    
            //3.为key添加超时时间
            jedis.expire("好好学习", 10);
            Thread.sleep(2000);
            System.out.println("剩余存活时间:"+jedis.ttl("好好学习"));
    
            //4.撤销剩余时间
            jedis.persist("好好学习");
            System.out.println("撤销成功");
        }
    }
  2. 高级API

    i. 测试setnx()

    @Test
    //如果数据不存在,则修改数据
    public void testSetNx() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129",6379);
        jedis.set("key1", "aaa");
        //如果key不存在则赋值
        jedis.setnx("key1", "测试方法");
        System.out.println(jedis.get("key1")); //"aaa"
    }

    ii. 测试setex()

    @Test
    //实现超时时间的设定与赋值操作原子性
    public void testSetEx() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129",6379);
        jedis.set("key2", "bbb");
        jedis.expire("key2", 3);
        //数据超时之后一定会被删除吗? 错!
        //考点:为数据设定超时时间时,切记满足原子性操作,否则会出现key永远不能删除的现象
        jedis.setex("key2", 3, "bbb");
    }

    iii. 测试SetParams

    @Test
    //如果key存在,则为其赋值并添加超时时间
    public void testSet() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129",6379);
        /*if(jedis.exists("key3")){
            jedis.setex("key3", 10, "cc");
        }*/
        //NX - 只有数据不存在时才会赋值 XX - 只有数据存在时才会赋值
        //EX - 设置超时时间:s PX - 设置超时时间:ms
        SetParams setParams = new SetParams();
        setParams.xx().ex(10);
        //将所有的操作采用原子性的方式进行控制
        jedis.set("key3", "ccc", setParams);
    }

    iv. 测试Hash

    @Test
    public void testHash() {
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        jedis.hset("person", "id","100");
        jedis.hset("person","name","Tomcat");
        System.out.println(jedis.hgetAll("person"));
    }

    v. 测试List

    @Test
    public void testList(){
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        jedis.lpush("list", "1","2","3","4");
        String value = jedis.rpop("list");
        System.out.println(value);
    }

    vi. 测试事务

    @Test
    public void testMulti(){
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        //开启事务
        Transaction transaction = jedis.multi();
        try {
            transaction.set("a", "a");
            transaction.set("b", "b");
            transaction.set("c", "c");
            //提交事务
             transaction.exec();
        }catch (Exception e){
            //回滚事务
            transaction.discard();
        }
    }
三. SpringBoot整合redis
  1. 编写RedisConfig配置类

    @Configuration //标识一个配置类 一般与@Bean注解连用
    @PropertySource("classpath:/properties/redis.properties")
    public class RedisConfig {
        @Value("${redis.host}")
        private String host;
        @Value("${redis.port}")
        private Integer port;
    
        @Bean
        public Jedis jedis(){
            return new Jedis(host,port);
        }
    }
  2. 修改TestRedis测试类

    @SpringBootTest
    public class TestRedis {
        @Autowired
        private Jedis jedis;
        ...
    }
四. 基于业务实现缓存
  1. ObjectMapperUtil实现

    i. 业务需求

    ​ 在业务中通常需要将业务对象

    ii. 入门案例-编写TestObjectMapper测试类

    public class TestObjectMapper {
        //objectMapper入门案例
        @Test
        public void test01() throws JsonProcessingException {
            //1.将对象转化为JSON
            ItemDesc itemDesc = new ItemDesc();
            itemDesc.setItemId(100L).setItemDesc("测试数据")
                    .setCreated(new Date()).setUpdated(itemDesc.getCreated());
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(itemDesc);
            System.out.println(json);
    
            //2.将JSON转化为对象
            ItemDesc desc = objectMapper.readValue(json, ItemDesc.class);
            System.out.println(desc);
        }
    }
    //objectMapper入门案例
    @Test
    public void test02() throws JsonProcessingException {
        List list = new ArrayList<>();
        //1.将集合转化为JSON
        ItemDesc itemDesc = new ItemDesc();
        itemDesc.setItemId(100L).setItemDesc("测试数据")
                .setCreated(new Date()).setUpdated(itemDesc.getCreated());
        ItemDesc itemDesc2 = new ItemDesc();
        itemDesc2.setItemId(50L).setItemDesc("测试数据2")
                .setCreated(new Date()).setUpdated(itemDesc.getCreated());
        list.add(itemDesc);
        list.add(itemDesc2);
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(list);
        System.out.println(json);
    
        //2.将JSON转化为集合
        List desc = objectMapper.readValue(json, list.getClass());
        System.out.println(desc);
    }

    iii. 编辑ObjectMapperUtil工具API

    public class ObjectMapperUtil {
        private static final ObjectMapper MAPPER = new ObjectMapper();
    
        private static String toJSON(Object target){
            try {
                return MAPPER.writeValueAsString(target);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
                //将检查异常转化为运行时异常
                throw new RuntimeException(e);
            }
        }
    
        private static  T toObj(String json,Class clazz){
            try {
                return MAPPER.readValue(json, clazz);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
    }
  2. 实现商品种类的缓存

    i. 编辑ItemCatController

    /* 业务需求:实现商品分类树形结构的展示
        url: http://localhost:8091/item/cat/list
        参数: id = 父级节点的id
        返回值: List
    */
    @RequestMapping("/list")
    public List showTree(Long id){
        //暂时只查询一级菜单信息
        long pid = (id==null)?0L:id;
        //数据库记录 ItemCat对象,页面中要的数据 EasyUITree
        //需要将pojo对象转换为vo对象 get/set...
        //List list = itemCatService.findAllCat(pid);
        //return list;
        return itemCatService.findItemCatCache(pid);
    }

    ii. 编辑ItemCatService/Impl

    /* 实现步骤:
        1.先定义key ITEM_CAT_PARENT::0
        2.先查询缓存
            T - 通过获取缓存数据之后,将JSON转化为对象,之后返回
            F - 应该查询数据库,之后将数据保存到redis中,默认30天超时
     */
    @Autowired(required = false)
    private Jedis jedis;
    @Override
    public List findItemCatCache(long pid) {
        List treeList = new ArrayList<>();
        //1.定义key
        String key = "ITEM_CAT_PARENT::"+pid;
        //2.
        if(jedis.exists(key)){
            String json = jedis.get(key);
            System.out.println("==查询缓存==");
            treeList =  ObjectMapperUtil.toObj(json,treeList.getClass());
        }else {
            //不存在
            treeList = findAllCat(pid);
            String json = ObjectMapperUtil.toJSON(treeList);
            jedis.setex(key,7*24*60*60, json);
            System.out.println("==查数据库==");
        }
        return treeList;
    }
  3. AOP实现Redis缓存

    i. AOP复习

    ​ 1) 利用AOP可以实现对方法(功能)的扩展,实现代码的解耦;

    ​ 2) 切面的组成要素:

    ​ 切面 = 切入点表达式 + 通知方法

    ​ 3) 切入点表达式

    bean(bean的id) 拦截bean的所有方法,是粗粒度的 --> 具体的某个类
    within(包名.类名) 扫描某个包下的某些类中的所有方法 com.jt.* 粗粒度的
    execution(返回值类型 包名.类名.方法名(参数列表)) 细粒度的 !!!
    @annotation(包名.注解名) 细粒度的

    ​ 4) 通知方法

    before 目标方法执行前
    afterReturning 目标方法返回后
    afterThrowing 目标方法抛出异常后
    after 不管什么情况,最后都要执行
    around 目标方法执行前后都要执行

    ​ 前四种通知类型一般用于记录程序运行的状态。如果要对程序运行的轨迹产生影响,首选around。

    ii. AOP入门案例 - 编写CacheAop

    @Aspect
    @Component
    public class CacheAop {
        //切面 = 切入点表达式+通知方法
        //表达式1: ItemCatServiceImpl类
        //@Pointcut("within(com.jt.service.*)")
        //.* 一级包下的类 ..* 所有子孙后代包中的类
        //@Pointcut("execution(* com.jt.service..*.*(**))")
        @Pointcut("bean(itemCatServiceImpl)") //l默认值类名首字母小写
        public void pointcut(){}
    
        @Before("pointcut()")
        public void before(JoinPoint joinPoint){
            //jointPoint代表连接点的对象-程序执行的方法适用于前四大类型
            //1.获取目标对象
            Object target = joinPoint.getTarget();
            System.out.println(target);
            //2.获取目标对象的路径 包名.类名.方法名
            String className = joinPoint.getSignature().getDeclaringTypeName();
            String methodName = joinPoint.getSignature().getName();
            System.out.println("目标方法的路径: "+className+methodName);
            //3.获取参数类型
            System.out.println(Arrays.toString(joinPoint.getArgs()));
        }
    
        @Around("pointcut()")
        public Object around(ProceedingJoinPoint joinPoint){
            System.out.println("==环绕通知==");
            Object data = null;
            try {
                data = joinPoint.proceed();
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            return data;
        }
    }
作业:通过自定义注解@CacheFind完成AOP-redis缓存

你可能感兴趣的:(java,intellij-idea)