一、搭建基本环境
1、导入数据库文件 创建department和employee表
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`departmentName` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`lastName` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`gender` int(2) DEFAULT NULL,
`d_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO `employee` VALUES ('1', '李四', '[email protected]', '1', '1');
INSERT INTO `employee` VALUES ('2', '张三', '[email protected]', '1', '1');
2、创建javabean封装数据
Department .java
package com.wzw.cache.bean;
public class Department {
private Integer id;
private String departmentName;
public Department() {
super();
// TODO Auto-generated constructor stub
}
public Department(Integer id, String departmentName) {
super();
this.id = id;
this.departmentName = departmentName;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDepartmentName() {
return departmentName;
}
public void setDepartmentName(String departmentName) {
this.departmentName = departmentName;
}
@Override
public String toString() {
return "Department [id=" + id + ", departmentName=" + departmentName + "]";
}
}
Employee .java
package com.wzw.cache.bean;
import java.io.Serializable;
public class Employee implements Serializable {
private Integer id;
private String lastName;
private String email;
private Integer gender; //性别 1男 0女
private Integer dId;
public Employee() {
super();
}
public Employee(Integer id, String lastName, String email, Integer gender, Integer dId) {
super();
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.dId = dId;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Integer getdId() {
return dId;
}
public void setdId(Integer dId) {
this.dId = dId;
}
@Override
public String toString() {
return "Employee [id=" + id + ", lastName=" + lastName + ", email=" + email + ", gender=" + gender + ", dId="
+ dId + "]";
}
}
3、整合Mybatis操作数据库
1)POM中导入需要的依赖,配置数据源信息
pom文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency>
properties配置文件
spring.datasource.url=jdbc:mysql://localhost:3306/spring_cache?serverTimezone=UTC //不加serverTimezone=UTC的话,运行时会时区报错
spring.datasource.username=root
spring.datasource.password=root
2)使用注解版的Mybatis
在SpringBoot启动类加上@MapperScan,指定需要扫描的mapper接口所在的包
二、给需要缓存的类加上注解
1、开启基于注解的缓存@EnableCaching
SpringBoot启动类加上开启注解@EnableCaching
2、标注缓存注解
@Cacheable
/**
* 将方法的运行结果进行缓存,以后再有相同的数据就直接从缓存中取,不再调用方法
* CacheManager管理多个Cache组件,对缓存的CRUD操作在Cache组件中,每一个缓存组件有自己唯一的名字
* @Cacheable的几个属性
* value/cacheNames:指定缓存组件的名称,将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存
* key:缓存数据使用的Key,可以用它来指定,key默认是使用方法参数的值,value它的值就是方法的返回值
* 编写SpEL:#id:参数id的值 #root.args[0]:第0个参数的值
* key = "#root.methodName+'['+#id+']'"————key的值就是getEmp[2]
* keyGenerator:key的生成器,可以自己指定key的生成器的组件Id
* keyGenerator/key:二选一使用
* cacheManager:指定缓存管理器,或者cacheResolver指定缓存解析器
* cacheManagercacheResolver/二选一
* condition:指定符合条件的情况下才缓存 condition = "#id>0":#id大于0才缓存
* unless:否定缓存;当unless指定条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断
* unless="#result==null"
* sync:是否使用异步模式
* @param id
* @return
*/
@Cacheable(cacheNames = "emp"/*,keyGenerator = "myKeyGenerator",condition = "#a0>1",unless = "#a0==2"*/)
public Employee getEmp(Integer id){
System.out.println("查询"+id+"号员工");
return employeeMapper.getEmpById(id);
}
@CachePut
/**
* @CachePut :既调用方法又更新缓存,修改了数据库某个数据,同时更新缓存
* 1、先调用目标方法,
* 2、将目标方法的结果缓存起来
* 测试:
* 1、查询1号员工,结果会存在缓存中
* 2、更新1号员工
* 3、结果没更新
* 因为之前1号员工的key保存的是1,而更新1号员工时,默认的key是employee对象
* 4、指定更新的员工的key
* key = "#employee.id",可以从传入的参数的属性取值
* key = "#result.id",这个方法返回了值,也可以从返回值的属性取值
* @param employee
* @return
*/
@CachePut(value = "emp",key = "#employee.id")
public Employee updateEmp(Employee employee){
System.out.println("调update方法:"+employee);
employeeMapper.updateEmployee(employee);
return employee;
}
@CacheEvict
/**
* @CacheEvict 缓存清除
* allEntries = true:清除emp这个缓存中所有缓存
* beforeInvocation = false:缓存的清除是否在方法之前执行,默认是在方法执行之后执行,如果出现异常,缓存就不会清除
* beforeInvocation = true:代表清除缓存在方法之前运行,无论是否出现异常,缓存都清除
* @param id
*/
@CacheEvict(value = "emp",allEntries = true)
public void deleteEmp(Integer id){
System.out.println("删除员工:"+id);
employeeMapper.deleteEmpById(id);
}
三、整合Redis作为缓存
1、安装Redis
引入redis的starter
最开始的POM已经引入过了
3、配置redis
这里就配置redis的ip地址即可
spring.redis.host=192.168.43.223
4、测试缓存
编写Mapper接口
package com.wzw.cache.mapper;
@Repository
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 updateEmployee(Employee employee);
@Delete("delete from employee where id=#{id}")
public void deleteEmpById(Integer id);
@Options(useGeneratedKeys = true,keyProperty = "id")
@Insert("insert into employee(lastName,email,gender,d_id) values(#{lastName},#{email},#{gender},#{dId})")
public void insertUser(Employee employee);
@Select("select * from employee where lastName=#{lastName}")
Employee getEmpByLastName(String lastName);
}
引入redis的starter,CacheManager就会变为RedisCacheManager,直接保存在Redis中。
package com.wzw.cache;
import com.wzw.cache.bean.Employee;
import com.wzw.cache.mapper.EmployeeMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
@SpringBootTest
class Springboot01CacheApplicationTests {
@Autowired
EmployeeMapper employeeMapper;
@Autowired
RedisTemplate redisTemplate; //操作k-v都是对象的
@Autowired
StringRedisTemplate stringRedisTemplate; //因为操作字符串的比较多,所以单独列出来一个StringRedisTemplate操作k-v都是字符串的
@Autowired
RedisTemplate<Object, Employee> empRedisTemplate;
/**
* redis常见的五大数据类型
* String(字符串)
* stringRedisTemplate.opsForValue()
* List(列表)
* stringRedisTemplate.opsForList()
* Set(集合)
* stringRedisTemplate.opsForSet()
* Hash(散列)
* stringRedisTemplate.opsForHash()
* ZSet(有序集合)
* stringRedisTemplate.opsForZSet()
*/
@Test
void testRedis(){
// stringRedisTemplate.opsForValue().append("key1","value1");
// System.out.println(stringRedisTemplate.opsForValue().get("key1"));
stringRedisTemplate.opsForList().leftPushAll("mylist","1","2","3","4");
// System.out.println(stringRedisTemplate.opsForList().range("mylist",0,-1));
}
//测试保存对象
@Test
void tetRedisObject(){
//默认如果保存对象,使用Jdk序列化机制,序列化后的数据保存到redis中
// redisTemplate.opsForValue().set("emp1",employeeMapper.getEmpById(1));
/**
* 1.将数据以json方式保存,
* (1)自己将对象转成json
* (2)redisTemplate默认的序列化规则,改变默认的序列化规则
*/
empRedisTemplate.opsForValue().set("emp1",employeeMapper.getEmpById(1));
System.out.println(empRedisTemplate.opsForValue().get("emp1"));
}
}
默认保存数据k-v都是Object,利用序列化保存,在Redis Desktop中查看到的是序列化后的结果
默认创建的RedisCacheManager操作Redis的时候使用的是RedisTemplate<Object,Objet>
想要直接在Redis中查看到对象,如下所示,需要编写配置类
编写配置类
@Configuration
public class MyRedisConfig extends CachingConfigurerSupport {
/**
* 配置序列化,作为一个对象存在Redis中,而不是jdk的序列化机制
*
* @param redisConnectionFactory
* @return
* @throws UnknownHostException
*/
@Bean
public RedisTemplate<Object, Employee> empRedisTemplate(RedisConnectionFactory redisConnectionFactory
) throws UnknownHostException {
RedisTemplate<Object, Employee> template = new RedisTemplate<Object, Employee>();
template.setConnectionFactory(redisConnectionFactory);
// Jackson2JsonRedisSerializer<Employee> ser = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
GenericJackson2JsonRedisSerializer ser = new GenericJackson2JsonRedisSerializer();
template.setDefaultSerializer(ser);
return template;
}
以上是测试的时候通过注入RedisTemplate就能改变默认的jdk序列化,作为一个对象存在Redis中,网页访问需要自定义配置类
自定义配置类
@Configuration
public class MyRedisConfig extends CachingConfigurerSupport {
/**
* 配置序列化,作为一个对象存在Redis中,而不是jdk的序列化机制
*
* @param redisConnectionFactory
* @return
* @throws UnknownHostException
*/
@Bean
public RedisTemplate<Object, Employee> empRedisTemplate(RedisConnectionFactory redisConnectionFactory
) throws UnknownHostException {
RedisTemplate<Object, Employee> template = new RedisTemplate<Object, Employee>();
template.setConnectionFactory(redisConnectionFactory);
// Jackson2JsonRedisSerializer<Employee> ser = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
GenericJackson2JsonRedisSerializer ser = new GenericJackson2JsonRedisSerializer();
template.setDefaultSerializer(ser);
return template;
}
/*
springboot2.0已经废弃
@Bean
public RedisCacheManager employeeCacheManager(RedisTemplate<Object, Employee> empRedisTemplate){
RedisCacheManager cacheManager=new RedisCacheManager(empRedisTemplate);
cacheManager.setUsePrefix(true);
}*/
//过期时间1天
private Duration timeToLive = Duration.ofDays(1);
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
//默认1
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(this.timeToLive)
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))
.disableCachingNullValues();
RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.transactionAware()
.build();
// log.debug("自定义RedisCacheManager加载完成");
return redisCacheManager;
}
private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
}
private RedisSerializer<Object> valueSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
}