1、一级缓存,即SqlSession范围的缓存(默认开启,不需要配置)。
SqlSession工厂类代码如下:
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class FKSqlSessionFactory {
private static SqlSessionFactory sqlSessionFactory = null;
// 初始化创建SqlSessionFactory对象
static{
try {
// 读取mybatis-config.xml文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取SqlSession对象的静态方法
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
// 获取SqlSessionFactory的静态方法
public static SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
}
测试代码及解释如下:
import org.apache.ibatis.session.SqlSession;
import org.fkit.domain.User;
import org.fkit.factory.FKSqlSessionFactory;
import org.fkit.mapper.UserMapper;
public class TestOneLevelCache {
public static void main(String[] args) throws Exception {
TestOneLevelCache t = new TestOneLevelCache();
//t.testCache1();
//t.testCache2();
t.testCache3();
}
/*
* 一级缓存: 也就Session级的缓存(默认开启)
*/
public void testCache1 (){
// 使用工厂类获得SqlSession对象
SqlSession session = FKSqlSessionFactory.getSqlSession();
// 获得UserMapping对象
UserMapper um = session.getMapper(UserMapper.class);
// 查询id为1的User对象,会执行select语句
User user = um.selectUserById(1);
System.out.println(user);
// 再次查询id为1的User对象,因为是同一个SqlSession,所以会从之前的一级缓存中查找数据
User user2 = um.selectUserById(1);
System.out.println(user2);
// 关闭SqlSession对象
session.close();
}
public void testCache2 (){
// 使用工厂类获得SqlSession对象
SqlSession session = FKSqlSessionFactory.getSqlSession();
// 获得UserMapping对象
UserMapper um = session.getMapper(UserMapper.class);
// 查询id为1的User对象,会执行select语句
User user = um.selectUserById(1);
System.out.println(user);
// 执行delete操作
um.deleteUserById(2);
// commit提交
session.commit();
// 再次查询id为1的User对象,因为DML操作会清空SqlSession缓存,所以会再次执行select语句
User user2 = um.selectUserById(1);
System.out.println(user2);
// 关闭SqlSession对象
session.close();
}
public void testCache3 (){
// 使用工厂类获得SqlSession对象
SqlSession session = FKSqlSessionFactory.getSqlSession();
// 获得UserMapping对象
UserMapper um = session.getMapper(UserMapper.class);
// 查询id为1的User对象,会执行select语句
User user = um.selectUserById(1);
System.out.println(user);
// 关闭一级缓存
session.close();
// 再次访问,需要再次获取一级缓存,然后才能查找数据,否则会抛出异常
session = FKSqlSessionFactory.getSqlSession();
// 再次获得UserMapping对象
um = session.getMapper(UserMapper.class);
// 再次访问,因为现在使用的是一个新的SqlSession对象,所以会再次执行select语句
User user2 = um.selectUserById(1);
System.out.println(user2);
// 关闭SqlSession对象
session.close();
}
}
testCache1方法运行结果如下:
DEBUG [main] - ==> Preparing: SELECT * FROM TB_USER WHERE id = ?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
User [id=1, name=jack, sex=男, age=22]
User [id=1, name=jack, sex=男, age=22]
因为开启了一级缓存,所以相同的查询操作,不会发出sql语句去查询数据库,而从一级缓存中找。
testCache2方法运行结果如下:
DEBUG [main] - ==> Preparing: SELECT * FROM TB_USER WHERE id = ?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
User [id=1, name=jack, sex=男, age=22]
DEBUG [main] - ==> Preparing: DELETE FROM TB_USER WHERE id = ?
DEBUG [main] - ==> Parameters: 2(Integer)
DEBUG [main] - <== Updates: 0
DEBUG [main] - ==> Preparing: SELECT * FROM TB_USER WHERE id = ?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
User [id=1, name=jack, sex=男, age=22]
因为中间执行了增删改操作,为了保证缓存中的数据是最新的,所以相同的查询操作会再次发出sql语句去查询数据库。
testCache3方法运行结果同上,因为close方法会清除一级缓存,所以需要重新获取SqlSession,因为是新的SqlSession,所以会发出sql。
2、二级缓存,即mapper级别的缓存(需要配置)
在配置文件的根元素<configuration>直接下级的<settings></settings>元素中开启二级缓存,代码如下:
<!-- 指定 MyBatis 所用日志的具体实现 -->
<settings>
<setting name="logImpl" value="LOG4J"/>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
测试代码及解释如下:
public void testCache2 (){
// 使用工厂类获得SqlSession对象
SqlSession session = FKSqlSessionFactory.getSqlSession();
// 获得UserMapping对象
UserMapper um = session.getMapper(UserMapper.class);
// 查询id为1的User对象,会执行select语句
User user = um.selectUserById(1);
System.out.println(user);
// 关闭一级缓存
session.close();
// 再次访问,需要再次获取一级缓存,然后才能查找数据,否则会抛出异常
session = FKSqlSessionFactory.getSqlSession();
// 再次获得UserMapping对象
um = session.getMapper(UserMapper.class);
// 再次访问,因为现在使用的是一个新的SqlSession对象,所以会再次执行select语句
//但因为开启了二级缓存,所以会去二级缓存中找,所以不会发送sql语句
User user2 = um.selectUserById(1);
System.out.println(user2);
// 关闭SqlSession对象
session.close();
}
控制台输出结果如下:
DEBUG [main] - Cache Hit Ratio [org.fkit.mapper.UserMapper]: 0.0
DEBUG [main] - ==> Preparing: SELECT * FROM TB_USER WHERE id = ?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
User [id=1, name=jack, sex=男, age=22]
DEBUG [main] - Cache Hit Ratio [org.fkit.mapper.UserMapper]: 0.5
User [id=1, name=jack, sex=男, age=22]
在持久化类的mapper映射文件中的配置:
<!-- 开启二级缓存
回收策略为先进先出
自动刷新时间60s
最多缓存512个引用对象
只读
-->
<cache
eviction="LRU"
flushInterval="60000"
size="512"
readOnly="true"/>
另外,mapper文件中<select>元素中有个叫userCache的属性,值为true或false。默认为true,即允许查询语句使用二级缓存,false则相反。
总结:
一级缓存的范围为SqlSession,相同的SqlSession发出同一查询操作时,共享缓存数据;二级缓存的范围为mapper,是跨SqlSession的,不同的SqlSession操作mapper映射文件中的查询语句时(开启了二级缓存),可以共享缓存数据。一级缓存中找不到需要数据,则会去二级缓存中找,再找不到,则会发出sql语句去查数据库。