實測版本
<!-- tk.mybatis , 其中mybatis版本 3.5.3 -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.0.4</version>
</dependency>
<!-- springboot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
mybatis二級緩存相關(guān)注解
@org.apache.ibatis.annotations.CacheNamespace
- 作用于mapper接口上, 標(biāo)記當(dāng)前mapper開啟二級緩存
- 對應(yīng)mapper.xml文件中的
<cache/>標(biāo)簽
@CacheNamespace
public interface DeviceInfoMapper {
...
}
這種方式標(biāo)記的,對mapper.xml中的sql無效, 但是對于使用
@Select注解中的sql有效
@org.apache.ibatis.annotations.CacheNamespaceRef.CacheNamespace
標(biāo)記當(dāng)前mapper接口引用了指定class的緩存
- 對應(yīng)mapper.xml文件中的
<cache-ref/>標(biāo)簽
@CacheNamespaceRef(DeviceInfoMapper.class) // 標(biāo)記當(dāng)前mapper使用與 DeviceInfoMapper 類相同的namespace
public interface DeviceNameDefinitionMapper {
...
}
如果在這個接口中,有增刪改相關(guān)操作, 將會清空指定
namespace下的緩存,特別是有關(guān)聯(lián)查詢的幾個mapper,需要在同一個namespace下,防止緩存中的臟數(shù)據(jù)產(chǎn)生
@org.apache.ibatis.annotations.Property
- 標(biāo)記在接口方法上,用于傳入相關(guān)配置屬性
- 對應(yīng)mapper.xml文件中的
<property></property>標(biāo)簽
<cache>
<property name="cacheSec" value="600"/>
</cache>
開啟二級緩存
簡單講就是加注解,加配置
- springboot項目,如果沒有指定的mybatis.xml配置文件,建議在 yaml或 properties 配置文件中增加配置項:
mybatis:
configuration:
cache-enabled: true
如果有 mybatis.xml的配置文件, 在文件中增加配置
<settings>
<setting name = "cacheEnabled" value = "true" />
</settings>
- 對應(yīng)mapper.xml里增加
<!-- type屬性中,指定的緩存的實現(xiàn)類,這里用到自定義實現(xiàn),將緩存放到redis里,以確保分布式服務(wù)緩存一致 -->
<cache type="com.kartist.demo.common.config.MybatisRedisCache"/>
- 如果在其他mapper中,有關(guān)聯(lián)查詢的, 在相關(guān)的mapper里增加
<cache-ref/>標(biāo)簽
<!-- namespace為引用的mapper路徑,一個原則 有關(guān)聯(lián)查詢的相關(guān)表,在同一個namespace下,具體是哪個不重要 -->
<cache-ref namespace="com.kartist.demo.worker.dao.BusinessUnitInfoMapper"/>
例如:
<?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.kartist.demo.device.dao.DeviceNameDefinitionMapper">
<cache type="com.kartist.demo.common.config.MybatisRedisCache"/>
<cache-ref namespace="com.kartist.demo.device.dao.DeviceInfoMapper"/>
...
</mapper>
- 在接口上增加注解
@CacheNamespaceRef(DeviceInfoMapper.class)
如果接口上不增加這個注解,在調(diào)用
tk.mybatis或mybatis-plus提供的原生接口時有可能導(dǎo)致緩存中臟數(shù)據(jù)的產(chǎn)生
整合redis
簡單講就是自定義緩存方式,在用的地方指定自定義的緩存方式
- 需要實現(xiàn)
org.apache.ibatis.cache.Cache接口 - 在開啟二級緩存的地方,指定使用實現(xiàn)類
- MybatisRedisCache
import cn.hutool.core.collection.CollectionUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@Slf4j
public class MybatisRedisCache implements Cache {
final static String NAME_SPACE ="mybatis-cache:";
private static RedisTemplate<String, Object> redisTemplate;
private static int cacheSec;
private final String id;
/**
* The {@code ReadWriteLock}.
*/
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public MybatisRedisCache(final String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
log.warn("MybatisRedisCache:id=" + id);
this.id = id;
}
public static void setCacheSec(int cacheSec) {
MybatisRedisCache.cacheSec = cacheSec;
}
public static void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
MybatisRedisCache.redisTemplate = redisTemplate;
}
@Override
public ReadWriteLock getReadWriteLock() {
return this.readWriteLock;
}
@Override
public String getId() {
return this.id;
}
@Override
public void putObject(Object key, Object value) {
try {
log.debug(">>>>>>>>>>>>>>>>>>>>>>>>putObject: key=" + key + ",value=" + value);
if (null != value) {
if (cacheSec > 0) {
// 這里簡單起見, 先固定好了緩存的時長
// 也可以嘗試 結(jié)合<property name="cacheSec" value="600"/> 在不同的mapper中指定特殊的緩存時長
// 也可以根據(jù)實際業(yè)務(wù)情況,制定緩存策略
redisTemplate.opsForValue().set(NAME_SPACE + key.toString(), value, cacheSec, TimeUnit.SECONDS);
} else {
redisTemplate.opsForValue().set(NAME_SPACE + key.toString(), value);
}
}
} catch (Exception e) {
e.printStackTrace();
log.error("redis保存數(shù)據(jù)異常!");
}
}
@Override
public Object getObject(Object key) {
try {
log.debug(">>>>>>>>>>>>>>>>>>>>>>>>getObject: key=" + key);
int size = this.getSize();
if (null != key) {
// 這里很坑, 如果選用的redis序列化反序列化的方式不合適,在返回結(jié)果后可能會報類轉(zhuǎn)換異常
return redisTemplate.opsForValue().get(NAME_SPACE + key.toString());
}
} catch (Exception e) {
e.printStackTrace();
log.error("redis獲取數(shù)據(jù)異常!");
}
return null;
}
@Override
public Object removeObject(Object key) {
try {
if (null != key)
return redisTemplate.delete(NAME_SPACE + key.toString());
} catch (Exception e) {
e.printStackTrace();
log.error("redis獲取數(shù)據(jù)異常!");
}
return null;
}
@Override
public void clear() {
Set<String> keys = redisTemplate.keys(NAME_SPACE + "*");
if (CollectionUtil.isNotEmpty(keys)) {
redisTemplate.delete(keys);
}
log.debug(">>>>>>>>>>>>>>>>>>>>>>>>clear");
}
@Override
public int getSize() {
Set<String> keys = redisTemplate.keys(NAME_SPACE + "*");
if (CollectionUtil.isNotEmpty(keys)) {
return keys.size();
}
return 0;
}
}
由于需要用到redis,因此還需要寫一個配置,用于注入redisTemplate
@Configuration
public class MybatisRedisCacheConfiguration {
@Value("${mybatis.cache-sec:600}")
private int cacheSec;
@Autowired
public void config(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 配置連接工廠
template.setConnectionFactory(factory);
// 序列化 這里也可根據(jù)實際情況,使用其他的序列化實現(xiàn)類,
JdkSerializationRedisSerializer serializer = new JdkSerializationRedisSerializer();
template.setValueSerializer(serializer);
//使用StringRedisSerializer來序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
// 設(shè)置hash key 和value序列化模式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
MybatisRedisCache.setRedisTemplate(template);
MybatisRedisCache.setCacheSec(cacheSec);
}
}