相关注解
@EnableCaching //在启动类上加上注解启动缓存
//作用在你要缓存的数据上
@Cacheable(key="#id",cacheNames="com.coydone.service.impl.MenuServiceImpl")
@Cacheput //解决脏读
@CachEvict//(解决脏读)
@Cacheconfig//(全局的配置缓存)
相关概念
-
脏读
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
例如:
张三的工资为5000,事务A中把他的工资改为8000,但事务A尚未提交。 与此同时,事务B正在读取张三的工资,读取到张三的工资为8000。 随后,事务A发生异常,而回滚了事务。张三的工资又回滚为5000。 最后,事务B读取到的张三工资为8000的数据即为脏数据,事务B做了一次脏读。
-
不可重复读
是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
例如:
在事务A中,读取到张三的工资为5000,操作没有完成,事务还没提交。 与此同时, 事务B把张三的工资改为8000,并提交了事务。 随后,在事务A中,再次读取张三的工资,此时工资变为8000。在一个事务中前后两次读取的结果并不致,导致了不可重复读。
-
幻读
是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
例如:
目前工资为5000的员工有10人,事务A读取所有工资为5000的人数为10人。 此时,事务B插入一条工资也为5000的记录。 这时,事务A再次读取工资为5000的员工,记录为11人。此时产生了幻读。
使用
原理:
项目结构:
1、创建SpringBoot工程
2、配置application.yml
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/k0503db?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
username: root
password: root123
type: org.springframework.jdbc.datasource.DriverManagerDataSource
redis:
host: 192.168.81.130
port: 6379
jedis:
pool:
max-active: 20
max-idle: 8
min-idle: 0
max-wait: 2000
mybatis:
type-aliases-package: com.coydone.entity
# 指定sql映射文件的位置
mapper-locations: classpath:mapper/*Mapper.xml
configuration:
log-prefix: mybatis
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3、使用Mybatis逆向工程生成实体类(entity)、mapper接口、mapper.xml文件。
4、编写service层接口和实现类。在service的实现类上配置Redis的缓存。
package com.coydone.service;
import com.coydone.entity.Student;
public interface StudentService{
int deleteByPrimaryKey(Integer xh);
int insert(Student record);
Student selectByPrimaryKey(Integer xh);
int updateByPrimaryKey(Student record);
}
package com.coydone.service.impl;
import com.coydone.entity.Student;
import com.coydone.entity.StudentExample;
import com.coydone.mapper.StudentMapper;
import com.coydone.service.StudentService;
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.List;
@Service
public class StudentServiceImpl implements StudentService{
@Autowired
private StudentMapper studentMapper;
@Override
// #id el表达式,代表去id的值
@CacheEvict(cacheNames="student",key="#xh")
public int deleteByPrimaryKey(Integer xh) {
return studentMapper.deleteByPrimaryKey(xh);
}
@Override
// 每次都把数据放入缓存里面 result 代表返回值
//@CachePut(cacheNames="student",key="#result.xh")
@CachePut(cacheNames="student",key="#record.xh")
public int insert(Student record) {
return studentMapper.insert(record);
}
@Override
@Cacheable(cacheNames="student",key="#xh")
public Student selectByPrimaryKey(Integer xh) {
return studentMapper.selectByPrimaryKey(xh);
}
@Override
@CachePut(cacheNames="student",key="#record.xh")
public int updateByPrimaryKey(Student record) {
return studentMapper.updateByPrimaryKey(record);
}
}
5、配置Redis、启动类开启Redis。
创建Redis的配置类,让Redis的序列化形式为json。
package com.coydone.config;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
@Configuration
public class RedisConfig {
@Bean
public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
CacheProperties.Redis redisProperties = cacheProperties.getRedis();
RedisCacheConfiguration config = RedisCacheConfiguration
.defaultCacheConfig();
config = config.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()));
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
config = config.prefixKeysWith(redisProperties.getKeyPrefix());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
}
在启动类上启动缓存
package com.coydone;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@MapperScan("com.coydone.mapper")
@EnableCaching //启用缓存
public class BootRedisCache03Application {
public static void main(String[] args) {
SpringApplication.run(BootRedisCache03Application.class, args);
}
}
6、测试。
package com.coydone.service;
import com.coydone.entity.Student;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class StudentServiceTest {
@Autowired
private StudentService studentService;
@Test
public void selectByPrimaryKeyTest(){
Student student = studentService.selectByPrimaryKey(106);
System.out.println(student);
}
}
以查询一个来说,但第一次执行时,会查询数据库,将数据库的数据存到Redis中,当再次查询Redis中已经存在的数据时,会直接从Redis中获取数据,而不会走MySQL数据库。
我们查询出的结果在项目中配置了以json的格式序列化,可以查看其保存的格式:
其它
1、在配置缓存注解时可以使用多个变量作为Redis的key。
@CachePut(cacheNames="student",key="#student.xh+'-'+#student.age")
2、可以修改序列化方式,默认的序列化方式为jdk的序列化方式,我们可以配置为json方式,实际开发中都是json形式。
3、注解缓存缓存的是当前注解所在方法的返回值,cacheNames表示缓存的前缀,key 代表内容,CachePut相当于jedis.set()方法,result代表当前方法执行完成之后的返回值对象。