用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")