相關注解
@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代表當前方法執行完成之后的返回值對象。
