springboot 配置mybatis的二级缓存机制
一级缓存 是sqlSession级别的缓存,面向单个访问用户
二级缓存 是namespace级别的缓存,同一个namespace内查询会缓存,一旦发生增删改操作则会清空该namespace缓存,二级缓存面向所有访问用户;二级缓存默认使用hashMap缓存,支持自定义缓存:mybatis支持encache和redis
mybatis 开启二级缓存的两种方式
首先配置 开启MyBatis的全局二级缓存
.properties文件或.yml 文件 增加配置
#推荐
mybatis.configuration.cache-enabled=true
或者
mybatis-config.xml文件增加配置
<settings>
<!--显示的开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
每个namespace只能选择其中一种方式
1.注解开启
//RedisCache.class为自定义配置类,实现org.apache.ibatis.cache.Cache接口
@CacheNamespace(implementation = RedisCache.class,eviction = RedisCache.class)
注解方式适用于持久层使用@Select、@insert等注解配置SQL语句
如果使用默认hashMap缓存,则@CacheNamespace
2.mapper文件开启
持久层mapper文件增加标签开启本mapper namespace下的二级缓存
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
属性说明
a. eviction:清除策略为FIFO缓存,先进先出原则,默认的清除策略是 LRU
b. flushInterval:属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量
c. size:最多可以存储结果对象或列表的引用数
d. readOnly:只读属性,可以被设置为 true 或 false。
mybatis开启redis二级缓存 demo代码
1.redis缓存配置类:实现org.apache.ibatis.cache.Cache接口
/**
* @program: test
* @description: mybatis 使用redis实现二级缓存
* @author: 闲走天涯
* @create: 2021-08-24 16:32
*/
public class RedisCache implements Cache {
//读写锁
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
private RedisTemplate<String,Object> redisTemplate;
//缓存实例id 名称
private String id;
public RedisCache(String id) {
if(id==null){
throw new IllegalArgumentException("为获取到缓存实例id");
}
this.id = id;
}
//返回cache的唯一名称
public String getId() {
return id;
}
/**
* 获取redisTemplate对象
* @return
*/
public RedisTemplate<String, Object> getRedisTemplate() {
return ApplicationContextHolder.getBean("redisTemplate");
}
/**
* 缓存存值
* @param key
* @param value
*/
@Override
public void putObject(Object key, Object value) {
getRedisTemplate().opsForHash().put(id,key.toString(),value);
}
/**
* 缓存取值
* @param key
* @return
*/
@Override
public Object getObject(Object key) {
return getRedisTemplate().opsForHash().get(id,key.toString());
}
/**
* 保留方法
* @param key
* @return
*/
@Override
public Object removeObject(Object key) {
return null;
}
/**
* 清空缓存,在增删改时会自动调动
*/
@Override
public void clear() {
getRedisTemplate().delete(id);
}
/**
* 获取缓存数量
* @return
*/
@Override
public int getSize() {
return getRedisTemplate().opsForHash().size(id).intValue();
}
/**
* 获取读写锁对象
* @return
*/
@Override
public ReadWriteLock getReadWriteLock() {
return this.readWriteLock;
}
}
2.配置获取容器中redisTemplate对象:实现ApplicationContextAware 类
/**
* @program: test
* @description: 容器类
* @author: 闲走天涯
* @create: 2021-08-24 17:01
*/
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationContextHolder.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
checkApplicationContext();
return applicationContext;
}
/**
* 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
* @param name
* @param <T>
* @return
*/
public static <T> T getBean(String name) {
checkApplicationContext();
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> clazz){
checkApplicationContext();
return applicationContext.getBean(clazz);
}
/**
* 清除applicationContext静态变量.
*/
public static void cleanApplicationContext() {
applicationContext = null;
}
/**
* 检查是否容器中是否存在
*/
private static void checkApplicationContext() {
if (applicationContext == null) {
throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义SpringContextHolder");
}
}
}
3.学生信息vo类
/**
* 描述:
* 学生信息 vo类,需要实现序列化接口
* @author 闲走天涯
* @create 2021/7/19 15:33
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@ApiModel(value = "MerchantVo", description = "商户信息")
public class Student implements Serializable {
@ApiModelProperty(value = "学生id,对应student表 s_id")
private Long sId;
@ApiModelProperty(value = "学生姓名,对应student表 s_name")
private String sName;
@ApiModelProperty(value = "学生生日,对应student表 s_birth")
private String sBirth;
@ApiModelProperty(value = "学生性别,对应student表 s_sex")
private String sSex;
}
4.持久层 接口 注解方式
/**
* @program: test
* @description: 二级缓存dao
* @author: 闲走天涯
* @create: 2021-08-24 15:11
*
* @CacheNamespace 开启二级缓存 (根据命名空间namespace,例如com.cache.dao.CacheDao)
* implementation 属性:默认是PerpetualCache类,即hashMap实现
* 如果该命名空间namespace发生增删改操作,则会将该空间的缓存全部清空
*/
@CacheNamespace(implementation = RedisCache.class,eviction = RedisCache.class)
@Repository
public interface CacheDao {
@Select({
"<script>",
"select * from student s",
"<where>",
"<if test='sId!=null and sId!='''>",
"AND s.s_id = #{sId}",
"</if>",
"<if test='sName!=null and sName!='''>",
"AND s.s_name = #{sName}",
"</if>",
"</where>",
"</script>",
})
Page<Student> getStudentByPage(Student student);
@Select("select * from student s where s.s_id=#{sid}")
Student getStudentById(@Param("sid")Long sid);
@Update({
"<script>",
"update student",
"<set>",
"<if test='sName!=null and sName!='''>",
"s_name=#{sName},",
"</if>",
"<if test='sBirth!=null and sBirth!='''>",
"s_birth=#{sBirth},",
"</if>",
"<if test='sSex!=null and sSex!='''>",
"s_sex=#{sSex},",
"</if>",
"</set>",
"<where>",
"s_id = #{sId}",
"</where>",
"</script>",
})
int updateStudent(Student student);
}
5.service接口 和 serviceImpl 业务层实现类
/**
* @program: test
* @description: mybatis 二级缓存service
* @author: 闲走天涯
* @create: 2021-08-24 15:12
*/
public interface CacheService {
PageInfo<Student> getStudentByPage(Student student,Integer pageNum,Integer pageSize);
Student getStudentById(Long sid);
int updateStudent(Student student);
}
/**
* @program: test
* @description: 二级缓存serviceimpl
* @author: 闲走天涯
* @create: 2021-08-24 15:14
*/
@Service
public class CacheServiceImpl implements CacheService {
@Autowired
private CacheDao cacheDao;
/**
* 分页查询学生
* @param student
* @return
*/
@Override
public PageInfo<Student> getStudentByPage(Student student,Integer pageNum,Integer pageSize) {
if (pageNum == null || pageNum <= 0) pageNum = 1;
if (pageSize == null || pageSize <= 0) pageSize = 10;
PageHelper.startPage(pageNum, pageSize);
Page<Student> page = cacheDao.getStudentByPage(student);
return new PageInfo<>(page);
}
/**
* 根据id获取学生信息
* @param sid
* @return
*/
@Override
public Student getStudentById(Long sid){
return cacheDao.getStudentById(sid);
}
/**
* 根据id修改学生信息
* @param student
* @return
*/
@Override
public int updateStudent(Student student){
int i = cacheDao.updateStudent(student);
if(i==1){
return i;
}else{
throw new RuntimeException("修改异常");
}
}
}
6.控制层controller
/**
* @program: test
* @description: mybatis 开启二级缓存 (自定义缓存:mybatis支持encache和redis)
* 一级缓存以sqlSession为主,面向单个访问用户
* 二级缓存以namespace为标识,同一个namespace内查询会缓存,一旦发生增删改操作则会清空该namespace缓存,二级缓存面向所有访问用户
* @author: 闲走天涯
* @create: 2021-08-24 15:10
*/
@RestController
@RequestMapping(value = "/cache")
public class CacheController {
@Autowired
private CacheService cacheService;
/**
* 分页查询学生信息
* @param student
* @param pageNum
* @param pageSize
* @return
*/
@RequestMapping("/getStudentByPage")
public JsonBean getStudentByPage(Student student,Integer pageNum,Integer pageSize){
try {
PageInfo<Student> pageInfo = cacheService.getStudentByPage(student,pageNum,pageSize);
return new JsonBean(true, "查询成功", 1, pageInfo);
}catch (Exception e){
e.printStackTrace();
}
return new JsonBean(false,"查询异常",0,null);
}
/**
* 根据学生id查询学生信息
* @param sid
* @return
*/
@RequestMapping(value = "/getStudentById")
public JsonBean getStudentById(Long sid){
try{
if(!VerifyData.longIsNotNull(sid)){
return new JsonBean(false,"参数不能为空",0,null);
}
Student student = cacheService.getStudentById(sid);
return new JsonBean(true,"查询成功",1,student);
}catch (Exception e){
e.printStackTrace();
}
return new JsonBean(false,"查询异常",0,null);
}
/**
* 更细学生信息
* @param student
* @return
*/
@RequestMapping(value = "/updateStudent")
public JsonBean updateStudent(Student student){
try{
if(!VerifyData.longIsNotNull(student.getSId())){
return new JsonBean(false,"参数不能为空",0,null);
}
int i = cacheService.updateStudent(student);
return new JsonBean(true,"修改成功",1,i);
}catch (Exception e){
e.printStackTrace();
}
return new JsonBean(false,"修改异常",0,null);
}
}
7.测试
http://127.0.0.1:1234/cache/getStudentById?sid=5
http://127.0.0.1:1234/cache/getStudentByPage
查询redis hvals com.cache.dao.CacheDao:存在缓存数据,多次重复访问,不会再次访问数据库,从redis中获取
http://127.0.0.1:1234/cache/updateStudent?sId=7&sName=郑竹2
更新操作后缓存被清除
重新访问
http://127.0.0.1:1234/cache/getStudentById?sid=5
需要重新访问数据库