今天这个是将数据库和缓存联系起来。将数据库中没有缓存的数据进行缓存并同时对数据库和缓存进行操作
基于API
添加依赖
redis依赖、mysql 连接依赖 、jpa依赖
配置文件
redis配置 数据库配置
数据表(t_comment)
一个关联数据表的实体类(Comment)
package com.zknu.inter.modle;
import javax.persistence.*;
import java.io.Serializable;
@Entity(name = "t_comment")
public class Comment implements Serializable {
// 要对实体进行序列化
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String title;
private String author;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return "Comment{" +
"id=" + id +
", title='" + title + '\'' +
", author='" + author + '\'' +
'}';
}
}
一个编写数据库操作的Respository接口文件
package com.zknu.inter.Repository;
import com.zknu.inter.modle.Comment;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import javax.transaction.Transactional;
public interface CommentRepository extends JpaRepository<Comment,Integer> {
@Transactional
@Modifying
@Query("update t_comment set title = ?1 where id = ?2")
public int updateComment(String title,Integer id);
}
一个编写业务操作(删改查)的Service类
package com.zknu.inter.service;
import com.zknu.inter.Repository.CommentRepository;
import com.zknu.inter.modle.Comment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@Service
public class ApiCommentService {
@Autowired
private CommentRepository commentRepository;
@Autowired
private RedisTemplate redisTemplate;
public Comment findById(int comment_id){
Object object = redisTemplate.opsForValue().get("comment_"+comment_id);
if (object != null){
return (Comment) object;
}
else {
Optional<Comment> optional = commentRepository.findById(comment_id);
if(optional.isPresent()){
Comment comment = optional.get();
redisTemplate.opsForValue().set("comment_"+comment_id,comment,1, TimeUnit.DAYS);
return comment;
}
else {
return null;
}
}
}
public Comment updateComment(Comment comment){
redisTemplate.opsForValue().set("comment_"+comment.getId(),comment);
return comment;
}
public void deleteComment(int comment_id){
commentRepository.deleteById(comment_id);
redisTemplate.delete("comment_"+comment_id);
}
}
控制类
package com.zknu.inter.controller;
import com.zknu.inter.modle.Comment;
import com.zknu.inter.service.ApiCommentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class ApiCommentController {
@Autowired
private ApiCommentService apiCommentService;
@GetMapping("/get/{id}")
public Comment findById(@PathVariable("id") int comment_id){
Comment comment = apiCommentService.findById(comment_id);
return comment;
}
@GetMapping("/update/{id}/{author}")
public Comment updateComment(@PathVariable("id") int comment_id,@PathVariable("author") String comment_author){
Comment comment = apiCommentService.findById(comment_id);
comment.setAuthor(comment_author);
Comment update = apiCommentService.updateComment(comment);
return update;
}
@GetMapping("/delete/{id}")
public void deleteComment(@PathVariable("id") int comment_id){
apiCommentService.deleteComment(comment_id);
}
}
运行结果
我们注意到,此时存储在内存中的值为好长一串十六进制的值,不方便我们查看何操作。
所以我们可以自定义 RedisTemplate ,自定义序列化机制,从而自定义我们存储在内存中的数据的格式(String、Json等)
自定义配置类 RedisConfig
package com.zknu.inter.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
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.serializer.Jackson2JsonRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
// 使用json格式序列化
Jackson2JsonRedisSerializer jackson = new Jackson2JsonRedisSerializer(Object.class);
// 解决查询转换缓存时的异常问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson.setObjectMapper(om);
template.setDefaultSerializer(jackson);
return template;
}
}
运行结果:
此时,我们存储的值就可以以json的格式显示出来了
基于注解
基于API 就是我们自己将查询然后存入缓存的逻辑写了出来,基于注解就是将这些逻辑全部用注解实现,具体这些注解怎么实现这个逻辑的,就需要自己去研究研究源码了。
所以,我们在此基础上改写 Service 及控制类即可
一个编写业务操作(删改查)的Service类
package com.zknu.inter.service;
import com.zknu.inter.Repository.CommentRepository;
import com.zknu.inter.modle.Comment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class CommentService {
@Autowired
private CommentRepository commentRepository;
@Cacheable(cacheNames = "comment",unless = "#result == null ")
// 对sql的查询结果进行缓存
public Comment findById(int comment_id){
Optional<Comment> optional = commentRepository.findById(comment_id);
if(optional.isPresent()){
return optional.get();
}
return null;
}
@CachePut(cacheNames = "comment" , key = "#result.id")
//更新缓存
public Comment updateComment(Comment comment){
commentRepository.updateComment(comment.getTitle(),comment.getId());
return comment;
}
@CacheEvict(cacheNames = "comment")
// 删除缓存
public void deleteComment(int comment_id){
commentRepository.deleteById(comment_id);
}
}
@Cacheable注解
用于对方法结果进行缓存存储
执行顺序:先进行缓存查询,如果为空则进行方法查询(数据库查询),并将结果进行缓存;如果缓存中有数据,不进行方法查询,而是直接使用缓存数据。
属性:
属性中可以使用SpEL表达式
@CachePut注解
更新缓存数据
执行顺序:先进行方法调用(数据库查询),然后将方法结果更新到缓存中。
属性:与@Cacheable注解的属性完全相同。
@CacheEvict注解
删除缓存数据
执行顺序:先进行方法调用(数据库查询),然后将缓存进行清除。
属性:比@Cacheable多两个属性 allEntries 和 beforeInvocation
allEntries属性
表示是否清除所有缓存数据,默认值为false
beforeInvocation属性
表示是否在方法执行之前进行缓存清除,默认值为false
还有两个注解:@Caching、@CacheConfig 不常用
Web访问控制类:
package com.zknu.inter.controller;
import com.zknu.inter.modle.Comment;
import com.zknu.inter.service.CommentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@ResponseBody
public class CommentController {
@Autowired
private CommentService commentService;
@GetMapping("/get/{id}")
public Comment findById(@PathVariable("id") int comment_id){
Comment comment = commentService.findById(comment_id);
return comment;
}
@GetMapping("/update/{id}/{title}")
public Comment updateComment(@PathVariable("id") int comment_id , @PathVariable("title") String title){
Comment comment = commentService.findById(comment_id);
//return comment;
comment.setTitle(title);
Comment update = commentService.updateComment(comment);
return update;
}
@GetMapping("/delete/{id}")
public void deleteComment(@PathVariable("id") int comment_id){
commentService.deleteComment(comment_id);
}
}
@ResponseBody 可以将返回的内容变为字符串。
例如:上述 findById 和 updateComment 都是返回的 Comment类型,加上这个注解后,最终会返回 Comment 的toString方法,便于Web查看
@Controller、@ResponseBody两个注解可以换位 @RestController ,对数据操作的效果一样,只是访问页面时会报错。
@GetMapping("/update/{id}/{title}")
大括号中的内容为在地址栏中传的参数,
等同于 /update?id=***&&title=***
@PathVariable(“id”)
接收地址栏传的参数
运行结果
查询操作
http://localhost:8080/get/3 的意思就是 findById(3) ,如果缓存中不存在该条数据,就将数据存入缓存更新操作
http://localhost:8080/update/3/wrk 其实就是执行了
update t_comment set title = ?1 where id = ?2删除操作
http://localhost:8080/delete/3 执行的是 deleteById(3)
同样,需要自定义序列化机制,自定义存储在内存中的值
自定义RedisCacheManager
在 RedisConfig 类中增加
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
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);
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofDays(1))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory).cacheDefaults(config).build();
return cacheManager;
}
运行结果