题目:
模拟一个微博的热搜
表: tb_news id title
Redis: zset
例:
1.创建一个实体类 NewsEntity
package com.lanou3g.redisdemo.entity;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.io.Serializable;
/**
* 创建人: 武奇
* 创建事件: 2019/6/26
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
@Table(name = "tb_news")
public class NewsEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@Transient
private Double score;
}
2 创建. NewsRepository 接口继承JpaRepository 接口(继承后可根据方法名自动生成sql语句)package com.lanou3g.redisdemo.repository;
import com.lanou3g.redisdemo.entity.NewsEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
/**
- 创建人: 武奇
创建事件: 2019/6/26
*/
public interface NewsRepository extends JpaRepository<NewsEntity, Long> {List findByIdIn(List idList);
}
3.编写前端导入JQ 向后台发送请求
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<title>新闻列表</title>
</head>
<body>
<div id="news-list"></div>
<script>
$.get("/news/list", function (items) {
const newsList = $("#news-list");
for (const item of items) {
//列出从数据库中查出的数据
const a = $("<a></a>").text(item.title);
//点击查出的数据获取id 去redis中查出谁的点击量最高
a.click(function () {
const data = {id: item.id};
$.post("/news/like", data, function (resp) {
console.log("点赞成功")
})
});
//把每条数据放入div中 在放入br标签使每条数据都换行
newsList.append(a);
newsList.append($("<br>"))
}
})
</script>
</body>
</html>
4.创建controller 层接收前端发送过来的请求
@RestController
@RequestMapping("/news")
public class NewsController {
// 引入NewsService 接口
@Resource
private NewsService newsService;
// 查询所有
@RequestMapping("/list")
public List<NewsEntity> list() {
return this.newsService.findAll();
}
// 增加点击数量 (Id 的添加)
@RequestMapping("/like")
public Map<String, Object> like(Long id) {
this.newsService.likeId(id);
return Collections.singletonMap("message", "OK");
}
// 向前端返回集合(id 的返回)
@RequestMapping("/sort")
public Collection<?> sort() {
return this.newsService.getWithScoresId();
}
创建 newservice 接口 (接口中写了两种添加 到 redis方式)
public interface NewsService {
// 查新所有新闻
List<NewsEntity> findAll();
// 添加点击数量(对象做法)
void like(Long id);
// 返回点击数量(对象做法)
Set<?> getWithScores();
// 添加点击数量(id 做法)
void likeId(Long id);
// 返回点击数量(id 做法)
Collection<?> getWithScoresId();
创建 NewsService 接口的实现类
@Service("newsService")
public class NewsServiceImpl implements NewsService {
// 引入NewsRepository接口
@Resource
private NewsRepository newsRepository;
// 引入springframework.data.redis.core 包下的 实体类 RedisTemplate
@Resource
private RedisTemplate<String, Object> redisTemplate;
// 查出所有
@Override
public List<NewsEntity> findAll() {
return this.newsRepository.findAll();
}
// 在redis中存入对象
@Override
public void like(Long id) {
ZSetOperations<String, Object> opsZ = this.redisTemplate.opsForZSet();
// 使用id查询所有
Optional<NewsEntity> optional = this.newsRepository.findById(id);
// 如果optional有值存在
if (optional.isPresent()) {
// 取出查出NewsEntity 点击增加多少
NewsEntity news = optional.get();
// 添加到redis 中 1,名字 2,对象 3每次
opsZ.incrementScore("news:like", news, 1);
}
}
// 返回 按照scores排序好的数据
@Override
public Set<?> getWithScores() {
ZSetOperations<String, Object> opsZ = this.redisTemplate.opsForZSet();
// 取出redis 中的按照scores排序好的数据 1,取出数据的名字 2,从0 开始 3,到第10 个结束
Set<ZSetOperations.TypedTuple<Object>> set = opsZ.reverseRangeWithScores("news:like", 0, 10);
return set;
}
// 在redis中存入id
@Override
public void likeId(Long id) {
ZSetOperations<String, Object> opsZ = this.redisTemplate.opsForZSet();
opsZ.incrementScore("news:like:id", id, 1);
}
// 从redis中取出 存入的id 与Scores
@Override
public Collection<?> getWithScoresId() {
ZSetOperations<String, Object> opsZ = this.redisTemplate.opsForZSet();
// 取出存入id的集合
Set<ZSetOperations.TypedTuple<Object>> tuple = opsZ.reverseRangeWithScores("news:like:id", 0, 10);
// 这个tuple里保存的关键信息是每个新闻的分数和新闻的id的顺序.
List<NewsEntity> items = new ArrayList<>();
for (ZSetOperations.TypedTuple<Object> item : tuple) {
Long id = Long.parseLong(String.valueOf(item.getValue()));
// 使用id查询 查询这条新闻的所有信息
Optional<NewsEntity> optional = this.newsRepository.findById(id);
// 如果返回的对象不为空
if (optional.isPresent()) {
NewsEntity news = optional.get();
// 把分数加入对象
news.setScore(item.getScore());
// 添加进list
items.add(news);
}
}
return items;
}
创建 RedisConfig 配置 存储 redis 时 存入对象的配置
package com.lanou3g.redisdemo.config;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.core.ValueOperations;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
/**
* 创建人: 武奇
* 创建事件: 2019/6/25
*/
@Configuration
public class RedisConfig {
// 配置key的序列化
@Bean
public RedisSerializer keySerializer() {
return new StringRedisSerializer();
}
// 配置value的序列化
@Bean
public RedisSerializer valueSerializer() {
// 当向Redis中存储一个对象时候,
// 会把对象转换为json数据存储到Redis中
return new GenericJackson2JsonRedisSerializer();
}
//
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(keySerializer());
redisTemplate.setHashKeySerializer(keySerializer());
// 如果不配置JSON的序列化, 还想保存对象,
// 那么前提是这个对象是可以被序列化的, 也就是说
// 对应的类必须是实现Serializable接口的
// 如果需要使用JSON的序列化, 被保存的对象,
// 必须得有默认的构造方法, 否则对象能被存上, 但是读不出来
redisTemplate.setValueSerializer(valueSerializer());
redisTemplate.setHashValueSerializer(valueSerializer());
return redisTemplate;
}
/*
<bean id="keySerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
<bean id="redisTemplate" class="com.xxx.RedisTemplate">
<property key="connectionFactory" ref="redisConnectionFactory"/>
<property key="keySerializer" ref="keySerializer" />
<property key="hashKeySerializer" ref="keySerializer" />
</bean>
*/
}
这样 模拟的微博热搜 就写完了
Redis缓存
需要在 application.yml 里配置(其实可以不配置,只要导包时 有导入redis 包 那么他会自动匹配可以不写)
#若是已经导入redis框架 可以不写 (下面一段)它默认就是redis
redis:
host: localhost
port: 6379
#选择缓存器是 谁的缓存(可不写 加入redis框架后默认是redis
#如不加redis框架也可以用缓存,会默认用simple
cache:
type: redis
在SpringBoot 项目开始创建时,自动创建的实体类中写 开始缓存的注解
package com.lanou3g.redisdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
//启用缓存
@EnableCaching
public class RedisDemoApplication {
public static void main(String[] args) {
SpringApplication.run(RedisDemoApplication.class, args);
}
}
在cotroller 层 中 做缓存
// @Cacheable 该方法支持缓存 cacheNames缓存的名字
@Cacheable(cacheNames = "cache:news")
// 在创建工程最初的被创建的实体类中开启缓存加上 @EnableCaching 注解
@RequestMapping("/{id}")
public NewsEntity getOne(@PathVariable Long id) {
return this.newsService.findById(id);
}
// 再缓存中真正的key 是由两部分组成的:
// 第一部分是缓存的名字,也cacheNames就是相应的值
// 第二部分是缓存的key, 也就是key的属性
//在做数据更新的时候需要缓存进行同步
// @CacheEvict删掉某个缓存
@CacheEvict(cacheNames = "cache:news",key = "#news.id")
@RequestMapping("/update")
public NewsEntity update(NewsEntity news) {
return this.newsService.update(news);
}
// 添加数据
// @CachePut 直接更新缓存
// key的参数是 返回值的id
@CachePut(cacheNames = "cache:news",key = "#result.id")
@RequestMapping("/save")
public NewsEntity save(NewsEntity news){
return NewsEntity.builder()
.id(10001L)
.build();
}
只要加上注解 redis 就会开始缓存
@Cacheable 该方法支持缓存
@CachePut 直接更新缓存
@CacheEvict删掉某个缓存
缓存相关的注解,不一定非写在controller层
写在service或Dao层都可以
根据实际需求灵活使用
service 层和以前一样 写