用redis和jpa实现缓存文章和点击量-SpringBoot(23)

用redis和jpa实现缓存文章和点击量-SpringBoot(23)

实现流程

1. 实现缓存文章

  1.1 实体类

package com.intehel.demo.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Article implements Serializable {
    private Integer id;
    private Integer num;
}

  1.2 数据库持久层

package com.intehel.demo.mapper;

import com.intehel.demo.domain.Article;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;

@Repository
@Mapper
public interface ArticleMapper {
    Article findArticleById(Long id);
    Long updateArticle(@Param("lviews") Long lviews, @Param("lid") Long lid);
}

  1.3 mybatis映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.intehel.demo.mapper.ArticleMapper" >
    <resultMap id="BaseResultMap" type="com.intehel.demo.domain.Article" >
        <id column="id" property="id" jdbcType="INTEGER"/>
        <result column="num" property="num" jdbcType="BIGINT"/>
    </resultMap>

    <!--根据参数名称查询参数配置表-->
    <select id="findArticleById" resultMap="BaseResultMap" parameterType="java.lang.Long" >
        select * from article where id = #{id}
    </select>

    <!--根据主键,不存在就新增,已存在就修改-->
    <update id="updateArticle" parameterType="java.lang.Long">
        update article set num = #{lviews} where id = #{lid}
    </update>
</mapper>

  1.4 实现服务层的缓存设置

package com.intehel.demo.service;

import com.intehel.demo.domain.Article;
import com.intehel.demo.mapper.ArticleMapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
@CacheConfig(cacheNames = "articleService")
public class ArticleService {
    @Autowired
    private ArticleMapper articleMapper;
    @Cacheable(key = "#id")
    public Article findArticleById(Long id) {
        return articleMapper.findArticleById(id);
    }
    @CachePut(key = "#lid")
    public Article updateArticle(@Param("lviews") Long lviews, @Param("lid") Long lid) {
        articleMapper.updateArticle(lviews,lid);
        return articleMapper.findArticleById(lid);
    }
}

  1.5 配置redis

package com.intehel.demo.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.*;

import java.lang.reflect.Method;
import java.time.Duration;

@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    //在缓存对象集合中,缓存是以key-value形式保存的
    //如果没有指定缓存的key,则Spring Boot 会使用SimpleKeyGenerator生成key
    @Override
    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            //定义缓存key的生成策略
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }
    @SuppressWarnings("rawtypes")
    @Bean
    //缓存管理器 2.X版本
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisSerializer<String> strSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jacksonSeial =
                new Jackson2JsonRedisSerializer(Object.class);

        // 解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSeial.setObjectMapper(om);

        // 定制缓存数据序列化方式及时效
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofDays(1))
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(strSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(jacksonSeial))
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager
                .builder(connectionFactory).cacheDefaults(config).build();
        return cacheManager;
    }
    //缓存管理器 1.X版本
/*    public CacheManager cacheManager(@SuppressWarnings("rawtypes")RedisTemplate redisTemplate){
        return new RedisCacheManager(redisTemplate);
    }*/

    @Bean
    //注册成Bean被Spring管理,如果没有这个Bean,则Redis可视化工具中的中文内容都会以二进制存储,不易检查
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        // 创建JSON格式序列化对象,对缓存数据的key和value进行转换
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        // 解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        //设置redisTemplate模板API的序列化方式为json
        template.setDefaultSerializer(jackson2JsonRedisSerializer);
        return template;
    }

    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory){
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(factory);
        return stringRedisTemplate;
    }
    private RedisSerializer<?> keySerializer() {
        return new StringRedisSerializer();
    }

    // 值使用jackson进行序列化
    private RedisSerializer<?> valueSerializer() {
        return new GenericJackson2JsonRedisSerializer();
        // return new JdkSerializationRedisSerializer();
    }
}

2. 实现统计点击量

package com.intehel.demo.controller;

import com.intehel.demo.domain.Article;
import com.intehel.demo.service.ArticleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/article")
public class ArticleController {
    @Autowired
    private ArticleService articleService;
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;
    @RequestMapping("/{id}")
    public Article testPathVariable(@PathVariable("id") Integer id){
        Article article = articleService.findArticleById(Long.valueOf(id));
        System.out.println(article);
        if (article.getNum()>0){
            if (redisTemplate.opsForValue().get("num::"+id)!=null){
                redisTemplate.opsForValue().increment("num::"+id, 1);
            }else {
                redisTemplate.boundValueOps("num::"+id).increment(article.getNum()+1);

            }
        }else {
            redisTemplate.boundValueOps("num::"+id).increment(1);
        }
        return article;
    }
}

3.实现定时同步

package com.intehel.demo.config;

import com.intehel.demo.service.ArticleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Component
public class CountScheduledTasks {
    @Autowired
    private ArticleService articleService;
    @Resource
    private RedisTemplate<String,Object> redisTemplate;
    @Scheduled(cron = "0/10 * * * * ?" )
    public void syncPostViews(){
        Long StartTime = System.nanoTime();
        List dtolist = new ArrayList();
        Set<String> keySet = redisTemplate.keys("num::*");
        System.out.println(keySet);
        for (String key : keySet) {
            String view = redisTemplate.opsForValue().get(key).toString();
            String sid = key.replaceAll("num::", "");
            Long lid = Long.valueOf(sid);
            Long lviews = Long.valueOf(view);
            articleService.updateArticle(lviews,lid);
            redisTemplate.delete(key);
        }
    }
}

  同时要在启动类中添加注解,开启redis缓存和定时任务:

@SpringBootApplication
@EnableCaching
@EnableScheduling
@MapperScan("com.intehel.demo")
hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » 用redis和jpa实现缓存文章和点击量-SpringBoot(23)