作者在学习Redis整合时使用JDBC与Jedis,但是呢,现如今的环境下,Mybatis系列ORM框架是更受关注的方法,作者有一点点Mybatis基础,Mybatisplus几乎忘的差不多了,现对Redis整合Mybatis相关知识进行梳理,在进一步学习Mybatis系列知识后,再将具体代码进行补充测试。
结合 MyBatis 和 Redis 进行缓存可以通过 MyBatis 提供的缓存机制来实现。下面的例子将指导你如何使用 MyBatis-Redis-Cache 这个库进行整合。
添加依赖: 在你的 pom.xml
文件中添加 MyBatis 和 MyBatis-Redis-Cache 的依赖。
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.4
org.mybatis.caches
mybatis-redis
1.0.0
配置 Redis: 在 application.properties
或 application.yml
文件中配置 Redis 的相关信息。
# Redis 配置
spring.redis.host=你的Redis服务器地址
spring.redis.port=6379
配置 MyBatis: 在 MyBatis 的配置文件(例如 mybatis-config.xml
)中开启缓存。
假设你有一个 UserMapper
接口和相应的 UserMapper.xml
文件:
在 UserMapper.xml
文件中开启缓存:
在服务层使用 UserMapper
:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User getUserById(int id) {
// 首次调用将查询数据库
// 之后的调用将直接从 Redis 缓存中获取结果
return userMapper.selectUserById(id);
}
}
在 MyBatis 的 Mapper XML 文件中使用
标签和属性时,这是在告诉 MyBatis 使用默认的缓存实现。
初始化时的操作:
标签时,它会尝试实例化指定类型(type
属性)的缓存对象。com.example.cache.RedisCache
)会被实例化,并且在需要缓存操作时被 MyBatis 调用。执行查询时的操作:
selectUserById
)时,MyBatis 会首先检查缓存是否已经包含该查询的结果。因为在定义SQL语言之前,命名空间里还声明了
这种声明会让Mybatis做出对缓存的检查。关闭操作:
在使用 MyBatis 和 Redis 缓存时,如果你想要更多地控制 Redis 的详细操作,你可能需要自定义缓存处理逻辑。
其本质就是创建一个类,实现 MyBatis 的 Cache
接口。然后就把自定义类的名字换成之前的RedisCache就行,都是一样的配置。其自定义类代码如下所示:
import org.apache.ibatis.cache.Cache;
import redis.clients.jedis.Jedis;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class MyRedisCache implements Cache {
private final String id;
private final Jedis redisClient = new Jedis("localhost", 6379);
public MyRedisCache(String id) {
this.id = id;
}
@Override
public String getId() {
return this.id;
}
@Override
public void putObject(Object key, Object value) {
redisClient.set(key.toString(), value.toString());
}
@Override
public Object getObject(Object key) {
return redisClient.get(key.toString());
}
@Override
public Object removeObject(Object key) {
return redisClient.del(key.toString());
}
@Override
public void clear() {
redisClient.flushDB();
}
@Override
public int getSize() {
return Integer.parseInt(redisClient.dbSize().toString());
}
@Override
public ReadWriteLock getReadWriteLock() {
return new ReentrantReadWriteLock();
}
}
在 MyBatis 的 Mapper XML 文件中使用自定义缓存类:
类定义中,读者可能会发现,咋多了这么多成员变量和方法啊?这其实是实现org.apache.ibatis.cache.Cache接口覆盖的成员和方法。
其中,有四个必须覆盖的方法:
String getId()
:返回缓存的标识符。每个缓存都必须有一个唯一的标识符。
void putObject(Object key, Object value)
:将查询结果存储在缓存中。
Object getObject(Object key)
:从缓存中获取由key
指定的查询结果。
void clear()
:清除缓存中的所有项目。
三个可选覆盖的方法:
Object removeObject(Object key);
int getSize();
ReadWriteLock getReadWriteLock();
这些方法是可选的,意味着不一定非要覆盖它们。removeObject()
用于从缓存中移除对象,但并没有被 MyBatis 核心框架调用。同样,getSize()
和 getReadWriteLock()
也是可选的,并不是 MyBatis 核心部分的调用。这三个方法的实现依赖于你的实际缓存策略和需求。
为了更好地理解,作者查到16年某文章的cache源码。
package org.apache.ibatis.cache;
import java.util.concurrent.locks.ReadWriteLock;
/**
* 缓存接口
* 给缓存供应商的SPI(Service Provider Interface)
* 一个Cache的实例将为名称空间被创建
* Cache接口的实现类必须有一个具有String类型参数的构造方法,用于接收Cache对象的id,作为其唯一标识
*
* mybatis将以namespace作为id调用这个构造函数创建对象
*
* @author Administrator
*
*/
public interface Cache {
/**
* 获取缓存对象的唯一标识
* @return
*/
String getId();
/**
* 保存key/value到缓存对象中
* key可以是任何对象,但一般是CacheKey对象
* value是查询结果,为List类型
* @param key
* @param value
*/
void putObject(Object key, Object value);
/**
* 从缓存对象中获取key对应的value
* @param key
* @return
*/
Object getObject(Object key);
/**
* 可选的方法,没有被核心框架调用,移除key对应的value
* @param key
* @return
*/
Object removeObject(Object key);
/**
* 清空缓存
*/
void clear();
/**
* 获取缓存对象中存储的键/值对的数量
* 可选的方法,没有被框架核心调用
*/
int getSize();
/**
* 获取读写锁
* 可选的方法,从3.2.6起这个方法不再被框架核心调用
* 任何需要的锁,都必须由缓存供应商提供
*
* @return A ReadWriteLock
*/
ReadWriteLock getReadWriteLock();
}
可以在自定义缓存类中添加其他方法。
但是,这些额外的方法不会被 MyBatis 自动调用,它们只能在Override覆盖的方法内部调用。比如你想添加一个方法来计算 Redis 缓存的大小,你可以添加一个 size()
方法到你的自定义缓存类,并在 putObject
方法中调用它来更新缓存大小的信息。比如:
public class MyRedisCache implements Cache {
// ...其他方法
public int size() {
// 实现计算 Redis 缓存大小的逻辑
return 0;
}
@Override
public void putObject(Object key, Object value) {
// 在存储对象之前或之后更新缓存大小信息
// ...
int currentSize = size();
// ...
}
}
注意,在MyBatis中:
getId()
: MyBatis会自动调用这个方法来获取缓存的唯一标识符。putObject(Object key, Object value)
: MyBatis会在将查询结果存入缓存时调用这个方法。getObject(Object key)
: MyBatis会在从缓存中获取对象时调用这个方法。clear()
: 在执行更新、插入或删除操作时,MyBatis会调用这个方法来清空命名空间下的缓存。
removeObject(Object key)
: 这个方法在MyBatis核心框架中并不会被自动调用,但是你可以在自定义缓存实现中使用它。getSize()
: 同样,这个方法也不会被MyBatis核心框架自动调用,除非你自己调用它。getReadWriteLock()
: 从3.2.6版本开始,这个方法不再被框架核心调用。任何需要的锁,都必须由缓存提供商自行实现和管理。
总的来说,getId()
, putObject()
, getObject()
, 和 clear()
这四个方法是在MyBatis操作过程中会被自动调用的,其他方法(removeObject()
, getSize()
, getReadWriteLock()和其他非Cache接口的方法
)则不会,需要根据实际情况自行调用或实现。
考虑以下 Mapper XML 文件:
当执行 selectUserById
查询时:
MyRedisCache
类的 getObject
方法来尝试从 Redis 缓存中获取结果。getObject
返回 null
(缓存未命中),MyBatis 将执行 SQL 查询并将结果通过 putObject
方法存储到 Redis 缓存中。getObject
返回非 null
(缓存命中),MyBatis 将直接返回缓存的结果。既可以通过藏在自动调用的函数(不过这应该不属于手动调用),也可以通过获取 SqlSessionFactory
的 Configuration
,然后获取特定的 Cache
对象来调用这个方法。不过貌似这种创建实例对象的方案不是很流行,具体等作者多学一点知识再说。
下面是一个例子:
SqlSessionFactory sqlSessionFactory = ... // 获取 SqlSessionFactory
Configuration configuration = sqlSessionFactory.getConfiguration();
Cache cache = configuration.getCache("com.example.cache.MyRedisCache"); // 使用你的 cache id
int size = cache.getSize();
System.out.println("Cache Size: " + size);
在这个例子中:
- 首先,获取了
SqlSessionFactory
的实例。- 然后,从
SqlSessionFactory
中获取了Configuration
对象。- 接着,从
Configuration
中通过 cache id 获取了Cache
对象。- 最后,调用了
getSize()
方法来获取缓存的大小。