一、创建springboot项目
二、pom.xml添加以下依赖
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-data-redis
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.1
mysql
mysql-connector-java
5.1.46
com.alibaba
druid
1.1.9
org.projectlombok
lombok
1.16.22
三、使用
1、启动类添加注解@EnableCaching
2、application.properties配置redis的host、post、password
#redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=tiger
3、application.properties配置mysql
#datasource
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/tiger?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&generateSimpleParameterMetadata=true
spring.datasource.username=tiger
spring.datasource.password=tiger
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.max-idle=10
spring.datasource.max-wait=60000
spring.datasource.min-idle=5
spring.datasource.initial-size=5
spring.datasource.validationQuery=select 'x'
4、创建学生信息表
CREATE TABLE student_info (
id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增',
student_id bigint(20) NOT NULL COMMENT '学号',
name varchar(64) NOT NULL COMMENT '姓名',
age int(2) NOT NULL COMMENT '年龄',
familly_address varchar(256) NOT NULL COMMENT '家庭地址',
created_date datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_date datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (student_id),
KEY id (id)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4
5、创建实体类:StudentInfo
package com.dl.cn.message.bean;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* Created by Tiger on 2018/10/8.
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StudentInfo implements Serializable{
private static final long serialVersionUID = 2597547944454691103L;
private Long id;
private Long studentId;
private String name;
private Integer age;
private String famillyAddress;
private Date createdDate;
private Date updatedDate;
}
6、mapper类:StudentInfoMapper
package com.dl.cn.message.mapper;
import com.dl.cn.message.bean.StudentInfo;
import org.apache.ibatis.annotations.*;
/**
* Created by Tiger on 2018/10/8.
*/
@Mapper
public interface StudentInfoMapper {
@Insert("insert into student_info(student_id,name,age,familly_address)" +
" values(#{studentId},#{name},#{age},#{famillyAddress})")
/**
* 通过bean保存实体类是,建议不要通过@Param注解,负责实体类的属性都在@Param中找
* */
void saveStudentInfo(StudentInfo studentInfo);
@Select("select * from student_info where student_id = #{studentId}")
StudentInfo findByStudentId(@Param("studentId") Long studentId);
@Update("update student_info set familly_address = #{famillyAddress},updated_date = now() ")
void updateFamillyAddress(@Param("studentId") Long studentId,@Param("famillyAddress") String famillyAddress);
}
7、service类:StudentInfoService
package com.dl.cn.message.service;
import com.dl.cn.message.bean.StudentInfo;
import com.dl.cn.message.mapper.StudentInfoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Created by Tiger on 2018/10/8.
*/
@Service
public class StudentInfoService {
@Autowired
StudentInfoMapper studentInfoMapper;
/**
* 保存学生信息
* @param studentInfo
* */
public void saveStudentInfo(StudentInfo studentInfo){
studentInfoMapper.saveStudentInfo(studentInfo);
}
/**
* 根据学号查学生信息
* @param studentId
* @return
* */
public StudentInfo findByStudentId(Long studentId){
return studentInfoMapper.findByStudentId(studentId);
}
/**
* 根据学号更新家庭地址
* @param studentId
* @param famillyAddress
* */
public void updateFamillyAddress(Long studentId,String famillyAddress){
studentInfoMapper.updateFamillyAddress(studentId,famillyAddress);
}
}
8、controller类:StudentInofController
package com.dl.cn.message.controller;
import com.dl.cn.message.service.StudentInfoService;
import com.dl.cn.message.bean.StudentInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* Created by Tiger on 2018/10/8.
*/
@RestController
@RequestMapping("/student")
@CacheConfig(cacheNames = "studentInfo")
@Slf4j
public class StudentInofController {
@Autowired
StudentInfoService studentInfoService;
/**
* 保存学生信息
* @param studentId
* @param name
* @param age
* @param famillyAddress
* */
@PostMapping("/save")
public void saveStudentInfo(@RequestParam("student_id") Long studentId,
@RequestParam("name") String name,
@RequestParam("age") Integer age,
@RequestParam("familly_address") String famillyAddress){
StudentInfo studentInfo = StudentInfo.builder()
.studentId(studentId)
.name(name)
.age(age)
.famillyAddress(famillyAddress)
.build();
studentInfoService.saveStudentInfo(studentInfo);
}
/**
* 根据学号查学生信息
* @param studentId
* @return
* */
@PostMapping("/findByStudentId")
@Cacheable(key = "#studentId",unless = "#result == null")
public StudentInfo findByStudentId(@RequestParam("student_id") Long studentId){
log.info("Get student information based on student number:{}",studentId);
System.out.println("查询信息>>>"+studentId);
return studentInfoService.findByStudentId(studentId);
}
@PostMapping("/updateFamillyAddress")
//删除对应key的缓存
@CacheEvict(key = "#studentId")
public void updateFamillyAddress(@RequestParam("student_id") Long studentId,
@RequestParam("familly_address") String famillyAddress){
studentInfoService.updateFamillyAddress(studentId,famillyAddress);
}
}
四、说明
1、缓存的实体类必须序列号,IDEA可以安装serialVersionUID插件,然后自己定义快捷键
2、@Cacheable注解,缓存到redis
cacheNames:指定缓存的名称
key:定义组成的key值,如果不定义,则使用全部的参数计算一个key值。可以使用spring El表达式
condition:在执行方法之前条件判断,满足条件缓存,负责不缓存
unless:在执行方法之后条件判断,不满足条件返回true,满足条件返回false
key 的值可以指定没固定值,也可以取方法参数,例如:key = "#studentId"
sync:redis中有值时,多个线程可以同时访问,如果没有值,只允许一个线程查询
3、@CacheEvict注解,删除指定的key
cacheNames:指定缓存的名称
key:定义组成的key值,如果不定义,则使用全部的参数计算一个key值。可以使用spring El表达式
4、@cachePut注解,更新数据之后,更新redis对应key的值,和@Cacheable配套使用,但是添加这两个注解的方法,返回必须一样
5、@CacheConfig注解,作用域是当前类,这样不需要每个类设置cacheName的值
五、测试结果
1、在方法findByStudentId添加@Cacheable注解,预期结果是第一次查询从mysql获取信息,此后从redis获取信息
第一次进入了方法体:
第二次、第三次之后没有进入方法体,说明缓存成功了,查看redis,找到对应的key
2、在方法updateFamillyAddress上添加了注解@CacheEvict,预期结果是根据学号修改学生家庭住址,在redis删除对应key的值,然后重新从mysql获取信息
将13240115对应的学生家庭地址修改为北京,redis对应的key被删除
再次获取13240115学生的信息,从mysql获取信息
六、问题
1、从mysql查询的字段,student_id,created_date,update_date,familly_address值为null,因为在application.properties没有开启mybatis驼峰命名的配置
#mybatis
#开启mybatis驼峰命名,这样可以将mysql中带有下划线的映射成驼峰命名的字段
mybatis.configuration.map-underscore-to-camel-case=true
2、对返回没有进行空值判断,缓存出现异常
java.lang.IllegalArgumentException: Cache 'studentInfo' does not allow 'null' values. Avoid storing null via '@Cacheable(unless="#result == null")' or configure RedisCache to allow 'null' via RedisCacheConfiguration
解决方法:加上unless属性 @Cacheable(key = "#studentId",unless = "#result == null")
3、redis缓存的key没有过期时间,如何给对应缓存设置过期时间
springboot2.0以后的配置方法
cacheNames中可以设置多个缓存名,然后分别设置过期时间,这里设置了2分钟!
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
//生成一个默认配置,通过config对象即可对缓存进行自定义配置
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
// 设置缓存的默认过期时间,也是使用Duration设置
config = config.entryTtl(Duration.ofSeconds(60*2))
.disableCachingNullValues();// 不缓存空值
//设置一个初始化的缓存空间set集合
Set cacheNames = new HashSet<>();
cacheNames.add("studentInfo");
// 对每个缓存空间应用不同的配置
Map configMap = new HashMap<>();
configMap.put("studentInfo", config);
RedisCacheManager cacheManager = RedisCacheManager.builder(factory) // 使用自定义的缓存配置初始化一个cacheManager
.initialCacheNames(cacheNames) // 注意这两句的调用顺序,一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
.withInitialCacheConfigurations(configMap)
.build();
return cacheManager;
}