mybatis默认开启了二级缓存功能,在mybatis主配置文件中,将cacheEnabled设置成false,则会关闭二级缓存功能
<settings>
<!--二级缓存默认开启,false关闭-->
<setting name="cacheEnabled" value="false" />
<!--mybatis日志打印到控制台-->
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
mybatis框架虽然默认开启了二级缓存功能,但是并没有默认实现,也就是下面这句代码返回null, 然后走一级缓存
下面是配置Redis作为mybatis的二级缓存,代码如下:
(1)mybatis主配置文件mybatis-config.xml中添加如下配置
<settings>
<!--二级缓存默认开启,false关闭-->
<setting name="cacheEnabled" value="true" />
<!--mybatis日志打印到控制台,以便于观察-->
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
(2)FemaleMapper.xml 配置如下
<mapper namespace="qinfeng.zheng.FemaleMapper">
<select id="getFemaleById" resultType="qinfeng.zheng.entity.Female" parameterType="int">
SELECT id,name FROM tb_female where id = #{id};
</select>
<cache eviction="LRU" type="qinfeng.zheng.RedisCache"/>
</mapper>
LRU是mybatis默认的过期策略
(3)qinfeng\zheng\RedisCache.java
import org.apache.ibatis.cache.Cache;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Map;
/**
* 使用redis的Hash结构
*/
public class RedisCache implements Cache {
private String id;
private JedisPool jedisPool;
private static int EXPIRE_TIME = 1000 * 1000;
public RedisCache(String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
this.id = id;
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPool = new JedisPool(jedisPoolConfig, "192.168.79.221", 6379, 3000, "123456");
}
@Override
public String getId() {
return id;
}
@Override
public void putObject(Object key, Object value) {
execute(jedis -> {
try {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ObjectOutputStream outputStream = new ObjectOutputStream(stream);
outputStream.writeObject(value);
byte[] idBytes = id.getBytes();
jedis.hset(idBytes, key.toString().getBytes(), stream.toByteArray());
// 设置一个过期时间。。
if (jedis.ttl(idBytes) == -1) {
jedis.expire(idBytes, EXPIRE_TIME);
}
} catch (Exception e) {
throw new RuntimeException("put object to redis error!", e);
}
return null;
});
}
@Override
public Object getObject(Object key) {
return execute(jedis -> {
try {
byte[] bytes = jedis.hget(id.getBytes(), key.toString().getBytes());
if (bytes == null) {
return null;
}
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
return objectInputStream.readObject();
} catch (Exception e) {
throw new RuntimeException("get data from redis error!", e);
}
});
}
@Override
public Object removeObject(Object key) {
return execute(jedis -> jedis.hdel(id.getBytes(), key.toString().getBytes()));
}
@Override
public void clear() {
execute(jedis -> jedis.del(id.getBytes())); //因为id是String类型,所以直接jedis.del(id)也行
}
@Override
public int getSize() {
return (Integer) execute(jedis -> {
Map<byte[], byte[]> map = jedis.hgetAll(id.getBytes());
return map.size();
});
}
private Object execute(RedisCallBack redisCallBack) {
try (Jedis jedis = jedisPool.getResource()) {
return redisCallBack.callBack(jedis);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
}
public interface RedisCallBack {
Object callBack(Jedis jedis);
}
使用redis作为mybatis的二级缓存框架,最主要就是实现mybatis提供的spi接口--Cache即可,然后实现putObject,getObject这两个主要的方法即可。
除此之外,记得pom.xml文件中添加jedis依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
(4)修改测试代码
public class V1Test {
public static void main(String[] args) {
try (InputStream is = Resources.getResourceAsStream("mybatis-config.xml")) {
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
FemaleMapper femaleMapper = sqlSession.getMapper(FemaleMapper.class);
Female female = femaleMapper.getFemaleById(1);
System.out.println(female);
System.out.println("-----------分隔符----------------------------------");
SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
FemaleMapper femaleMapper2 = sqlSession2.getMapper(FemaleMapper.class);
Female female2 = femaleMapper2.getFemaleById(1);
System.out.println(female2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
第一次运行:
Opening JDBC Connection
Created connection 331510866.
==> Preparing: SELECT id,name FROM tb_female where id = ?;
==> Parameters: 1(Integer)
<== Columns: id, name
<== Row: 1, soga
<== Total: 1
Female(id=1, name=soga)
-----------分隔符----------------------------------
Opening JDBC Connection
Created connection 1316061703.
==> Preparing: SELECT id,name FROM tb_female where id = ?;
==> Parameters: 1(Integer)
<== Columns: id, name
<== Row: 1, soga
<== Total: 1
Female(id=1, name=soga)
哈哈,貌似还是查库两次,redis缓存好像没有起到作用。
事实上,mybatis框架为二级缓存提供了一个缓冲Map, 需要在调用commit方法之后,才会将缓冲区Map中的数据写入到redis中。
我们修改一下测试代码,然后再运行观察结果
public class V1Test {
public static void main(String[] args) {
try (InputStream is = Resources.getResourceAsStream("mybatis-config.xml")) {
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
FemaleMapper femaleMapper = sqlSession.getMapper(FemaleMapper.class);
Female female = femaleMapper.getFemaleById(1);
System.out.println(female);
sqlSession.commit();
System.out.println("-----------分隔符----------------------------------");
SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
FemaleMapper femaleMapper2 = sqlSession2.getMapper(FemaleMapper.class);
Female female2 = femaleMapper2.getFemaleById(1);
System.out.println(female2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果:
哎哎哎,报了个错,记得Female 需要序列化
Opening JDBC Connection
Created connection 490150701.
==> Preparing: SELECT id,name FROM tb_female where id = ?;
==> Parameters: 1(Integer)
<== Columns: id, name
<== Row: 1, soga
<== Total: 1
Female(id=1, name=soga)
-----------分隔符----------------------------------
Cache Hit Ratio [qinfeng.zheng.FemaleMapper]: 0.5
Female(id=1, name=soga)
请看,这次只查了一个库,第2冲直接缓存命中、
我们再运行一次刚才的代码
Cache Hit Ratio [qinfeng.zheng.FemaleMapper]: 1.0
Female(id=1, name=soga)
-----------分隔符----------------------------------
Cache Hit Ratio [qinfeng.zheng.FemaleMapper]: 1.0
Female(id=1, name=soga)
两次都缓存命中!!
再看一下redis:
好了, mybatis整合redis为二级缓存到此完毕,下章分析一下它的源码实现!