前面提到了,使用mybatis cache,一般是结合redis使用。
一、demo
1、数据表
create table demo.t_address
(
id int auto_increment
primary key,
address_name varchar(200) null,
address_code varchar(20) null,
address_type int null
);
项目结构:
2、pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>plus-mybatis-cache</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.4</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--尽量不要同时导入mybatis 和 mybatis_plus,避免版本差异-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
</dependencies>
</project>
3、配置文件
server.port=1112
server.servlet.context-path=/mybatisCacheDemo
#mysql
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3308/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=wtyy
#mybatis
mybatis.mapper-locations=classpath*:mapper/*Mapper.xml
#打印日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
# 开启二级缓存
mybatis-plus.configuration.cache-enabled=true
#redis
spring.data.redis.host=127.0.0.1
spring.data.redis.port=6379
spring.data.redis.password=
spring.data.redis.lettuce.pool.max-active=8
spring.data.redis.lettuce.pool.max-wait=-1
spring.data.redis.lettuce.pool.max-idle=8
spring.data.redis.lettuce.pool.min-idle=0
spring.data.redis.lettuce.pool.enabled=true
spring.data.redis.lettuce.pool.time-between-eviction-runs=30s
4、util
package com.pluscache.demo.util;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.lang.annotation.Annotation;
import java.util.Map;
@Component
public class ApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext ;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationContextUtil.applicationContext = applicationContext;
}
public static Object getBean(String name) {
return ApplicationContextUtil.applicationContext.getBean(name);
}
public static <T> T getBean(String name, Class<T> clazz) {
return applicationContext.getBean(name, clazz);
}
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
public static <T> Map<String, T> getBeansOfType(Class<T> clazz) {
return applicationContext.getBeansOfType(clazz);
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static <T extends Annotation> T getAnnotation(Object bean, Class<T> annotationClass) {
T annotation = bean.getClass().getAnnotation(annotationClass);
if (annotation == null) {
annotation = AopUtils.getTargetClass(bean).getAnnotation(annotationClass);
}
return annotation;
}
}
5、config
package com.pluscache.demo.config;
import com.pluscache.demo.util.ApplicationContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
@Slf4j
//@Component
public class MybatisRedisCache implements Cache {
/* * id是必须的带上的,这里的id会指定当前放入缓存的mapper的namespace
* 如这里的id就是com.sample.dao.IEmployeeDao
*/
private final String id;
private RedisTemplate redisTemplate;
public MybatisRedisCache(final String id) {
//获取redis实例
//redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");
//指定key的序列化方式
//redisTemplate.setKeySerializer(new StringRedisSerializer());
//redisTemplate.setHashKeySerializer(new StringRedisSerializer());
this.id = id;
}
@Override
public String getId() {
return id;
}
@Override
public void putObject(Object key, Object value) {
this.getRedisTemplate();
redisTemplate.opsForHash().put(id.toString(),key.toString(),value);
}
private void getRedisTemplate() {
if (redisTemplate == null) {
//由于启动期间注入失败,只能运行期间注入,这段代码可以删除
redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");
}
}
@Override
public Object getObject(Object key) {
this.getRedisTemplate();
return redisTemplate.opsForHash().get(id.toString(),key.toString());
}
@Override
public Object removeObject(Object key) {
return null;
}
@Override
public void clear() {
this.getRedisTemplate();
redisTemplate.delete(id.toString());
}
@Override
public int getSize() {
this.getRedisTemplate();
return redisTemplate.opsForHash().size(id.toString()).intValue();
}
}
6、dto
package com.pluscache.demo.dto;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName("t_address")
public class AddressDTO implements Serializable {
private Integer id;
private String addressName;
private String addressCode;
public Integer addressType;
}
7、dao
(1)repository
可以省略,移到service中
package com.pluscache.demo.repository;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.pluscache.demo.dto.AddressDTO;
import com.pluscache.demo.mapper.AddressMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
@Slf4j
public class AddressRepository {
@Autowired
private AddressMapper addressMapper;
//plus新增
public void insert(AddressDTO addressDTO) {
addressMapper.insert(addressDTO);
}
//手动SQL新增
public void save(AddressDTO addressDTO) {
addressMapper.save(addressDTO);
}
public AddressDTO getById(Integer id) {
LambdaQueryWrapper<AddressDTO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AddressDTO::getId, id);
return addressMapper.selectOne(queryWrapper);
}
public List<AddressDTO> listByType(Integer type) {
LambdaQueryWrapper<AddressDTO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AddressDTO::getAddressType, type);
return addressMapper.selectList(queryWrapper);
}
public List<AddressDTO> listByTypeRecord(Integer type) {
return addressMapper.listByTypeRecord(type);
}
public List<AddressDTO> listById(Integer id) {
LambdaQueryWrapper<AddressDTO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AddressDTO::getId, id);
return addressMapper.selectList(queryWrapper);
}
}
(2)mapper
在这里做的缓存
package com.pluscache.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pluscache.demo.config.MybatisRedisCache;
import com.pluscache.demo.dto.AddressDTO;
import org.apache.ibatis.annotations.CacheNamespace;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@CacheNamespace(implementation = MybatisRedisCache.class, eviction = MybatisRedisCache.class)
public interface AddressMapper extends BaseMapper<AddressDTO> {
void save(@Param("record") AddressDTO addressDTO);
List<AddressDTO> listByTypeRecord(@Param("type") Integer type);
}
(3)xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pluscache.demo.mapper.AddressMapper">
<cache-ref namespace="com.pluscache.demo.mapper.AddressMapper"/>
<insert id="save">
insert into t_address(address_name,
address_code,
address_type
) values (#{record.addressName},
#{record.addressCode},
#{record.addressType})
</insert>
<select id="listByTypeRecord" resultType="com.pluscache.demo.dto.AddressDTO">
select address_name,address_code from t_address where address_type =#{type}
</select>
</mapper>
8、service
package com.pluscache.demo.service.impl;
import com.pluscache.demo.dto.AddressDTO;
import com.pluscache.demo.repository.AddressRepository;
import com.pluscache.demo.service.AddressService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("addressService")
public class AddressServiceImpl implements AddressService {
@Autowired
private AddressRepository addressRepository;
@Override
public void insert(AddressDTO addressDTO) {
addressRepository.insert(addressDTO);
}
@Override
public void save(AddressDTO addressDTO) {
addressRepository.save(addressDTO);
}
@Override
public AddressDTO getById(Integer id) {
return addressRepository.getById(id);
}
@Override
public List<AddressDTO> listByType(Integer type) {
return addressRepository.listByType(type);
}
@Override
public List<AddressDTO> listByTypeRecord(Integer type) {
return addressRepository.listByTypeRecord(type);
}
@Override
public List<AddressDTO> listById(Integer id) {
return addressRepository.listById(id);
}
}
9、controller
package com.pluscache.demo.controller;
import com.pluscache.demo.dto.AddressDTO;
import com.pluscache.demo.service.AddressService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/address")
public class AddressController {
@Autowired
private AddressService addressService;
@RequestMapping("/insert")
public void insert(AddressDTO addressDTO) {
addressService.insert(addressDTO);
}
@RequestMapping("/save")
public void save(AddressDTO addressDTO) {
addressService.save(addressDTO);
}
@RequestMapping("/getById")
public AddressDTO getById(Integer id) {
return addressService.getById(id);
}
@RequestMapping("/listByType")
public List<AddressDTO> listByType(Integer type) {
return addressService.listByType(type);
}
@RequestMapping("/listByTypeRecord")
public List<AddressDTO> listByTypeRecord(Integer type) {
return addressService.listByTypeRecord(type);
}
@RequestMapping("/listById")
public List<AddressDTO> listById(Integer id) {
return addressService.listById(id);
}
}
10、启动类
package com.pluscache.demo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("com.pluscache.demo.mapper")
@SpringBootApplication
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class, args);
}
}
11、测试
(1)新增数据
新增了几条数据
(2)SQL法根据type查询
① 第一次访问
localhost:1112/mybatisCacheDemo/address/listByTypeRecord?type=1
后台打印:
查看redis生成了缓存
② 再次访问,没有查询db了
(3) mybatis-plus根据type查询
① 第一次访问localhost:1112/mybatisCacheDemo/address/listByType?type=1
后台从db查询
查看redis的缓存:可以看到又生成了一条缓存
② 再次访问,没有从数据库查询
(4)mybatis-plus根据id查询
① 第一次访问localhost:1112/mybatisCacheDemo/address/listById?id=1
走db查看
查看redis缓存:
② 再次访问,不再走db查询
(5)执行add
这里生成的id是自增长的,理论上,只需要清空type=1的缓存,不需要清空id=1的缓存。但是刷新redis可以看到,整个大key都清除了。