Springboot整合Redis实现缓存
使用原始方式实现查询
application.yml
spring:
datasource:
url: jdbc:mysql:///springbootdata?serverTimezone=UTC&useUnicode=true&useSSL=true&characterEncoding=utf8
username: root
password: 1234
jpa:
show-sql: true
maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
实体类comment
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity(name = "t_comment") // 映射的数据库表名
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // 主键自增
private Integer id;
private String content;
private String author;
@Column(name = "a_id") // 对应的数据库列名
private Integer aId;
}
mapper层
public interface CommentRepository extends JpaRepository<Comment, Integer> {
// 根据id修改评论作者
@Transactional // 更新的操作需要做事务控制
@Modifying
@Query("update t_comment c set c.author=?1 where c.id=?2")
int updateComment(String author, Integer id);
}
service层
@Service
public class CommentService {
@Autowired
private CommentRepository commentRepository;
public Comment findById(int comment_id){
Optional<Comment> optional = commentRepository.findById(comment_id);
if (optional.isPresent()){
return optional.get();
}
return null;
}
public Comment updateComment(Comment comment){
commentRepository.updateComment(comment.getAuthor(),comment.getId());
return comment;
}
public void deleteComment(int comment_id){
commentRepository.deleteById(comment_id);
}
}
控制层
@Controller
@ResponseBody
public class CommentController {
@Autowired
private CommentService commentService;
@RequestMapping("/get/{id}")
public Comment findById(@PathVariable("id") int comment_id){
Comment comment = commentService.findById(comment_id);
return comment;
}
public Comment updateComment(@PathVariable("id")int comment_id,@PathVariable("author") String author){
Comment comment = commentService.findById(comment_id);
comment.setAuthor(author);
Comment updateComment = commentService.updateComment(comment);
return updateComment;
}
public void deleteComment(@PathVariable("id") int comment_id){
commentService.deleteComment(comment_id);
}
}
基于注解实现缓存
在主启动类上添加注解:@EnableCaching
该注解配置在类上,通常都是在主启动类上
在需要加缓存的方法上加一个注解:@Cacheable
@Cacheable(cacheNames = "comment") // 通常用过缓存数据
@CachePut(cacheNames = "comment", key = "#result.id") // 用作更新数据
@CacheEvict(cacheNames = "comment") // 用作删除缓存数据
@Service
public class CommentService {
@Autowired
private CommentRepository commentRepository;
@Cacheable(cacheNames = "comment", unless = "#result==null")
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.getAuthor(), comment.getId());
return comment;
}
@CacheEvict(cacheNames = "comment")
public void deleteComment(int comment_id) {
commentRepository.deleteById(comment_id);
}
}
@Controller
@ResponseBody
public class CommentController {
@Autowired
private CommentService commentService;
@RequestMapping("/get/{id}")
public Comment findById(@PathVariable("id") int comment_id){
Comment comment = commentService.findById(comment_id);
return comment;
}
@RequestMapping("/update/{id}/{author}")
public Comment updateComment(@PathVariable("id")int comment_id,@PathVariable("author") String author){
Comment comment = commentService.findById(comment_id);
comment.setAuthor(author);
Comment updateComment = commentService.updateComment(comment);
return updateComment;
}
@RequestMapping("/{id}")
public void deleteComment(@PathVariable("id") int comment_id){
commentService.deleteComment(comment_id);
}
}
基于API实现缓存
使用RedisTemplate
@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();
// 将查询结果放缓存中,时间为1天
redisTemplate.opsForValue().set("comment_"+comment_id,comment,1, TimeUnit.DAYS);
return comment;
}else {
return null;
}
}
}
public Comment updateComment(Comment comment){
commentRepository.updateComment(comment.getAuthor(),comment.getId());
// 更新数据后,将缓存数据也更新
redisTemplate.opsForValue().set("comment_"+comment.getId(),comment);
return comment;
}
public void deleteComment(int comment_id){
commentRepository.deleteById(comment_id);
// 删除后,将缓存也删除
redisTemplate.delete("comment_"+comment_id);
}
}
@Controller
@ResponseBody
@RequestMapping("api")
public class ApiCommentController {
@Autowired
private ApiCommentService commentService;
@RequestMapping("/get/{id}")
public Comment findById(@PathVariable("id") int comment_id){
Comment comment = commentService.findById(comment_id);
return comment;
}
@RequestMapping("/update/{id}/{author}")
public Comment updateComment(@PathVariable("id")int comment_id,@PathVariable("author") String author){
Comment comment = commentService.findById(comment_id);
comment.setAuthor(author);
Comment updateComment = commentService.updateComment(comment);
return updateComment;
}
@RequestMapping("/{id}")
public void deleteComment(@PathVariable("id") int comment_id){
commentService.deleteComment(comment_id);
}
}
基于API自定义序列化缓存
上面的缓存机制是jdk,给我们的阅读造成了很大的困扰。我们可以考虑考虑使用自定义的缓存机制;
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
我们可以参照这种写法来自定义一个序列化机制
RedisConfig.java
@Configuration
public class RedisConfig {
public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
// 使用json格式序列化对象,对缓存数据key和value进行转换
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
// 解决查询缓存转换异常的问题
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
// 设置RedisTemplate模板API的序列化方式为json
template.setDefaultSerializer(serializer);
return template;
}
}
使用redis做中间件为缓存查询验证码
首先我们还是需要先定义好阿里云短信工具类
import java.util.HashMap;
import java.util.Map;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
public class AliyunSmsUtil {
/**
* 产品名称:云通信短信API产品
*/
private static final String PRODUCT = "Dysmsapi";
/**
* 产品域名
*/
private static final String DOMAIN = "dysmsapi.aliyuncs.com";
/**
* todo 开发者自己的AK(在阿里云访问控制台寻找)
*/
private static final String ACCESS_KEY_ID = "LTAI4GCJ4nhyB3sCQj7ftJ9P";
/**
* todo accessKeySecret(在阿里云访问控制台寻找)
*/
private static final String ACCESS_KEY_SECRET = "og4lIDixHgVlEYYXuHC9duhhZr0QT2";
/**
* todo 必填:短信签名
*/
private static final String SIGN_NAME = "ABC商城";
/**
* todo 必填:REGION_ID
*/
private static final String REGION_ID = "cn-hangzhou";
/**
* 发送方法
*
* @param phone 电话号码
* @param templateCode 模板编号
* @param templateParam 模板中的参数
* @return 返回值
* @throws ClientException 异常
*/
public static SendSmsResponse sendSms(String phone, String templateCode, String templateParam) throws ClientException {
//可自助调整超时时间
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000");
//初始化acsClient,暂不支持region化
IClientProfile profile = DefaultProfile.getProfile(REGION_ID, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
DefaultProfile.addEndpoint(REGION_ID, REGION_ID, PRODUCT, DOMAIN);
IAcsClient acsClient = new DefaultAcsClient(profile);
SendSmsRequest request = new SendSmsRequest();
request.setPhoneNumbers(phone);
request.setSignName(SIGN_NAME); // 短信签名
request.setTemplateCode(templateCode);
request.setTemplateParam(templateParam);
//可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
request.setOutId("10000");
//hint 此处可能会抛出异常,注意catch
SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
return sendSmsResponse;
}
/**
* 生成六位随机数
*
* @return
*/
public static String createRandomVcode() {
//验证码
StringBuilder vcode = new StringBuilder();
for (int i = 0; i < 6; i++) {
vcode.append((int) (Math.random() * 9));
}
return vcode.toString();
}
/*
* 调用发送短信的接口,返回去一个map,用来取参数
* */
public static HashMap<String, Object> start(String phone) throws ClientException {
HashMap<String, Object> codeMap = new HashMap<>();
String code = createRandomVcode();
// 随机生成6位数,添加在map参数中
codeMap.put("code", code);
String templateParam = JSONObject.toJSONString(codeMap); // 转json之后清空,方便多次使用
String templateCode = "SMS_205138182";
SendSmsResponse response = sendSms(phone, templateCode, templateParam);
return codeMap;
}
}
封装好了验证码之后,调用start方法,传入手机号,将获取到的验证码存在HashMap中使用,通过map.get(“code”)取出验证码数字。
加入springboot-redis的maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
在yml中加入redis连接属性
spring:
redis:
port: 6379
host: localhost
password:
修改掉redisTemplate的默认序列化方式,开启redis缓存
@EnableCaching // 开启缓存
@Configuration
public class RedisConfig {
/*
* 实现自定义序列化方式
* */
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.json());
template.setValueSerializer(RedisSerializer.string());
template.setHashValueSerializer(RedisSerializer.json());
return template;
}
}
测试缓存
@SpringBootTest
class JqueryApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
private static String phone = "17628263062";
@Test
void contextLoads() throws ClientException {
String code = AliyunSmsUtil.start(phone).get("code");
redisTemplate.opsForValue().set(phone, code, 1, TimeUnit.DAYS); // 将短信存在redis缓存中,有效时间为1分钟
/*
* 在登录时只需要将redis中存入的短信取出来就行
* */
String tell = (String) redisTemplate.opsForValue().get(phone);
System.out.println(tell);
System.out.println(redisTemplate);
}
}