spring boot2.3.2 redis缓存
在学习中,发现springboot2.x对缓存进行了优化,使复写率大大降低。但对应的,源码逻辑更加复杂,需要经过精心阅读才可以发现其中的奥妙。
一、实现代码
下面说一下springboot2.3.2版本使用redis进行缓存的代码:
实体类:
必须实现序列化,这是缓存存取数据的基础
public class Employee implements Serializable{
private Integer id;
private String lastName;
private String email;
private Integer gender; //性别 1男 0女
private Integer dId;
//此处省略getter,setter方法
}
持久层:
这里使用mybatis进行持久化操作
@Mapper
public interface EmployeeMapper {
@Select("select * from employee where id=#{id}")
public Employee getEmpById(Integer id);
@Update("update employee set lastName=#{lastName},email=#{email},gender=#{gender},d_id=#{dId} where id=#{id}")
public void updateEmp(Employee employee);
@Delete("delete from employee where id=#{id}")
public void deleteEmp(Integer id);
@Insert("insert into employee (lastName,email,gender,d_id) values (#{lastName},#{email},#{gender},#{dId})")
public Employee insertEmp(Employee employee);
}
业务逻辑层:
缓存操作做好是写在业务逻辑层,方便更好的管理对应操作。
@Service
public class EmployeeService {
@Autowired
EmployeeMapper employeeMapper;
//此注解优先缓存中存取,如果缓存中有就不查询数据库再将数据存到redis做缓存,常用于查询操作
//cacheNames时缓存的名字,key是缓存数据的键,通过这两个能确定缓存中的唯一数据
@Cacheable(cacheNames={"emp"},key="#id"/*,condition = "#id>1"*/)//可以写spel表达式
public Employee getEmp(Integer id){
System.out.println("查询"+id+"号员工");
Employee emp = employeeMapper.getEmpById(id);
return emp;
}
//此注解先操作数据库中的数据(一定操作数据库),再将数据存到redis做缓存,常用于修改和新增操作
@CachePut(cacheNames={"emp"},key="#result.id")
public Employee updateEmp(Employee employee){
System.out.println("update执行了"+employee);
employeeMapper.updateEmp(employee);
return employee;
}
//此注解先操作数据库,再清除缓存数据(注意指定cacheNames和key)
@CacheEvict(cacheNames = {"emp"},key = "#id")
public void deleteEmp(Integer id){
System.out.println("删除了"+id+"号员工。");
// employeeMapper.deleteEmp(id);
}
}
控制层:
这里使用restful风格,方便测试。
@RestController
public class EmployeeController {
@Autowired
EmployeeService employeeService;
@GetMapping("/emp/{id}")
public Employee getEmployee(@PathVariable("id") Integer id){
Employee employee = employeeService.getEmp(id);
return employee;
}
@GetMapping("/emp")
public Employee updateEmpoyee(Employee employee){
Employee employee1 = employeeService.updateEmp(employee);
return employee1;
}
@GetMapping("/delemp/{id}")
public String deleteEmp(@PathVariable("id") Integer id){
employeeService.deleteEmp(id);
return "success";
}
}
配置文件application.properties
#配置mybatis
mybatis.mapper-locations=classpath:mapper/*Mapper.xml
mybatis.type-aliases-package=com.itjj.springboot.dao
#配置数据源
spring.datasource.url=jdbc:mysql://localhost:3306/spring_cache?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
#spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#开启驼峰命名规则:dId看成d_id
mybatis.configuration.map-underscore-to-camel-case=true
#开启测试报告
#debug=true
#打印某包下的日志
logging.level.com.itjj.springboot.mapper=debug
#配置redis
spring.redis.host=localhost
修改自定义序列化
序列化时spring默认使用jdk的序列化,我们难以阅读,所有使用json格式存储。
**注意:**此处操作再springboot2.x中进行了优化,下面配置只适用于springboot2.x
@Configuration
public class MyRedisConfiguration {
/*
//如果直接使用redis模板进行操作,进行这段操作,
//从默认的RedisConfiguration配置类中粘贴过来的,再进行自定义修改
@Bean
public RedisTemplate<Object, Employee> empRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Employee> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
//选择自己的序列化器,这选的时json
RedisSerializer redisSerializer = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
template.setDefaultSerializer(redisSerializer);
return template;
}
*/
//修改默认的序列化
//springboot1中,是每个实体类分别自定义配置CacheManager,配置方法是构造器传入上面自定义的RedisTemplate模板分别配置,且需要指定哪个为主配置
//springboot2中直接使用RedisCacheConfiguration进行自定义配置,再使用RedisCacheManager的builder方法返回RedisCacheManagerBuilder对象,在调用该对象的cacheDefaults方法将自定义的RedisCacheConfiguration传入,再调用其build方法创建CacheManager方法,总结起来就是如下方法
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofDays(1))
.disableCachingNullValues()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(factory).cacheDefaults(cacheConfiguration).build();
}
}
启动类:
@EnableCaching //开启基于注解的缓存
@MapperScan("com.itjj.springboot.mapper")//指定扫描mapper的包
@SpringBootApplication
public class SpringbootCacheApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootCacheApplication.class, args);
}
}
二、注意实现
在不使用缓存的时候, 用户实时查询的是数据库中的原生数据。不会出现查询结果与数据库数据不一致的问题。
而在使用redis缓存的时候,如果直接修改数据库中的数据(不经过程序),就会出现数据库中数据与缓存中数据不一致的问题,此时用户查询操作依然查的是缓存中的数据,解决此问题方法可以:修改数据库数据的同时,手动清除缓存中的对应数据。
此处依然有很多问题会发生,所以作为一个深入学习的启示,方便遇到问题时,了解问题产生的底层原理。